Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize find method #226

Merged
merged 1 commit into from Jan 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 13 additions & 5 deletions custom_node.js
Expand Up @@ -135,16 +135,24 @@ Node.prototype.findByLabel = function (path) {
return this.children[path[0]]
}

Node.prototype.findMatchingChild = function (derivedConstraints, path) {
var child = this.children[path[0]]
Node.prototype.findMatchingChild = function (derivedConstraints, path, pathIndex) {
var child = this.children[path[pathIndex]]
if (child !== undefined && (child.numberOfChildren > 0 || child.getMatchingHandler(derivedConstraints) !== null)) {
if (path.slice(0, child.prefix.length) === child.prefix) {
let isPathStartsWithPrefix = true
for (let i = 0; i < child.prefix.length; i++) {
if (path.charCodeAt(pathIndex + i) !== child.prefix.charCodeAt(i)) {
isPathStartsWithPrefix = false
break
}
}

if (isPathStartsWithPrefix) {
return child
}
}

child = this.children[':']
if (child !== undefined && path[0] !== ':' && (child.numberOfChildren > 0 || child.getMatchingHandler(derivedConstraints) !== null)) {
child = path[pathIndex] !== ':' ? this.children[':'] : undefined
if (child !== undefined && (child.numberOfChildren > 0 || child.getMatchingHandler(derivedConstraints) !== null)) {
return child
}

Expand Down
189 changes: 72 additions & 117 deletions index.js
Expand Up @@ -398,7 +398,7 @@ Router.prototype.lookup = function lookup (req, res, ctx) {
}

Router.prototype.find = function find (method, path, derivedConstraints) {
var currentNode = this.trees[method]
let currentNode = this.trees[method]
if (currentNode === undefined) return null

if (path.charCodeAt(0) !== 47) { // 47 is '/'
Expand All @@ -413,37 +413,35 @@ Router.prototype.find = function find (method, path, derivedConstraints) {
return this._onBadUrl(path)
}

var originalPath = path
var originalPathLength = path.length

if (this.caseSensitive === false) {
path = path.toLowerCase()
}

var maxParamLength = this.maxParamLength
var wildcardNode = null
var pathLenWildcard = 0
var decoded = null
const maxParamLength = this.maxParamLength

let pathIndex = currentNode.prefix.length
const params = []
var i = 0
var idxInOriginalPath = 0
const pathLen = path.length

let wildcardNode = null
let wildcardNodePathIndex = 0

let lastParametricBrother = null
const parametricBrothersStack = []

while (true) {
var pathLen = path.length
var prefix = currentNode.prefix

// found the route
if (pathLen === 0 || path === prefix) {
var handle = derivedConstraints !== undefined ? currentNode.getMatchingHandler(derivedConstraints) : currentNode.unconstrainedHandler
if (pathIndex === pathLen) {
const handle = derivedConstraints !== undefined
? currentNode.getMatchingHandler(derivedConstraints)
: currentNode.unconstrainedHandler

if (handle !== null && handle !== undefined) {
var paramsObj = {}
const paramsObj = {}
if (handle.paramsLength > 0) {
var paramNames = handle.params
const paramNames = handle.params

for (i = 0; i < handle.paramsLength; i++) {
for (let i = 0; i < handle.paramsLength; i++) {
paramsObj[paramNames[i]] = params[i]
}
}
Expand All @@ -456,150 +454,107 @@ Router.prototype.find = function find (method, path, derivedConstraints) {
}
}

const parameterBrother = currentNode.parametricBrother
if (
path !== '' &&
parameterBrother !== null &&
parameterBrother !== lastParametricBrother
) {
parametricBrothersStack.push({
pathPointer: originalPathLength - pathLen,
paramsCount: params.length
})
lastParametricBrother = parameterBrother
}

var prefixLen = prefix.length
var len = 0

// search for the longest common prefix
i = pathLen < prefixLen ? pathLen : prefixLen
while (len < i && path.charCodeAt(len) === prefix.charCodeAt(len)) len++

if (len === prefixLen) {
path = path.slice(len)
pathLen = path.length
idxInOriginalPath += len
}

var node = currentNode.findMatchingChild(derivedConstraints, path)
let node = currentNode.findMatchingChild(derivedConstraints, path, pathIndex)

if (node === null) {
node = currentNode.parametricBrother
if (node === null) {
return this._getWildcardNode(wildcardNode, originalPath, pathLenWildcard, derivedConstraints, params)
return this._getWildcardNode(wildcardNode, path, wildcardNodePathIndex, derivedConstraints, params)
}

const { pathPointer, paramsCount } = parametricBrothersStack.pop()
const parametricBrotherPath = originalPath.slice(pathPointer)
const { brotherPathIndex, paramsCount } = parametricBrothersStack.pop()
pathIndex = brotherPathIndex
params.splice(paramsCount)
} else if (
pathIndex < pathLen &&
node.parametricBrother !== null &&
node.parametricBrother !== lastParametricBrother
) {
parametricBrothersStack.push({
brotherPathIndex: pathIndex,
paramsCount: params.length
})
lastParametricBrother = node.parametricBrother
}

idxInOriginalPath = idxInOriginalPath -
(parametricBrotherPath.length - path.length)
path = parametricBrotherPath
pathLen = parametricBrotherPath.length
len = prefixLen
// if exist, save the wildcard child
if (currentNode.wildcardChild !== null) {
wildcardNode = currentNode.wildcardChild
wildcardNodePathIndex = pathIndex
}

var kind = node.kind
currentNode = node
const kind = node.kind

// static route
if (kind === NODE_TYPES.STATIC) {
// if exist, save the wildcard child
if (currentNode.wildcardChild !== null) {
wildcardNode = currentNode.wildcardChild
pathLenWildcard = pathLen
}
currentNode = node
pathIndex += node.prefix.length
continue
}

if (len !== prefixLen) {
return this._getWildcardNode(wildcardNode, originalPath, pathLenWildcard, derivedConstraints, params)
}

// if exist, save the wildcard child
if (currentNode.wildcardChild !== null) {
wildcardNode = currentNode.wildcardChild
pathLenWildcard = pathLen
}
let paramEndIndex = pathIndex

// parametric route
if (kind === NODE_TYPES.PARAM) {
currentNode = node
i = path.indexOf('/')
if (i === -1) i = pathLen
if (i > maxParamLength) return null
decoded = sanitizedUrl.sliceParameter(idxInOriginalPath, idxInOriginalPath + i)
if (decoded === null) {
return this._onBadUrl(originalPath.slice(idxInOriginalPath, idxInOriginalPath + i))
for (; paramEndIndex < pathLen; paramEndIndex++) {
if (path.charCodeAt(paramEndIndex) === 47) {
break
}
}
params.push(decoded)
path = path.slice(i)
idxInOriginalPath += i
continue
}

// wildcard route
if (kind === NODE_TYPES.MATCH_ALL) {
decoded = sanitizedUrl.sliceParameter(idxInOriginalPath)
if (decoded === null) {
return this._onBadUrl(originalPath.slice(idxInOriginalPath))
}
params.push(decoded)
currentNode = node
path = ''
continue
paramEndIndex = pathLen
}

// parametric(regex) route
if (kind === NODE_TYPES.REGEX) {
currentNode = node
i = path.indexOf('/')
if (i === -1) i = pathLen
if (i > maxParamLength) return null
decoded = sanitizedUrl.sliceParameter(idxInOriginalPath, idxInOriginalPath + i)
if (decoded === null) {
return this._onBadUrl(originalPath.slice(idxInOriginalPath, idxInOriginalPath + i))
for (; paramEndIndex < pathLen; paramEndIndex++) {
if (path.charCodeAt(paramEndIndex) === 47) {
break
}
}
if (!node.regex.test(path.slice(pathIndex, paramEndIndex))) {
return null
}
if (!node.regex.test(decoded)) return null
params.push(decoded)
path = path.slice(i)
idxInOriginalPath += i
continue
}

// multiparametric route
if (kind === NODE_TYPES.MULTI_PARAM) {
currentNode = node
i = 0
if (node.regex !== null) {
var matchedParameter = path.match(node.regex)
const matchedParameter = node.regex.exec(path.slice(pathIndex))
if (matchedParameter === null) return null
i = matchedParameter[1].length
paramEndIndex = pathIndex + matchedParameter[1].length
} else {
while (i < pathLen && path.charCodeAt(i) !== 47 && path.charCodeAt(i) !== 45 && path.charCodeAt(i) !== 46) i++
if (i > maxParamLength) return null
}
decoded = sanitizedUrl.sliceParameter(idxInOriginalPath, idxInOriginalPath + i)
if (decoded === null) {
return this._onBadUrl(originalPath.slice(idxInOriginalPath, idxInOriginalPath + i))
for (; paramEndIndex < pathLen; paramEndIndex++) {
const charCode = path.charCodeAt(paramEndIndex)
if (charCode === 47 || charCode === 45 || charCode === 46) {
break
}
}
}
params.push(decoded)
path = path.slice(i)
idxInOriginalPath += i
continue
}

wildcardNode = null
if (paramEndIndex > pathIndex + maxParamLength) {
return null
}

const decoded = sanitizedUrl.sliceParameter(pathIndex, paramEndIndex)
if (decoded === null) {
return this._onBadUrl(path.slice(pathIndex, paramEndIndex))
}

params.push(decoded)
pathIndex = paramEndIndex
}
}

Router.prototype._getWildcardNode = function (node, sanitizedUrl, len, derivedConstraints, params) {
if (node === null) return null
var decoded = sanitizedUrl.slice(-len)
var decoded = sanitizedUrl.slice(len)
if (decoded === null) {
return this._onBadUrl(sanitizedUrl.slice(-len))
return this._onBadUrl(sanitizedUrl.slice(len))
}

var handle = derivedConstraints !== undefined ? node.getMatchingHandler(derivedConstraints) : node.unconstrainedHandler
Expand Down