Skip to content

Commit 505335e

Browse files
authored
Merge pull request #175 from florianl/drop-init
Reduces start time of executables by lazy initialising regexes on first use and dropping package level init()
2 parents 0824a89 + 6dd734b commit 505335e

File tree

2 files changed

+59
-38
lines changed

2 files changed

+59
-38
lines changed

constraint.go

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,26 @@ import (
88
"regexp"
99
"sort"
1010
"strings"
11+
"sync"
1112
)
1213

14+
var (
15+
constraintRegexp *regexp.Regexp
16+
constraintRegexpOnce sync.Once
17+
)
18+
19+
func getConstraintRegexp() *regexp.Regexp {
20+
constraintRegexpOnce.Do(func() {
21+
// This heavy lifting only happens the first time this function is called
22+
constraintRegexp = regexp.MustCompile(fmt.Sprintf(
23+
`^\s*(%s)\s*(%s)\s*$`,
24+
`<=|>=|!=|~>|<|>|=|`,
25+
VersionRegexpRaw,
26+
))
27+
})
28+
return constraintRegexp
29+
}
30+
1331
// Constraint represents a single constraint for a version, such as
1432
// ">= 1.0".
1533
type Constraint struct {
@@ -29,38 +47,11 @@ type Constraints []*Constraint
2947

3048
type constraintFunc func(v, c *Version) bool
3149

32-
var constraintOperators map[string]constraintOperation
33-
3450
type constraintOperation struct {
3551
op operator
3652
f constraintFunc
3753
}
3854

39-
var constraintRegexp *regexp.Regexp
40-
41-
func init() {
42-
constraintOperators = map[string]constraintOperation{
43-
"": {op: equal, f: constraintEqual},
44-
"=": {op: equal, f: constraintEqual},
45-
"!=": {op: notEqual, f: constraintNotEqual},
46-
">": {op: greaterThan, f: constraintGreaterThan},
47-
"<": {op: lessThan, f: constraintLessThan},
48-
">=": {op: greaterThanEqual, f: constraintGreaterThanEqual},
49-
"<=": {op: lessThanEqual, f: constraintLessThanEqual},
50-
"~>": {op: pessimistic, f: constraintPessimistic},
51-
}
52-
53-
ops := make([]string, 0, len(constraintOperators))
54-
for k := range constraintOperators {
55-
ops = append(ops, regexp.QuoteMeta(k))
56-
}
57-
58-
constraintRegexp = regexp.MustCompile(fmt.Sprintf(
59-
`^\s*(%s)\s*(%s)\s*$`,
60-
strings.Join(ops, "|"),
61-
VersionRegexpRaw))
62-
}
63-
6455
// NewConstraint will parse one or more constraints from the given
6556
// constraint string. The string must be a comma-separated list of
6657
// constraints.
@@ -176,7 +167,7 @@ func (c *Constraint) String() string {
176167
}
177168

178169
func parseSingle(v string) (*Constraint, error) {
179-
matches := constraintRegexp.FindStringSubmatch(v)
170+
matches := getConstraintRegexp().FindStringSubmatch(v)
180171
if matches == nil {
181172
return nil, fmt.Errorf("malformed constraint: %s", v)
182173
}
@@ -186,7 +177,25 @@ func parseSingle(v string) (*Constraint, error) {
186177
return nil, err
187178
}
188179

189-
cop := constraintOperators[matches[1]]
180+
var cop constraintOperation
181+
switch matches[1] {
182+
case "=":
183+
cop = constraintOperation{op: equal, f: constraintEqual}
184+
case "!=":
185+
cop = constraintOperation{op: notEqual, f: constraintNotEqual}
186+
case ">":
187+
cop = constraintOperation{op: greaterThan, f: constraintGreaterThan}
188+
case "<":
189+
cop = constraintOperation{op: lessThan, f: constraintLessThan}
190+
case ">=":
191+
cop = constraintOperation{op: greaterThanEqual, f: constraintGreaterThanEqual}
192+
case "<=":
193+
cop = constraintOperation{op: lessThanEqual, f: constraintLessThanEqual}
194+
case "~>":
195+
cop = constraintOperation{op: pessimistic, f: constraintPessimistic}
196+
default:
197+
cop = constraintOperation{op: equal, f: constraintEqual}
198+
}
190199

191200
return &Constraint{
192201
f: cop.f,

version.go

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,31 @@ import (
99
"regexp"
1010
"strconv"
1111
"strings"
12+
"sync"
1213
)
1314

1415
// The compiled regular expression used to test the validity of a version.
1516
var (
16-
versionRegexp *regexp.Regexp
17-
semverRegexp *regexp.Regexp
17+
versionRegexp *regexp.Regexp
18+
versionRegexpOnce sync.Once
19+
semverRegexp *regexp.Regexp
20+
semverRegexpOnce sync.Once
1821
)
1922

23+
func getVersionRegexp() *regexp.Regexp {
24+
versionRegexpOnce.Do(func() {
25+
versionRegexp = regexp.MustCompile("^" + VersionRegexpRaw + "$")
26+
})
27+
return versionRegexp
28+
}
29+
30+
func getSemverRegexp() *regexp.Regexp {
31+
semverRegexpOnce.Do(func() {
32+
semverRegexp = regexp.MustCompile("^" + SemverRegexpRaw + "$")
33+
})
34+
return semverRegexp
35+
}
36+
2037
// The raw regular expression string used for testing the validity
2138
// of a version.
2239
const (
@@ -41,22 +58,17 @@ type Version struct {
4158
original string
4259
}
4360

44-
func init() {
45-
versionRegexp = regexp.MustCompile("^" + VersionRegexpRaw + "$")
46-
semverRegexp = regexp.MustCompile("^" + SemverRegexpRaw + "$")
47-
}
48-
4961
// NewVersion parses the given version and returns a new
5062
// Version.
5163
func NewVersion(v string) (*Version, error) {
52-
return newVersion(v, versionRegexp)
64+
return newVersion(v, getVersionRegexp())
5365
}
5466

5567
// NewSemver parses the given version and returns a new
5668
// Version that adheres strictly to SemVer specs
5769
// https://semver.org/
5870
func NewSemver(v string) (*Version, error) {
59-
return newVersion(v, semverRegexp)
71+
return newVersion(v, getSemverRegexp())
6072
}
6173

6274
func newVersion(v string, pattern *regexp.Regexp) (*Version, error) {

0 commit comments

Comments
 (0)