Compare commits

..

2 commits

Author SHA1 Message Date
ee0326c9f4 remove all the nonsense benchmark stuff 2024-11-10 00:26:27 +00:00
566c59ec95 more benchmark nonsense 2024-11-10 00:24:14 +00:00
9 changed files with 40 additions and 3919 deletions

View file

@ -63,8 +63,6 @@ const statuses = {
304: true 304: true
} }
} }
const __paramMapName = '__param'
const __fullParamMapName = '__fullparam'
function assertIsHandler(handler, name) { function assertIsHandler(handler, name) {
if (typeof(handler) !== 'function') { if (typeof(handler) !== 'function') {
@ -428,7 +426,6 @@ const regCleanNonAschii = /(?![a-zA-Z_])./g
const regCleanRest = /_+/g const regCleanRest = /_+/g
const regStarDoubleParam = /::[^:/]+/g const regStarDoubleParam = /::[^:/]+/g
const regStarSingleParam = /:[^:/]+/g const regStarSingleParam = /:[^:/]+/g
const SlashCode = '/'.charCodeAt(0)
const spaces = ' ' const spaces = ' '
export class FlaskaRouter { export class FlaskaRouter {
@ -535,6 +532,7 @@ export class FlaskaRouter {
let staticPaths = new Map() let staticPaths = new Map()
let paramsPaths = [] let paramsPaths = []
let collator = new Intl.Collator('en', { sensitivity: 'accent' }); let collator = new Intl.Collator('en', { sensitivity: 'accent' });
let sealed = Object.seal({})
paths.forEach(function(entry) { paths.forEach(function(entry) {
if (entry.path[0] !== '/') throw new RouterError(entry, null, 'Specified route was missing forward slash at start') if (entry.path[0] !== '/') throw new RouterError(entry, null, 'Specified route was missing forward slash at start')
@ -543,7 +541,7 @@ export class FlaskaRouter {
if (entry.path.indexOf('/:') < 0 && separateStatic) { if (entry.path.indexOf('/:') < 0 && separateStatic) {
return staticPaths.set(entry.path, { return staticPaths.set(entry.path, {
path: entry, path: entry,
params: {} params: sealed,
}) })
} }
@ -597,13 +595,15 @@ export class FlaskaRouter {
: '') : '')
} }
__treeIntoCompiledCodeReturnPath(indentString, paths, branch, params) { __treeIntoCompiledCodeReturnPath(indentString, paths, branch, params, pathParamMapIndex) {
let pathIndex = paths.indexOf(branch.path) let pathIndex = paths.indexOf(branch.path)
if (pathIndex < 0) { if (pathIndex < 0) {
throw new RouterError(branch.path, null, 'InternalError: Specified path was not found in paths') throw new RouterError(branch.path, null, 'InternalError: Specified path was not found in paths')
} }
let mapIndex = pathParamMapIndex.size + 1
pathParamMapIndex.set(mapIndex, pathIndex)
let output = '\n' + indentString + `return {` let output = '\n' + indentString + `return {`
output += '\n' + indentString + ` path: paths[${pathIndex}],` output += '\n' + indentString + ` path: paths_${mapIndex},`
if (params.length) { if (params.length) {
output += '\n' + indentString + ` params: {` output += '\n' + indentString + ` params: {`
for (let param of params) { for (let param of params) {
@ -611,13 +611,13 @@ export class FlaskaRouter {
} }
output += '\n' + indentString + ` },` output += '\n' + indentString + ` },`
} else { } else {
output += '\n' + indentString + ` params: {},` output += '\n' + indentString + ` {},`
} }
output += '\n' + indentString + `}` output += '\n' + indentString + `}`
return output return output
} }
__treeIntoCompiledCodeBranch(paths, branches, indent = 0, params = []) { __treeIntoCompiledCodeBranch(paths, branches, indent = 0, params = [], pathParamMapIndex) {
let output = '' let output = ''
let indentation = spaces.slice(0, (indent - params.length) * 2) let indentation = spaces.slice(0, (indent - params.length) * 2)
let addEndBracket = true let addEndBracket = true
@ -637,8 +637,8 @@ export class FlaskaRouter {
output += `if (str.charCodeAt(${this.__getIndex(indent, 0, params)}) === ${branch.char.charCodeAt(0)}) { // ${branch.char}` output += `if (str.charCodeAt(${this.__getIndex(indent, 0, params)}) === ${branch.char.charCodeAt(0)}) { // ${branch.char}`
if (branch.path) { if (branch.path) {
output += '\n' + indentation + ` if (str.length === ${this.__getIndex(indent, 1, params)}) {` output += '\n' + indentation + ` if (strLength === ${this.__getIndex(indent, 1, params)}) {`
output += this.__treeIntoCompiledCodeReturnPath(indentation + ' ', paths, branch, params) output += this.__treeIntoCompiledCodeReturnPath(indentation + ' ', paths, branch, params, pathParamMapIndex)
output += '\n' + indentation + ` }` output += '\n' + indentation + ` }`
} }
} else { } else {
@ -650,10 +650,10 @@ export class FlaskaRouter {
params.push([branch.isParams || branch.isFullParams, paramVarName]) params.push([branch.isParams || branch.isFullParams, paramVarName])
if (branch.isFullParams) { if (branch.isFullParams) {
output += this.__treeIntoCompiledCodeReturnPath(indentation, paths, branch, params) output += this.__treeIntoCompiledCodeReturnPath(indentation, paths, branch, params, pathParamMapIndex)
} else if (branch.path) { } else if (branch.path) {
output += '\n' + indentation + `if (str.length === ${this.__getIndex(indent, 0, params)}) {` output += '\n' + indentation + `if (strLength === ${this.__getIndex(indent, 0, params)}) {`
output += this.__treeIntoCompiledCodeReturnPath(indentation + ' ', paths, branch, params) output += this.__treeIntoCompiledCodeReturnPath(indentation + ' ', paths, branch, params, pathParamMapIndex)
output += '\n' + indentation + `}` output += '\n' + indentation + `}`
} }
} }
@ -664,7 +664,7 @@ export class FlaskaRouter {
} else { } else {
output += '\n' + indentation + ' ' output += '\n' + indentation + ' '
} }
output += this.__treeIntoCompiledCodeBranch(paths, branch.children, indent + 1, params.slice()) output += this.__treeIntoCompiledCodeBranch(paths, branch.children, indent + 1, params.slice(), pathParamMapIndex)
} }
if (addEndBracket) { if (addEndBracket) {
output += '\n' + indentation + '} ' output += '\n' + indentation + '} '
@ -674,7 +674,9 @@ export class FlaskaRouter {
} }
__treeIntoCompiledCodeClosure(paths, tree, staticList) { __treeIntoCompiledCodeClosure(paths, tree, staticList) {
let output = 'return function RBufferStrSliceClosure(str) {' let pathParamMapIndex = new Map()
let output = ''
let prefix = ''
if (staticList.size > 0) { if (staticList.size > 0) {
output += '\n let checkStatic = staticList.get(str)' output += '\n let checkStatic = staticList.get(str)'
output += '\n if(checkStatic) {' output += '\n if(checkStatic) {'
@ -682,11 +684,17 @@ export class FlaskaRouter {
output += '\n }' output += '\n }'
} }
if (tree.length) { if (tree.length) {
output += '\n ' + this.__treeIntoCompiledCodeBranch(paths, tree, 1, []) output += '\n let strLength = str.length'
output += '\n ' + this.__treeIntoCompiledCodeBranch(paths, tree, 1, [], pathParamMapIndex)
} }
output += '\n return null' output += '\n return null'
output += '\n}' output += '\n}'
//console.log(output) pathParamMapIndex.forEach(function (val, key) {
prefix += `let paths_${key} = paths[${val}]\n`
})
output = prefix + 'return function flaskaFastRouter(str) {\n "use strict";' + output
return new Function('paths', 'staticList', output)(paths, staticList) return new Function('paths', 'staticList', output)(paths, staticList)
} }
@ -1223,6 +1231,7 @@ ctx.state.nonce = nonce;
// Waiting 0.1 second for it to close down // Waiting 0.1 second for it to close down
setTimeout(res, 100) setTimeout(res, 100)
}) })
this.server.closeAllConnections()
}) })
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,242 +0,0 @@
class RouterError extends Error {
constructor(route1, route2, ...params) {
// Pass remaining arguments (including vendor specific ones) to parent constructor
super(...params);
// Maintains proper stack trace for where our error was thrown (only available on V8)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, RouterError);
}
this.name = "RouterError";
this.routeA = route1
this.routeB = route2
}
}
function Child(split, x, i) {
this.path = null
this.isParams = split[x].isParams ? split[x].word : null
this.isFullParams = split[x].isFullParams ? split[x].word : null
this.paramVarName = split[x].paramVarName ?? null
this.char = !this.isParams && !this.isFullParams ? split[x].word[i] || '/' : null
this.count = 0
this.children = []
}
function buildChild(x, i, splitPaths) {
let splitPath = splitPaths[0]
let letter = new Child(splitPath.split, x, i)
let consume = []
if (splitPath.split.length === x + 1
&& (splitPath.split[x].isParams
|| splitPath.split[x].isFullParams
|| splitPath.split[x].word.length === i + 1)) {
letter.path = splitPath.entry
letter.count += 1
} else {
consume = [splitPath]
}
for (let y = 1; y < splitPaths.length; y++) {
let checkPath = splitPaths[y]
if (!checkPath.split[x]
|| checkPath.split[x].isParams !== splitPath.split[x].isParams
|| checkPath.split[x].isFullParams !== splitPath.split[x].isFullParams
|| !checkPath.split[x].isParams
&& !checkPath.split[x].isFullParams
&& (checkPath.split[x].word[i] || '/') !== letter.char) break
consume.push(checkPath)
}
letter.count += consume.length
if (splitPath.split[x].word.length === i || splitPath.split[x].isParams || splitPath.split[x].isFullParams) {
x++
i = -1
}
while (consume.length) {
letter.children.push(buildChild(x, i + 1, consume))
consume.splice(0, letter.children[letter.children.length - 1].count)
}
return letter
}
function buildTree(splitPaths) {
let builder = []
while (splitPaths.length) {
builder.push(buildChild(0, 0, splitPaths))
splitPaths.splice(0, builder[builder.length - 1].count)
}
return builder
}
const regParamPrefix = /^::?/
const regCleanNonAschii = /(?![a-zA-Z_])./g
const regCleanRest = /_+/g
function splitAndSortPaths(paths, separateStatic = true) {
let staticPaths = new Map()
let paramsPaths = []
let collator = new Intl.Collator('en', { sensitivity: 'accent' });
paths.forEach(function(entry) {
if (entry.path[0] !== '/') throw new RouterError(entry, null, 'Specified route was missing forward slash at start')
// Collect static paths separately
if (entry.path.indexOf('/:') < 0 && separateStatic) {
return staticPaths.set(entry.path, {
path: entry,
params: {}
})
}
// Collect params path separately
paramsPaths.push({
split: entry.path.slice(1).split(/\//g).map(function(word) {
let actualWord = word.replace(regParamPrefix, '')
return {
word: actualWord,
isParams: word[0] === ':' && word[1] !== ':',
isFullParams: word[0] === ':' && word[1] === ':',
paramVarName: word[0] === ':'
? actualWord.replace(regCleanNonAschii, '_').replace(regCleanRest, '_')
: null
}
}),
entry,
})
})
paramsPaths.sort(function(aGroup, bGroup) {
let length = Math.max(aGroup.split.length, bGroup.split.length)
for (let x = 0; x < length; x++) {
let a = aGroup.split[x]
let b = bGroup.split[x]
if (!a) return -1
if (!b) return 1
// Full params go last
if (a.isFullParams && b.isFullParams) throw new RouterError(aGroup.entry, bGroup.entry, 'Two full path routes found on same level')
if (a.isFullParams) return 1
if (b.isFullParams) return -1
// Params go second last
if (a.isParams && !b.isParams) return 1
if (!a.isParams && b.isParams) return -1
// otherwise sort alphabetically if not identical
if (a.word !== b.word) return collator.compare(a.word, b.word)
}
throw new RouterError(aGroup, bGroup, 'Two identical paths were found')
})
return {
staticPaths,
paramsPaths,
}
}
const SlashCode = '/'.charCodeAt(0)
function getIndex(offset, additions, params) {
return (offset + additions)
+ (params.length
? ' + ' + params.map(a => `offset${a[1]}`).join(' + ')
: '')
}
function treeIntoCompiledCodeReturnPath(indentString, paths, branch, params) {
let pathIndex = paths.indexOf(branch.path)
if (pathIndex < 0) {
throw new RouterError(branch.path, null, 'InternalError: Specified path was not found in paths')
}
let output = '\n' + indentString + `return {`
output += '\n' + indentString + ` path: paths[${pathIndex}],`
if (params.length) {
output += '\n' + indentString + ` params: {`
for (let param of params) {
output += '\n' + indentString + ` ${param[0]}: s${param[1]},`
}
output += '\n' + indentString + ` },`
} else {
output += '\n' + indentString + ` params: {},`
}
output += '\n' + indentString + `}`
return output
}
const spaces = ' '
function treeIntoCompiledCodeBranch(paths, branches, indent = 0, params = []) {
let output = ''
let indentation = spaces.slice(0, (indent - params.length) * 2)
let addEndBracket = true
for (let i = 0; i < branches.length; i++) {
let branch = branches[i]
if (i > 0) {
if (!branch.isParams && !branch.isFullParams) {
output += ' else '
} else {
// output += '} //'
output += '\n' + indentation
}
}
if (!branch.isParams && !branch.isFullParams) {
output += `if (buf[${getIndex(indent, 0, params)}] === ${branch.char.charCodeAt(0)}) { // ${branch.char}`
if (branch.path) {
output += '\n' + indentation + ` if (buf.length === ${getIndex(indent, 1, params)}) {`
output += treeIntoCompiledCodeReturnPath(indentation + ' ', paths, branch, params)
output += '\n' + indentation + ` }`
}
} else {
addEndBracket = false
let paramVarName = (params.length + 1) + '_' + branch.paramVarName
output += `let s${paramVarName} = str.slice(${getIndex(indent, 0, params)}${branch.isFullParams ? '' : `, buf.indexOf(${SlashCode}, ${getIndex(indent, 0, params)}) >>> 0`})`
output += '\n' + indentation + `let offset${paramVarName} = s${paramVarName}.length`
output += '\n' + indentation
params.push([branch.isParams || branch.isFullParams, paramVarName])
if (branch.isFullParams) {
output += treeIntoCompiledCodeReturnPath(indentation, paths, branch, params)
} else if (branch.path) {
output += '\n' + indentation + `if (buf.length === ${getIndex(indent, 0, params)}) {`
output += treeIntoCompiledCodeReturnPath(indentation + ' ', paths, branch, params)
output += '\n' + indentation + `}`
}
}
if (branch.children.length) {
if (branch.path) {
output += ' else '
} else {
output += '\n' + indentation + ' '
}
output += treeIntoCompiledCodeBranch(paths, branch.children, indent + 1, params.slice())
}
if (addEndBracket) {
output += '\n' + indentation + '} '
}
}
return output
}
function treeIntoCompiledCodeClosure(paths, tree, staticList) {
let output = 'return function RBufferStrSliceClosure(str) {'
output += '\n let checkStatic = staticList.get(str)'
output += '\n if(checkStatic) {'
output += '\n return checkStatic'
output += '\n }'
output += '\n var buf = Buffer.from(str)'
output += '\n ' + treeIntoCompiledCodeBranch(paths, tree, 1, [])
output += '\n return null'
output += '\n}'
return new Function('paths', 'staticList', output)(paths, staticList)
}
export function compilePaths(paths) {
let splitPaths = splitAndSortPaths(paths)
let tree = buildTree(splitPaths.paramsPaths.slice())
return treeIntoCompiledCodeClosure(paths, tree, splitPaths.staticPaths)
}

View file

@ -1,26 +0,0 @@
import { Flaska } from './flaska.mjs'
const port = 51026
const flaska = new Flaska({}, )
flaska.devMode()
flaska.get('/', function(ctx) {
ctx.body = { status: true }
})
flaska.get('/:item/asdf/herp/:derp/bla', function(ctx) {
ctx.body = { item: ctx.params.item }
})
flaska.get('/a', function(ctx) {
ctx.body = { status: true }
})
flaska.get('/error', function(ctx) {
process.exit(1)
})
flaska.listen(port, function() {
console.log('listening on port', port)
})

View file

@ -1072,10 +1072,23 @@ t.describe('#closeAsync()', function() {
assert.strictEqual(err, assertError) assert.strictEqual(err, assertError)
}) })
t.test('it should call closeAllConnections', async function() {
const assertError = new Error('Pirate Fight')
let flaska = new Flaska()
flaska.server = {
close: stub(),
closeAllConnections: stub(),
}
flaska.server.closeAllConnections.throws(assertError)
let err = await assert.isRejected(flaska.closeAsync())
assert.strictEqual(err, assertError)
})
t.test('should otherwise work', async function() { t.test('should otherwise work', async function() {
let flaska = new Flaska() let flaska = new Flaska()
flaska.server = { flaska.server = {
close: stub() close: stub(),
closeAllConnections: stub(),
} }
flaska.server.close.returnWith(function(cb) { flaska.server.close.returnWith(function(cb) {
cb(null, { a: 1 }) cb(null, { a: 1 })

View file

@ -1,20 +0,0 @@
import { Flaska } from './flaska_fast.mjs'
const port = 51028
const flaska = new Flaska({}, )
flaska.get('/', function(ctx) {
ctx.body = { status: true }
})
flaska.get('/:item/asdf/herp/:derp/bla', function(ctx) {
ctx.body = { item: ctx.params.item }
})
flaska.get('/error', function(ctx) {
process.exit(1)
})
flaska.listen(port, function() {
console.log('listening on port', port)
})

View file

@ -1,20 +0,0 @@
import { Flaska } from './flaska_buffer.mjs'
const port = 51027
const flaska = new Flaska({}, )
flaska.get('/', function(ctx) {
ctx.body = { status: true }
})
flaska.get('/:item/asdf/herp/:derp/bla', function(ctx) {
ctx.body = { item: ctx.params.item }
})
flaska.get('/error', function(ctx) {
process.exit(1)
})
flaska.listen(port, function() {
console.log('listening on port', port)
})