Skip to content

Commit 6c7a68a

Browse files
committed
perf: use bit flags for options
1 parent 665eee4 commit 6c7a68a

16 files changed

+155
-160
lines changed

bin/semver.js

+6-14
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,16 @@ let inc = null
1313

1414
const version = require('../package.json').version
1515

16-
let loose = false
17-
18-
let includePrerelease = false
19-
16+
let options = 0
2017
let coerce = false
2118

22-
let rtl = false
23-
2419
let identifier
2520

2621
const semver = require('../')
22+
const { FLAG_includePrerelease, FLAG_loose, FLAG_rtl } = require('../internal/constants')
2723

2824
let reverse = false
2925

30-
let options = {}
31-
3226
const main = () => {
3327
if (!argv.length) {
3428
return help()
@@ -46,10 +40,10 @@ const main = () => {
4640
reverse = true
4741
break
4842
case '-l': case '--loose':
49-
loose = true
43+
options |= FLAG_loose
5044
break
5145
case '-p': case '--include-prerelease':
52-
includePrerelease = true
46+
options |= FLAG_includePrerelease
5347
break
5448
case '-v': case '--version':
5549
versions.push(argv.shift())
@@ -75,10 +69,10 @@ const main = () => {
7569
coerce = true
7670
break
7771
case '--rtl':
78-
rtl = true
72+
options |= FLAG_rtl
7973
break
8074
case '--ltr':
81-
rtl = false
75+
options &= ~FLAG_rtl
8276
break
8377
case '-h': case '--help': case '-?':
8478
return help()
@@ -88,8 +82,6 @@ const main = () => {
8882
}
8983
}
9084

91-
options = { loose: loose, includePrerelease: includePrerelease, rtl: rtl }
92-
9385
versions = versions.map((v) => {
9486
return coerce ? (semver.coerce(v, options) || { version: v }).version : v
9587
}).filter((v) => {

classes/comparator.js

+24-15
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,17 @@ class Comparator {
66
}
77

88
constructor (comp, options) {
9-
options = parseOptions(options)
9+
this.flagOptions = parseOptions(options)
1010

1111
if (comp instanceof Comparator) {
12-
if (comp.loose === !!options.loose) {
12+
if (hasFlag(comp.flagOptions, FLAG_loose)) {
1313
return comp
1414
} else {
1515
comp = comp.value
1616
}
1717
}
1818

19-
debug('comparator', comp, options)
20-
this.options = options
21-
this.loose = !!options.loose
19+
debug('comparator', comp, this.flagOptions)
2220
this.parse(comp)
2321

2422
if (this.semver === ANY) {
@@ -30,8 +28,21 @@ class Comparator {
3028
debug('comp', this)
3129
}
3230

31+
get options() {
32+
return {
33+
includePrerelease: hasFlag(this.flagOptions, FLAG_includePrerelease),
34+
loose: hasFlag(this.flagOptions, FLAG_loose),
35+
rtl: hasFlag(this.flagOptions, FLAG_rtl),
36+
}
37+
}
38+
39+
get loose() {
40+
return hasFlag(this.flagOptions, FLAG_loose)
41+
}
42+
3343
parse (comp) {
34-
const r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR]
44+
const loose = hasFlag(this.flagOptions, FLAG_loose)
45+
const r = loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR]
3546
const m = comp.match(r)
3647

3748
if (!m) {
@@ -47,7 +58,7 @@ class Comparator {
4758
if (!m[2]) {
4859
this.semver = ANY
4960
} else {
50-
this.semver = new SemVer(m[2], this.options.loose)
61+
this.semver = new SemVer(m[2], loose)
5162
}
5263
}
5364

@@ -56,21 +67,21 @@ class Comparator {
5667
}
5768

5869
test (version) {
59-
debug('Comparator.test', version, this.options.loose)
70+
debug('Comparator.test', version, hasFlag(this.flagOptions, FLAG_loose))
6071

6172
if (this.semver === ANY || version === ANY) {
6273
return true
6374
}
6475

6576
if (typeof version === 'string') {
6677
try {
67-
version = new SemVer(version, this.options)
78+
version = new SemVer(version, this.flagOptions)
6879
} catch (er) {
6980
return false
7081
}
7182
}
7283

73-
return cmp(version, this.operator, this.semver, this.options)
84+
return cmp(version, this.operator, this.semver, this.flagOptions)
7485
}
7586

7687
intersects (comp, options) {
@@ -79,10 +90,7 @@ class Comparator {
7990
}
8091

8192
if (!options || typeof options !== 'object') {
82-
options = {
83-
loose: !!options,
84-
includePrerelease: false,
85-
}
93+
options = FLAG_loose
8694
}
8795

8896
if (this.operator === '') {
@@ -130,7 +138,8 @@ module.exports = Comparator
130138

131139
const parseOptions = require('../internal/parse-options')
132140
const { re, t } = require('../internal/re')
141+
const { FLAG_loose, hasFlag } = require('../internal/constants')
133142
const cmp = require('../functions/cmp')
134143
const debug = require('../internal/debug')
135144
const SemVer = require('./semver')
136-
const Range = require('./range')
145+
const Range = require('./range')

classes/range.js

+40-53
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
// hoisted class for cyclic dependency
22
class Range {
33
constructor (range, options) {
4-
options = parseOptions(options)
4+
this.flagOptions = parseOptions(options)
55

66
if (range instanceof Range) {
7-
if (
8-
range.loose === !!options.loose &&
9-
range.includePrerelease === !!options.includePrerelease
7+
if (hasFlag(range.flagOptions, FLAG_loose) === hasFlag(this.flagOptions, FLAG_loose)
8+
&& hasFlag(range.flagOptions, FLAG_includePrerelease) === hasFlag(this.flagOptions, FLAG_includePrerelease)
109
) {
10+
// if ((range.flagOptions & ~FLAG_rtl) === (this.flagOptions & ~FLAG_rtl)) {
1111
return range
1212
} else {
13-
return new Range(range.raw, options)
13+
return new Range(range.raw, this.flagOptions)
1414
}
1515
}
1616

@@ -22,10 +22,6 @@ class Range {
2222
return this
2323
}
2424

25-
this.options = options
26-
this.loose = !!options.loose
27-
this.includePrerelease = !!options.includePrerelease
28-
2925
// First, split based on boolean or ||
3026
this.raw = range
3127
this.set = range
@@ -62,6 +58,24 @@ class Range {
6258
this.format()
6359
}
6460

61+
get options() {
62+
return {
63+
includePrerelease: hasFlag(this.flagOptions, FLAG_includePrerelease),
64+
loose: hasFlag(this.flagOptions, FLAG_loose),
65+
rtl: hasFlag(this.flagOptions, FLAG_rtl),
66+
}
67+
}
68+
69+
get loose() {
70+
return hasFlag(this.flagOptions, FLAG_loose)
71+
}
72+
73+
// this isn't actually relevant for versions, but keep it so that we
74+
// don't run into trouble passing this.options around.
75+
get includePrerelease() {
76+
return hasFlag(this.flagOptions, FLAG_includePrerelease)
77+
}
78+
6579
format () {
6680
this.range = this.set
6781
.map((comps) => {
@@ -81,17 +95,17 @@ class Range {
8195

8296
// memoize range parsing for performance.
8397
// this is a very hot path, and fully deterministic.
84-
const memoOpts = buildMemoKeyFromOptions(this.options)
98+
const memoOpts = this.flagOptions.toString()
8599
const memoKey = memoOpts + range
86100
const cached = cache.get(memoKey)
87101
if (cached) {
88102
return cached
89103
}
90104

91-
const loose = this.options.loose
105+
const loose = hasFlag(this.flagOptions, FLAG_loose)
92106
// `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
93107
const hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE]
94-
range = range.replace(hr, hyphenReplace(this.options.includePrerelease))
108+
range = range.replace(hr, hyphenReplace(hasFlag(this.flagOptions, FLAG_includePrerelease)))
95109
debug('hyphen replace', range)
96110
// `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
97111
range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace)
@@ -111,16 +125,16 @@ class Range {
111125

112126
let rangeList = range
113127
.split(' ')
114-
.map(comp => parseComparator(comp, this.options))
128+
.map(comp => parseComparator(comp, this.flagOptions))
115129
.join(' ')
116130
.split(/\s+/)
117131
// >=0.0.0 is equivalent to *
118-
.map(comp => replaceGTE0(comp, this.options))
132+
.map(comp => replaceGTE0(comp, this.flagOptions))
119133

120134
if (loose) {
121135
// in loose mode, throw out any that are not valid comparators
122136
rangeList = rangeList.filter(comp => {
123-
debug('loose invalid filter', comp, this.options)
137+
debug('loose invalid filter', comp, this.flagOptions)
124138
return !!comp.match(re[t.COMPARATORLOOSE])
125139
})
126140
}
@@ -130,7 +144,7 @@ class Range {
130144
// if more than one comparator, remove any * comparators
131145
// also, don't include the same comparator more than once
132146
const rangeMap = new Map()
133-
const comparators = rangeList.map(comp => new Comparator(comp, this.options))
147+
const comparators = rangeList.map(comp => new Comparator(comp, this.flagOptions))
134148
for (const comp of comparators) {
135149
if (isNullSet(comp)) {
136150
return [comp]
@@ -176,49 +190,21 @@ class Range {
176190

177191
if (typeof version === 'string') {
178192
try {
179-
version = new SemVer(version, this.options)
193+
version = new SemVer(version, this.flagOptions)
180194
} catch (er) {
181195
return false
182196
}
183197
}
184198

185199
for (let i = 0; i < this.set.length; i++) {
186-
if (testSet(this.set[i], version, this.options)) {
200+
if (testSet(this.set[i], version, this.flagOptions)) {
187201
return true
188202
}
189203
}
190204
return false
191205
}
192206
}
193207

194-
function buildMemoKeyFromOptions(options) {
195-
if (options.includePrerelease === true) {
196-
if (options.loose === true && options.rtl === true) {
197-
return '1';
198-
}
199-
200-
if (options.loose === true) {
201-
return '2';
202-
}
203-
204-
if (options.rtl === true) {
205-
return '3';
206-
}
207-
208-
return '4';
209-
} else if (options.loose === true) {
210-
if (options.rtl === true) {
211-
return '5';
212-
}
213-
214-
return '6';
215-
} else if (options.rtl === true) {
216-
return '7';
217-
} else {
218-
return '8';
219-
}
220-
}
221-
222208
module.exports = Range
223209

224210
const LRU = require('lru-cache')
@@ -235,6 +221,7 @@ const {
235221
tildeTrimReplace,
236222
caretTrimReplace,
237223
} = require('../internal/re')
224+
const { FLAG_loose, FLAG_includePrerelease, FLAG_rtl, hasFlag } = require('../internal/constants')
238225

239226
const isNullSet = c => c.value === '<0.0.0-0'
240227
const isAny = c => c.value === ''
@@ -288,7 +275,7 @@ const replaceTildes = (comp, options) =>
288275
}).join(' ')
289276

290277
const replaceTilde = (comp, options) => {
291-
const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE]
278+
const r = hasFlag(options, FLAG_loose) ? re[t.TILDELOOSE] : re[t.TILDE]
292279
return comp.replace(r, (_, M, m, p, pr) => {
293280
debug('tilde', comp, _, M, m, p, pr)
294281
let ret
@@ -330,8 +317,8 @@ const replaceCarets = (comp, options) =>
330317

331318
const replaceCaret = (comp, options) => {
332319
debug('caret', comp, options)
333-
const r = options.loose ? re[t.CARETLOOSE] : re[t.CARET]
334-
const z = options.includePrerelease ? '-0' : ''
320+
const r = hasFlag(options, FLAG_loose) ? re[t.CARETLOOSE] : re[t.CARET]
321+
const z = hasFlag(options, FLAG_includePrerelease) ? '-0' : ''
335322
return comp.replace(r, (_, M, m, p, pr) => {
336323
debug('caret', comp, _, M, m, p, pr)
337324
let ret
@@ -390,7 +377,7 @@ const replaceXRanges = (comp, options) => {
390377

391378
const replaceXRange = (comp, options) => {
392379
comp = comp.trim()
393-
const r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE]
380+
const r = hasFlag(options, FLAG_loose) ? re[t.XRANGELOOSE] : re[t.XRANGE]
394381
return comp.replace(r, (ret, gtlt, M, m, p, pr) => {
395382
debug('xRange', comp, ret, gtlt, M, m, p, pr)
396383
const xM = isX(M)
@@ -404,7 +391,7 @@ const replaceXRange = (comp, options) => {
404391

405392
// if we're including prereleases in the match, then we need
406393
// to fix this to -0, the lowest possible prerelease value
407-
pr = options.includePrerelease ? '-0' : ''
394+
pr = hasFlag(options, FLAG_includePrerelease) ? '-0' : ''
408395

409396
if (xM) {
410397
if (gtlt === '>' || gtlt === '<') {
@@ -474,7 +461,7 @@ const replaceStars = (comp, options) => {
474461
const replaceGTE0 = (comp, options) => {
475462
debug('replaceGTE0', comp, options)
476463
return comp.trim()
477-
.replace(re[options.includePrerelease ? t.GTE0PRE : t.GTE0], '')
464+
.replace(re[hasFlag(options, FLAG_loose) ? t.GTE0PRE : t.GTE0], '')
478465
}
479466

480467
// This function is passed to string.replace(re[t.HYPHENRANGE])
@@ -521,7 +508,7 @@ const testSet = (set, version, options) => {
521508
}
522509
}
523510

524-
if (version.prerelease.length && !options.includePrerelease) {
511+
if (version.prerelease.length && !hasFlag(options, FLAG_includePrerelease)) {
525512
// Find the set of versions that are allowed to have prereleases
526513
// For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0
527514
// That should allow `1.2.3-pr.2` to pass.

0 commit comments

Comments
 (0)