Skip to content

Commit

Permalink
perf: use bit flags for options
Browse files Browse the repository at this point in the history
  • Loading branch information
H4ad committed Apr 2, 2023
1 parent 665eee4 commit 6c7a68a
Show file tree
Hide file tree
Showing 16 changed files with 155 additions and 160 deletions.
20 changes: 6 additions & 14 deletions bin/semver.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,16 @@ let inc = null

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

let loose = false

let includePrerelease = false

let options = 0
let coerce = false

let rtl = false

let identifier

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

let reverse = false

let options = {}

const main = () => {
if (!argv.length) {
return help()
Expand All @@ -46,10 +40,10 @@ const main = () => {
reverse = true
break
case '-l': case '--loose':
loose = true
options |= FLAG_loose
break
case '-p': case '--include-prerelease':
includePrerelease = true
options |= FLAG_includePrerelease
break
case '-v': case '--version':
versions.push(argv.shift())
Expand All @@ -75,10 +69,10 @@ const main = () => {
coerce = true
break
case '--rtl':
rtl = true
options |= FLAG_rtl
break
case '--ltr':
rtl = false
options &= ~FLAG_rtl
break
case '-h': case '--help': case '-?':
return help()
Expand All @@ -88,8 +82,6 @@ const main = () => {
}
}

options = { loose: loose, includePrerelease: includePrerelease, rtl: rtl }

versions = versions.map((v) => {
return coerce ? (semver.coerce(v, options) || { version: v }).version : v
}).filter((v) => {
Expand Down
39 changes: 24 additions & 15 deletions classes/comparator.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,17 @@ class Comparator {
}

constructor (comp, options) {
options = parseOptions(options)
this.flagOptions = parseOptions(options)

if (comp instanceof Comparator) {
if (comp.loose === !!options.loose) {
if (hasFlag(comp.flagOptions, FLAG_loose)) {
return comp
} else {
comp = comp.value
}
}

debug('comparator', comp, options)
this.options = options
this.loose = !!options.loose
debug('comparator', comp, this.flagOptions)
this.parse(comp)

if (this.semver === ANY) {
Expand All @@ -30,8 +28,21 @@ class Comparator {
debug('comp', this)
}

get options() {
return {
includePrerelease: hasFlag(this.flagOptions, FLAG_includePrerelease),
loose: hasFlag(this.flagOptions, FLAG_loose),
rtl: hasFlag(this.flagOptions, FLAG_rtl),
}
}

get loose() {
return hasFlag(this.flagOptions, FLAG_loose)
}

parse (comp) {
const r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR]
const loose = hasFlag(this.flagOptions, FLAG_loose)
const r = loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR]
const m = comp.match(r)

if (!m) {
Expand All @@ -47,7 +58,7 @@ class Comparator {
if (!m[2]) {
this.semver = ANY
} else {
this.semver = new SemVer(m[2], this.options.loose)
this.semver = new SemVer(m[2], loose)
}
}

Expand All @@ -56,21 +67,21 @@ class Comparator {
}

test (version) {
debug('Comparator.test', version, this.options.loose)
debug('Comparator.test', version, hasFlag(this.flagOptions, FLAG_loose))

if (this.semver === ANY || version === ANY) {
return true
}

if (typeof version === 'string') {
try {
version = new SemVer(version, this.options)
version = new SemVer(version, this.flagOptions)
} catch (er) {
return false
}
}

return cmp(version, this.operator, this.semver, this.options)
return cmp(version, this.operator, this.semver, this.flagOptions)
}

intersects (comp, options) {
Expand All @@ -79,10 +90,7 @@ class Comparator {
}

if (!options || typeof options !== 'object') {
options = {
loose: !!options,
includePrerelease: false,
}
options = FLAG_loose
}

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

const parseOptions = require('../internal/parse-options')
const { re, t } = require('../internal/re')
const { FLAG_loose, hasFlag } = require('../internal/constants')
const cmp = require('../functions/cmp')
const debug = require('../internal/debug')
const SemVer = require('./semver')
const Range = require('./range')
const Range = require('./range')
93 changes: 40 additions & 53 deletions classes/range.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
// hoisted class for cyclic dependency
class Range {
constructor (range, options) {
options = parseOptions(options)
this.flagOptions = parseOptions(options)

if (range instanceof Range) {
if (
range.loose === !!options.loose &&
range.includePrerelease === !!options.includePrerelease
if (hasFlag(range.flagOptions, FLAG_loose) === hasFlag(this.flagOptions, FLAG_loose)
&& hasFlag(range.flagOptions, FLAG_includePrerelease) === hasFlag(this.flagOptions, FLAG_includePrerelease)
) {
// if ((range.flagOptions & ~FLAG_rtl) === (this.flagOptions & ~FLAG_rtl)) {
return range
} else {
return new Range(range.raw, options)
return new Range(range.raw, this.flagOptions)
}
}

Expand All @@ -22,10 +22,6 @@ class Range {
return this
}

this.options = options
this.loose = !!options.loose
this.includePrerelease = !!options.includePrerelease

// First, split based on boolean or ||
this.raw = range
this.set = range
Expand Down Expand Up @@ -62,6 +58,24 @@ class Range {
this.format()
}

get options() {
return {
includePrerelease: hasFlag(this.flagOptions, FLAG_includePrerelease),
loose: hasFlag(this.flagOptions, FLAG_loose),
rtl: hasFlag(this.flagOptions, FLAG_rtl),
}
}

get loose() {
return hasFlag(this.flagOptions, FLAG_loose)
}

// this isn't actually relevant for versions, but keep it so that we
// don't run into trouble passing this.options around.
get includePrerelease() {
return hasFlag(this.flagOptions, FLAG_includePrerelease)
}

format () {
this.range = this.set
.map((comps) => {
Expand All @@ -81,17 +95,17 @@ class Range {

// memoize range parsing for performance.
// this is a very hot path, and fully deterministic.
const memoOpts = buildMemoKeyFromOptions(this.options)
const memoOpts = this.flagOptions.toString()
const memoKey = memoOpts + range
const cached = cache.get(memoKey)
if (cached) {
return cached
}

const loose = this.options.loose
const loose = hasFlag(this.flagOptions, FLAG_loose)
// `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
const hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE]
range = range.replace(hr, hyphenReplace(this.options.includePrerelease))
range = range.replace(hr, hyphenReplace(hasFlag(this.flagOptions, FLAG_includePrerelease)))
debug('hyphen replace', range)
// `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace)
Expand All @@ -111,16 +125,16 @@ class Range {

let rangeList = range
.split(' ')
.map(comp => parseComparator(comp, this.options))
.map(comp => parseComparator(comp, this.flagOptions))
.join(' ')
.split(/\s+/)
// >=0.0.0 is equivalent to *
.map(comp => replaceGTE0(comp, this.options))
.map(comp => replaceGTE0(comp, this.flagOptions))

if (loose) {
// in loose mode, throw out any that are not valid comparators
rangeList = rangeList.filter(comp => {
debug('loose invalid filter', comp, this.options)
debug('loose invalid filter', comp, this.flagOptions)
return !!comp.match(re[t.COMPARATORLOOSE])
})
}
Expand All @@ -130,7 +144,7 @@ class Range {
// if more than one comparator, remove any * comparators
// also, don't include the same comparator more than once
const rangeMap = new Map()
const comparators = rangeList.map(comp => new Comparator(comp, this.options))
const comparators = rangeList.map(comp => new Comparator(comp, this.flagOptions))
for (const comp of comparators) {
if (isNullSet(comp)) {
return [comp]
Expand Down Expand Up @@ -176,49 +190,21 @@ class Range {

if (typeof version === 'string') {
try {
version = new SemVer(version, this.options)
version = new SemVer(version, this.flagOptions)
} catch (er) {
return false
}
}

for (let i = 0; i < this.set.length; i++) {
if (testSet(this.set[i], version, this.options)) {
if (testSet(this.set[i], version, this.flagOptions)) {
return true
}
}
return false
}
}

function buildMemoKeyFromOptions(options) {
if (options.includePrerelease === true) {
if (options.loose === true && options.rtl === true) {
return '1';
}

if (options.loose === true) {
return '2';
}

if (options.rtl === true) {
return '3';
}

return '4';
} else if (options.loose === true) {
if (options.rtl === true) {
return '5';
}

return '6';
} else if (options.rtl === true) {
return '7';
} else {
return '8';
}
}

module.exports = Range

const LRU = require('lru-cache')
Expand All @@ -235,6 +221,7 @@ const {
tildeTrimReplace,
caretTrimReplace,
} = require('../internal/re')
const { FLAG_loose, FLAG_includePrerelease, FLAG_rtl, hasFlag } = require('../internal/constants')

const isNullSet = c => c.value === '<0.0.0-0'
const isAny = c => c.value === ''
Expand Down Expand Up @@ -288,7 +275,7 @@ const replaceTildes = (comp, options) =>
}).join(' ')

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

const replaceCaret = (comp, options) => {
debug('caret', comp, options)
const r = options.loose ? re[t.CARETLOOSE] : re[t.CARET]
const z = options.includePrerelease ? '-0' : ''
const r = hasFlag(options, FLAG_loose) ? re[t.CARETLOOSE] : re[t.CARET]
const z = hasFlag(options, FLAG_includePrerelease) ? '-0' : ''
return comp.replace(r, (_, M, m, p, pr) => {
debug('caret', comp, _, M, m, p, pr)
let ret
Expand Down Expand Up @@ -390,7 +377,7 @@ const replaceXRanges = (comp, options) => {

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

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

if (xM) {
if (gtlt === '>' || gtlt === '<') {
Expand Down Expand Up @@ -474,7 +461,7 @@ const replaceStars = (comp, options) => {
const replaceGTE0 = (comp, options) => {
debug('replaceGTE0', comp, options)
return comp.trim()
.replace(re[options.includePrerelease ? t.GTE0PRE : t.GTE0], '')
.replace(re[hasFlag(options, FLAG_loose) ? t.GTE0PRE : t.GTE0], '')
}

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

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

0 comments on commit 6c7a68a

Please sign in to comment.