router v2 improved and finished almost
This commit is contained in:
parent
510d25d2b1
commit
29a9c786ec
11 changed files with 783 additions and 187 deletions
|
@ -35,6 +35,21 @@ export const allRoutes = [
|
||||||
'/::rest',
|
'/::rest',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
export const allRoutesGoldenBalance = [
|
||||||
|
'/',
|
||||||
|
'/a/:a/aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/:aa',
|
||||||
|
'/b/:b/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/:bb',
|
||||||
|
'/c/:c/ccccccccccccccccccccccccccccc/:cc',
|
||||||
|
'/d/:d/ddddddddddddddddddddddddddddd/:dd',
|
||||||
|
'/e/:e/eeeeeeeeeeeeeeeeeeeeeeeeeeeee/:ee',
|
||||||
|
'/f/:f/fffffffffffffffffffffffffffff/:ff',
|
||||||
|
'/g/:g/ggggggggggggggggggggggggggggg/:gg',
|
||||||
|
'/h/:h/hhhhhhhhhhhhhhhhhhhhhhhhhhhhh/:hh',
|
||||||
|
'/i/:i/iiiiiiiiiiiiiiiiiiiiiiiiiiiii/:ii',
|
||||||
|
'/j/:j/jjjjjjjjjjjjjjjjjjjjjjjjjjjjj/:jj',
|
||||||
|
'/::rest',
|
||||||
|
]
|
||||||
|
|
||||||
export const allManyRoutes = [
|
export const allManyRoutes = [
|
||||||
'/',
|
'/',
|
||||||
'/api/articles',
|
'/api/articles',
|
||||||
|
|
25
benchmark/mapl_compiler.mjs
Normal file
25
benchmark/mapl_compiler.mjs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { getExternalKeys } from '@mapl/compiler';
|
||||||
|
import { compileRouter as compileRouterContent } from '@mapl/router';
|
||||||
|
const PATH = "__req_p";
|
||||||
|
const REQ = '__req';
|
||||||
|
const PARAMS = `${REQ}_ps`;
|
||||||
|
|
||||||
|
export function compileRouter(root) {
|
||||||
|
const state = {
|
||||||
|
contentBuilder: [],
|
||||||
|
declarationBuilders: [],
|
||||||
|
externalValues: [],
|
||||||
|
|
||||||
|
compileItem: (item, state, hasParam) => {
|
||||||
|
state.contentBuilder.push(`return [f${state.externalValues.push(item)},${hasParam ? PARAMS : '[]'}];`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
compileRouterContent(root, state);
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
return Function(
|
||||||
|
...getExternalKeys(state),
|
||||||
|
`return (${PATH})=>{${state.contentBuilder.join('')}return null;}`
|
||||||
|
)(...state.externalValues);
|
||||||
|
}
|
64
benchmark/mapl_router_compile.mjs
Normal file
64
benchmark/mapl_router_compile.mjs
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import { createRouter, insertItem } from '@mapl/router'
|
||||||
|
import { compileRouter } from './mapl_compiler.mjs'
|
||||||
|
import * as consts from './const.js'
|
||||||
|
|
||||||
|
let testPaths = consts.allManyRoutes.map(x => x.replace(/:[^\/]+/g, '*')) // consts.allManyRoutes
|
||||||
|
|
||||||
|
testPaths = [
|
||||||
|
'/*/file',
|
||||||
|
'/*',
|
||||||
|
]
|
||||||
|
|
||||||
|
const router2 = createRouter();
|
||||||
|
for (let route of testPaths) {
|
||||||
|
insertItem(router2, route, '{works}')
|
||||||
|
}
|
||||||
|
const match2 = compileRouter(router2)
|
||||||
|
for (let route of testPaths) {
|
||||||
|
let check = route.replace(/\*/g, 'something')
|
||||||
|
let gotMatched = match2(check.slice(1))
|
||||||
|
console.log(check, gotMatched?.[0] || '{ERROR NO MATCH}')
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit(0)
|
||||||
|
|
||||||
|
const r = createRouter();
|
||||||
|
for (let route of testPaths) {
|
||||||
|
let cleaned = route.replace(/:[^\/]+/g, '*')
|
||||||
|
insertItem(r, cleaned, () => { })
|
||||||
|
}
|
||||||
|
|
||||||
|
const m = compileRouter(r)
|
||||||
|
for (let item of testPaths) {
|
||||||
|
console.log(item, m(item.slice(1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
function printTime (t) {
|
||||||
|
let time = Number(t)
|
||||||
|
let units = ['n', 'μ', 'm', 'c', 's']
|
||||||
|
let unit = units[0]
|
||||||
|
let unitPower = 1
|
||||||
|
for (let i = 0; i < units.length; i++) {
|
||||||
|
let power = Math.pow(10, (i + 1) * 3)
|
||||||
|
if (power * 2 > time) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
unitPower = power
|
||||||
|
unit = units[1]
|
||||||
|
}
|
||||||
|
console.log(t, '=', Number((time / unitPower).toFixed(2)), unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
let paths = testPaths.map(x => ({ path: x }))
|
||||||
|
|
||||||
|
let s1 = process.hrtime.bigint()
|
||||||
|
let s2 = process.hrtime.bigint()
|
||||||
|
|
||||||
|
const match = compileRouter(router);
|
||||||
|
console.log(match.toString());
|
||||||
|
|
||||||
|
let s3 = process.hrtime.bigint()
|
||||||
|
|
||||||
|
let time = s3 - s2 - (s2 - s1)
|
||||||
|
|
||||||
|
printTime(time)
|
|
@ -11,6 +11,7 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "WTFPL",
|
"license": "WTFPL",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@mapl/router": "^0.0.14",
|
||||||
"benchmarkjs-pretty": "^2.0.0",
|
"benchmarkjs-pretty": "^2.0.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"koa-router": "^8.0.8",
|
"koa-router": "^8.0.8",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { summary, run, bench } from 'mitata';
|
import { summary, run, bench } from 'mitata';
|
||||||
import assert from 'assert'
|
import assert from 'assert'
|
||||||
import { compilePaths } from "../router_v2.mjs"
|
import { compilePaths } from "./router_v2.mjs"
|
||||||
import * as consts from './const.js'
|
import * as consts from './const.js'
|
||||||
|
|
||||||
function printCurrentStatus(fn) {
|
function printCurrentStatus(fn) {
|
||||||
|
@ -42,7 +42,7 @@ let paths = [
|
||||||
{ path: '/::rest', },
|
{ path: '/::rest', },
|
||||||
]
|
]
|
||||||
|
|
||||||
paths = consts.allManyRoutes.map(x => ({ path: x }))
|
paths = consts.allRoutesGoldenBalance.map(x => ({ path: x }))
|
||||||
|
|
||||||
let tests = [
|
let tests = [
|
||||||
['/', paths[5]],
|
['/', paths[5]],
|
||||||
|
|
517
benchmark/router_v2.mjs
Normal file
517
benchmark/router_v2.mjs
Normal file
|
@ -0,0 +1,517 @@
|
||||||
|
const Debug = false
|
||||||
|
|
||||||
|
export function printTree(children, indent = 0) {
|
||||||
|
if (!children.length || !Debug) return
|
||||||
|
|
||||||
|
for (let child of children) {
|
||||||
|
console.log(''.padStart(indent * 2) + (child.char || '*')
|
||||||
|
+ ' ' + (child.isParams ? `{ params = ${child.isParams} }` : '')
|
||||||
|
+ (child.isFullParams ? `{ fullParams = ${child.isFullParams} }` : '')
|
||||||
|
+ ' ' + (child.path ? `{ handler = ${child.path.path} }` : ''))
|
||||||
|
printTree(child.children, indent + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
export 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 treeIntoCompiledTreeBufferReturnPath(indentString, paths, branch, params, alt) {
|
||||||
|
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 = ''
|
||||||
|
output += '\n' + indentString + `return {`
|
||||||
|
output += '\n' + indentString + ` path: paths[${pathIndex}],`
|
||||||
|
if (params.length) {
|
||||||
|
output += '\n' + indentString + ` params: {`
|
||||||
|
for (let param of params) {
|
||||||
|
if (alt === 0) {
|
||||||
|
output += '\n' + indentString + ` ${param[0]}: s${param[1]}.toString(),`
|
||||||
|
} else if (alt === 1 || alt === 3) {
|
||||||
|
output += '\n' + indentString + ` ${param[0]}: s${param[1]},`
|
||||||
|
} else if (alt === 2) {
|
||||||
|
output += '\n' + indentString + ` ${param[0]}: textDecoder.decode(s${param[1]}),`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output += '\n' + indentString + ` },`
|
||||||
|
} else {
|
||||||
|
output += '\n' + indentString + ` params: {},`
|
||||||
|
}
|
||||||
|
output += '\n' + indentString + `}`
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
function treeIntoCompiledTreeBufferBranch(paths, branches, indent = 0, params = [], alt = 0) {
|
||||||
|
let output = ''
|
||||||
|
let indentation = ''.padStart((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 += treeIntoCompiledTreeBufferReturnPath(indentation + ' ', paths, branch, params, alt)
|
||||||
|
output += '\n' + indentation + ` }`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addEndBracket = false
|
||||||
|
let paramVarName = (params.length + 1) + '_' + branch.paramVarName
|
||||||
|
if (alt === 1 || alt === 3) {
|
||||||
|
output += `let s${paramVarName} = str.slice(${getIndex(indent, 0, params)}${branch.isFullParams ? '' : `, buf.indexOf(${SlashCode}, ${getIndex(indent, 0, params)}) >>> 0`})`
|
||||||
|
} else {
|
||||||
|
output += `let s${paramVarName} = buf.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 += treeIntoCompiledTreeBufferReturnPath(indentation, paths, branch, params, alt)
|
||||||
|
} else if (branch.path) {
|
||||||
|
output += '\n' + indentation + `if (buf.length === ${getIndex(indent, 0, params)}) {`
|
||||||
|
output += treeIntoCompiledTreeBufferReturnPath(indentation + ' ', paths, branch, params, alt)
|
||||||
|
output += '\n' + indentation + `}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (branch.children.length) {
|
||||||
|
if (branch.path) {
|
||||||
|
output += ' else '
|
||||||
|
} else {
|
||||||
|
output += '\n' + indentation + ' '
|
||||||
|
}
|
||||||
|
output += treeIntoCompiledTreeBufferBranch(paths, branch.children, indent + 1, params.slice())
|
||||||
|
}
|
||||||
|
if (addEndBracket) {
|
||||||
|
output += '\n' + indentation + '} '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
export function treeIntoCompiledTreeBuffer(paths, tree) {
|
||||||
|
let output = 'return function RBuffer(paths, static, str) {'
|
||||||
|
output += '\n let checkStatic = static.get(str)'
|
||||||
|
output += '\n if(checkStatic) {'
|
||||||
|
output += '\n return checkStatic'
|
||||||
|
output += '\n }'
|
||||||
|
output += '\n var buf = Buffer.from(str)'
|
||||||
|
output += '\n ' + treeIntoCompiledTreeBufferBranch(paths, tree, 1)
|
||||||
|
output += '\n return null'
|
||||||
|
output += '\n}'
|
||||||
|
if (Debug) {
|
||||||
|
console.log(output)
|
||||||
|
}
|
||||||
|
return new Function(output)()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function treeIntoCompiledTreeStringReturnPath(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 = ''
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
function treeIntoCompiledTreeStringBranch(paths, branches, indent = 0, params = []) {
|
||||||
|
let output = ''
|
||||||
|
let indentation = ''.padStart((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 (str.charCodeAt(${getIndex(indent, 0, params)}) === ${branch.char.charCodeAt(0)}) { // ${branch.char}`
|
||||||
|
|
||||||
|
if (branch.path) {
|
||||||
|
output += '\n' + indentation + ` if (str.length === ${getIndex(indent, 1, params)}) {`
|
||||||
|
output += treeIntoCompiledTreeStringReturnPath(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 ? '' : `, str.indexOf('/', ${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 += treeIntoCompiledTreeStringReturnPath(indentation, paths, branch, params)
|
||||||
|
} else if (branch.path) {
|
||||||
|
output += '\n' + indentation + `if (str.length === ${getIndex(indent, 0, params)}) {`
|
||||||
|
output += treeIntoCompiledTreeStringReturnPath(indentation + ' ', paths, branch, params)
|
||||||
|
output += '\n' + indentation + `}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (branch.children.length) {
|
||||||
|
if (branch.path) {
|
||||||
|
output += ' else '
|
||||||
|
} else {
|
||||||
|
output += '\n' + indentation + ' '
|
||||||
|
}
|
||||||
|
output += treeIntoCompiledTreeStringBranch(paths, branch.children, indent + 1, params.slice())
|
||||||
|
}
|
||||||
|
if (addEndBracket) {
|
||||||
|
output += '\n' + indentation + '} '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
function treeIntoCompiledTreeStringBranchIndices(paths, branches, indent = 0, params = []) {
|
||||||
|
let output = ''
|
||||||
|
let indentation = ''.padStart((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 (str[${getIndex(indent, 0, params)}] === '${branch.char}') { // ${branch.char}`
|
||||||
|
|
||||||
|
if (branch.path) {
|
||||||
|
output += '\n' + indentation + ` if (str.length === ${getIndex(indent, 1, params)}) {`
|
||||||
|
output += treeIntoCompiledTreeStringReturnPath(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 ? '' : `, str.indexOf('/', ${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 += treeIntoCompiledTreeStringReturnPath(indentation, paths, branch, params)
|
||||||
|
} else if (branch.path) {
|
||||||
|
output += '\n' + indentation + `if (str.length === ${getIndex(indent, 0, params)}) {`
|
||||||
|
output += treeIntoCompiledTreeStringReturnPath(indentation + ' ', paths, branch, params)
|
||||||
|
output += '\n' + indentation + `}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (branch.children.length) {
|
||||||
|
if (branch.path) {
|
||||||
|
output += ' else '
|
||||||
|
} else {
|
||||||
|
output += '\n' + indentation + ' '
|
||||||
|
}
|
||||||
|
output += treeIntoCompiledTreeStringBranch(paths, branch.children, indent + 1, params.slice())
|
||||||
|
}
|
||||||
|
if (addEndBracket) {
|
||||||
|
output += '\n' + indentation + '} '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
export function treeIntoCompiledTreeString(paths, tree) {
|
||||||
|
let output = 'return function RString(paths, static, str) {'
|
||||||
|
output += '\n let checkStatic = static.get(str)'
|
||||||
|
output += '\n if(checkStatic) {'
|
||||||
|
output += '\n return checkStatic'
|
||||||
|
output += '\n }'
|
||||||
|
output += '\n ' + treeIntoCompiledTreeStringBranch(paths, tree, 1)
|
||||||
|
output += '\n return null'
|
||||||
|
output += '\n}'
|
||||||
|
if (Debug) {
|
||||||
|
console.log(output)
|
||||||
|
}
|
||||||
|
return new Function(output)()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function treeIntoCompiledTreeStringIndices(paths, tree) {
|
||||||
|
let output = 'return function RStringIndices(paths, static, str) {'
|
||||||
|
output += '\n let checkStatic = static.get(str)'
|
||||||
|
output += '\n if(checkStatic) {'
|
||||||
|
output += '\n return checkStatic'
|
||||||
|
output += '\n }'
|
||||||
|
output += '\n ' + treeIntoCompiledTreeStringBranchIndices(paths, tree, 1)
|
||||||
|
output += '\n return null'
|
||||||
|
output += '\n}'
|
||||||
|
if (Debug) {
|
||||||
|
console.log(output)
|
||||||
|
}
|
||||||
|
return new Function(output)()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function treeIntoCompiledTreeBufferAltOne(paths, tree) {
|
||||||
|
let output = 'return function RBufferStrSlice(paths, static, str) {'
|
||||||
|
output += '\n let checkStatic = static.get(str)'
|
||||||
|
output += '\n if(checkStatic) {'
|
||||||
|
output += '\n return checkStatic'
|
||||||
|
output += '\n }'
|
||||||
|
output += '\n var buf = Buffer.from(str)'
|
||||||
|
output += '\n ' + treeIntoCompiledTreeBufferBranch(paths, tree, 1, [], 1)
|
||||||
|
output += '\n return null'
|
||||||
|
output += '\n}'
|
||||||
|
if (Debug) {
|
||||||
|
console.log(output)
|
||||||
|
}
|
||||||
|
return new Function(output)()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function treeIntoCompiledTreeBufferAltTwo(paths, tree) {
|
||||||
|
let output = 'return function RTextEncoderDecoder(textEncoder, textDecoder, paths, static, str) {'
|
||||||
|
output += '\n let checkStatic = static.get(str)'
|
||||||
|
output += '\n if(checkStatic) {'
|
||||||
|
output += '\n return checkStatic'
|
||||||
|
output += '\n }'
|
||||||
|
output += '\n var buf = textEncoder.encode(str)'
|
||||||
|
output += '\n ' + treeIntoCompiledTreeBufferBranch(paths, tree, 1, [], 2)
|
||||||
|
output += '\n return null'
|
||||||
|
output += '\n}'
|
||||||
|
if (Debug) {
|
||||||
|
console.log(output)
|
||||||
|
}
|
||||||
|
return new Function(output)()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function treeIntoCompiledTreeBufferAltThree(paths, tree) {
|
||||||
|
let output = 'return function RTextEncoderStrSlice(textEncoder, paths, static, str) {'
|
||||||
|
output += '\n let checkStatic = static.get(str)'
|
||||||
|
output += '\n if(checkStatic) {'
|
||||||
|
output += '\n return checkStatic'
|
||||||
|
output += '\n }'
|
||||||
|
output += '\n var buf = textEncoder.encode(str)'
|
||||||
|
output += '\n ' + treeIntoCompiledTreeBufferBranch(paths, tree, 1, [], 3)
|
||||||
|
output += '\n return null'
|
||||||
|
output += '\n}'
|
||||||
|
if (Debug) {
|
||||||
|
console.log(output)
|
||||||
|
}
|
||||||
|
return new Function(output)()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compilePaths(paths) {
|
||||||
|
let splitPaths = splitAndSortPaths(paths)
|
||||||
|
let tree = buildTree(splitPaths.paramsPaths.slice())
|
||||||
|
printTree(tree, 0)
|
||||||
|
let compiled = treeIntoCompiledTreeBuffer(paths, tree)
|
||||||
|
let compiledString = treeIntoCompiledTreeString(paths, tree)
|
||||||
|
let compiledStringIndices = treeIntoCompiledTreeStringIndices(paths, tree)
|
||||||
|
|
||||||
|
let compiledTextOne = treeIntoCompiledTreeBufferAltOne(paths, tree)
|
||||||
|
|
||||||
|
// let compiledTextTwo = treeIntoCompiledTreeBufferAltTwo(paths, tree)
|
||||||
|
// let compiledTextThree = treeIntoCompiledTreeBufferAltThree(paths, tree)
|
||||||
|
return [
|
||||||
|
[compiled, compiled.bind(null, paths, splitPaths.staticPaths)],
|
||||||
|
[compiledTextOne, compiledTextOne.bind(null, paths, splitPaths.staticPaths)],
|
||||||
|
// [compiledTextTwo, compiledTextTwo.bind(null, new TextEncoder(), new TextDecoder(), paths, splitPaths.staticPaths)],
|
||||||
|
// [compiledTextThree, compiledTextThree.bind(null, new TextEncoder(), paths, splitPaths.staticPaths)],
|
||||||
|
[compiledString, compiledString.bind(null, paths, splitPaths.staticPaths)],
|
||||||
|
[compiledStringIndices, compiledStringIndices.bind(null, paths, splitPaths.staticPaths)],
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compilePathsBuffer(paths) {
|
||||||
|
let splitPaths = splitAndSortPaths(paths)
|
||||||
|
let tree = buildTree(splitPaths.paramsPaths.slice())
|
||||||
|
printTree(tree, 0)
|
||||||
|
let compiled = treeIntoCompiledTreeBuffer(paths, tree)
|
||||||
|
let compiledString = treeIntoCompiledTreeString(paths, tree)
|
||||||
|
let compiledStringIndices = treeIntoCompiledTreeStringIndices(paths, tree)
|
||||||
|
|
||||||
|
let compiledTextOne = treeIntoCompiledTreeBufferAltOne(paths, tree)
|
||||||
|
|
||||||
|
// let compiledTextTwo = treeIntoCompiledTreeBufferAltTwo(paths, tree)
|
||||||
|
// let compiledTextThree = treeIntoCompiledTreeBufferAltThree(paths, tree)
|
||||||
|
return [
|
||||||
|
[compiled, compiled.bind(null, paths, splitPaths.staticPaths)],
|
||||||
|
[compiledTextOne, compiledTextOne.bind(null, paths, splitPaths.staticPaths)],
|
||||||
|
// [compiledTextTwo, compiledTextTwo.bind(null, new TextEncoder(), new TextDecoder(), paths, splitPaths.staticPaths)],
|
||||||
|
// [compiledTextThree, compiledTextThree.bind(null, new TextEncoder(), paths, splitPaths.staticPaths)],
|
||||||
|
[compiledString, compiledString.bind(null, paths, splitPaths.staticPaths)],
|
||||||
|
[compiledStringIndices, compiledStringIndices.bind(null, paths, splitPaths.staticPaths)],
|
||||||
|
]
|
||||||
|
}
|
31
benchmark/router_v2_compile.mjs
Normal file
31
benchmark/router_v2_compile.mjs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { compilePaths } from "../router_v2.mjs"
|
||||||
|
import * as consts from './const.js'
|
||||||
|
|
||||||
|
function printTime (t) {
|
||||||
|
let time = Number(t)
|
||||||
|
let units = ['n', 'μ', 'm', 'c', 's']
|
||||||
|
let unit = units[0]
|
||||||
|
let unitPower = 1
|
||||||
|
for (let i = 0; i < units.length; i++) {
|
||||||
|
let power = Math.pow(10, (i + 1) * 3)
|
||||||
|
if (power * 2 > time) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
unitPower = power
|
||||||
|
unit = units[1]
|
||||||
|
}
|
||||||
|
console.log(t, '=', Number((time / unitPower).toFixed(2)), unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
let paths = consts.allManyRoutes.map(x => ({ path: x }))
|
||||||
|
|
||||||
|
let s1 = process.hrtime.bigint()
|
||||||
|
let s2 = process.hrtime.bigint()
|
||||||
|
|
||||||
|
compilePaths(paths)
|
||||||
|
|
||||||
|
let s3 = process.hrtime.bigint()
|
||||||
|
|
||||||
|
let time = s3 - s2 - (s2 - s1)
|
||||||
|
|
||||||
|
printTime(time)
|
0
benchmark/router_v2_compiler_runner.mjs
Normal file
0
benchmark/router_v2_compiler_runner.mjs
Normal file
115
benchmark/strings_compare.mjs
Normal file
115
benchmark/strings_compare.mjs
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
import { printCurrentStatus, printStatusHelperText } from "./compiler/utils.mjs";
|
||||||
|
import { summary, run, bench } from 'mitata';
|
||||||
|
|
||||||
|
// Do warmup
|
||||||
|
bench('noop', () => { })
|
||||||
|
bench('noop2', () => { })
|
||||||
|
|
||||||
|
// Do checks with arrays
|
||||||
|
let arr = ['hi', 'there', 'nagna', 'randomlongstring', 'small']
|
||||||
|
arr = arr.map(x => [x, { path: x }])
|
||||||
|
let paths = arr.map(x => x[1])
|
||||||
|
|
||||||
|
// Do checks with objects
|
||||||
|
let obj = {};
|
||||||
|
let map = new Map()
|
||||||
|
for (let i = 0, l = arr.length; i < l; ++i) {
|
||||||
|
obj[arr[i][0]] = arr[i][1]
|
||||||
|
map.set(arr[i][0], arr[i][1])
|
||||||
|
};
|
||||||
|
let objHas1 = (obj, item) => { let x = obj[item]; if (x) { return x } return null };
|
||||||
|
let objHas2 = (obj, item) => { if (item in obj) { return obj[item] } return null }
|
||||||
|
let mapGet = (map, item) => { let x = map.get(item); if (x) { return x }; return null };
|
||||||
|
|
||||||
|
|
||||||
|
// Generate a function which compares the input string to all strings in the list
|
||||||
|
let basicIfChain = Function(`
|
||||||
|
return (paths, str) => {
|
||||||
|
${arr.map((item, i) => 'if (str === "' + item[0] + '") { return paths[' + i + '] }').join('\n ')}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
`)();
|
||||||
|
let basicIfChainAlt = Function(`
|
||||||
|
return (str) => {
|
||||||
|
${arr.map((item, i) => 'if (str === "' + item[0] + '") { return "' + item[0] + '" }').join('\n ')}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
`)();
|
||||||
|
function basicIfChainNoFunc(str) {
|
||||||
|
if (str === "hi") { return "hi" }
|
||||||
|
if (str === "there") { return "there" }
|
||||||
|
if (str === "nagna") { return "nagna" }
|
||||||
|
if (str === "randomlongstring") { return "randomlongstring" }
|
||||||
|
if (str === "small") { return "small" }
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
function addTwo(a, b) {
|
||||||
|
return a + b
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`
|
||||||
|
return (str) => {
|
||||||
|
${arr.map((item, i) => 'if (str === "' + item[0] + '") { return "' + item[0] + '" }').join('\n ')}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
let func = [
|
||||||
|
['basicIfChainNoFunc', basicIfChainNoFunc, basicIfChainNoFunc],
|
||||||
|
['basicIfChainAlt', basicIfChainAlt, basicIfChainAlt],
|
||||||
|
['objHas1', objHas1, objHas1.bind(null, obj)],
|
||||||
|
['objHas2', objHas2, objHas2.bind(null, obj)],
|
||||||
|
['mapGet', mapGet, mapGet.bind(null, map)],
|
||||||
|
['basicIfChain', basicIfChain, basicIfChain.bind(null, paths)],
|
||||||
|
]
|
||||||
|
|
||||||
|
for (let [name, __, fun] of func) {
|
||||||
|
console.log(`--- Sanity checking ${name} ---`)
|
||||||
|
for (let a of arr) {
|
||||||
|
console.log(a[0], fun(a[0]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate strings
|
||||||
|
let newArr = [];
|
||||||
|
for (let i = 0; i < 100000; i++)
|
||||||
|
newArr.push(Math.random() < 0.5 ? `${Math.random()}` : arr[Math.round(Math.random() * 4)].slice(0, -1 >>> 0));
|
||||||
|
|
||||||
|
console.log(`--- warming up addTwo ---`)
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
console.log(`--- run ${i} ---`)
|
||||||
|
for (let num of newArr) {
|
||||||
|
addTwo(num.length, num.length + 1)
|
||||||
|
}
|
||||||
|
await new Promise(res => setTimeout(res, 1000))
|
||||||
|
printCurrentStatus(addTwo);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (let [name, org, fun] of func) {
|
||||||
|
console.log(`--- warming up ${name} ---`)
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
console.log(`--- run ${i} ---`)
|
||||||
|
newArr.forEach(fun)
|
||||||
|
await new Promise(res => setTimeout(res, 1000))
|
||||||
|
printCurrentStatus(org);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('--- sleeping ---')
|
||||||
|
await new Promise(res => setTimeout(res, 1000))
|
||||||
|
|
||||||
|
|
||||||
|
for (let [_, org] of func) {
|
||||||
|
printCurrentStatus(org);
|
||||||
|
}
|
||||||
|
printStatusHelperText()
|
||||||
|
|
||||||
|
|
||||||
|
summary(() => {
|
||||||
|
func.forEach(([name, _, fun]) => {
|
||||||
|
bench(name, () => newArr.map(fun));
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
run();
|
|
@ -1,5 +1,5 @@
|
||||||
import assert from 'assert'
|
import assert from 'assert'
|
||||||
import { compilePaths } from "../router_v2.mjs"
|
import { compilePaths } from "./router_v2.mjs"
|
||||||
import * as consts from './const.js'
|
import * as consts from './const.js'
|
||||||
|
|
||||||
let paths = [
|
let paths = [
|
||||||
|
|
196
router_v2.mjs
196
router_v2.mjs
|
@ -1,16 +1,3 @@
|
||||||
const Debug = false
|
|
||||||
|
|
||||||
export function printTree(children, indent = 0) {
|
|
||||||
if (!children.length || !Debug) return
|
|
||||||
|
|
||||||
for (let child of children) {
|
|
||||||
console.log(''.padStart(indent * 2) + (child.char || '*')
|
|
||||||
+ ' ' + (child.isParams ? `{ params = ${child.isParams} }` : '')
|
|
||||||
+ (child.isFullParams ? `{ fullParams = ${child.isFullParams} }` : '')
|
|
||||||
+ ' ' + (child.path ? `{ handler = ${child.path.path} }` : ''))
|
|
||||||
printTree(child.children, indent + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RouterError extends Error {
|
class RouterError extends Error {
|
||||||
constructor(route1, route2, ...params) {
|
constructor(route1, route2, ...params) {
|
||||||
|
@ -157,7 +144,7 @@ function getIndex(offset, additions, params) {
|
||||||
: '')
|
: '')
|
||||||
}
|
}
|
||||||
|
|
||||||
function treeIntoCompiledTreeBufferReturnPath(indentString, paths, branch, params, alt) {
|
function treeIntoCompiledCodeReturnPath(indentString, paths, branch, params, alt) {
|
||||||
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')
|
||||||
|
@ -184,7 +171,7 @@ function treeIntoCompiledTreeBufferReturnPath(indentString, paths, branch, param
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
function treeIntoCompiledTreeBufferBranch(paths, branches, indent = 0, params = [], alt = 0) {
|
function treeIntoCompiledCodeBranch(paths, branches, indent = 0, params = [], alt = 0) {
|
||||||
let output = ''
|
let output = ''
|
||||||
let indentation = ''.padStart((indent - params.length) * 2)
|
let indentation = ''.padStart((indent - params.length) * 2)
|
||||||
let addEndBracket = true
|
let addEndBracket = true
|
||||||
|
@ -205,26 +192,22 @@ function treeIntoCompiledTreeBufferBranch(paths, branches, indent = 0, params =
|
||||||
|
|
||||||
if (branch.path) {
|
if (branch.path) {
|
||||||
output += '\n' + indentation + ` if (buf.length === ${getIndex(indent, 1, params)}) {`
|
output += '\n' + indentation + ` if (buf.length === ${getIndex(indent, 1, params)}) {`
|
||||||
output += treeIntoCompiledTreeBufferReturnPath(indentation + ' ', paths, branch, params, alt)
|
output += treeIntoCompiledCodeReturnPath(indentation + ' ', paths, branch, params, alt)
|
||||||
output += '\n' + indentation + ` }`
|
output += '\n' + indentation + ` }`
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addEndBracket = false
|
addEndBracket = false
|
||||||
let paramVarName = (params.length + 1) + '_' + branch.paramVarName
|
let paramVarName = (params.length + 1) + '_' + branch.paramVarName
|
||||||
if (alt === 1 || alt === 3) {
|
output += `let s${paramVarName} = str.slice(${getIndex(indent, 0, params)}${branch.isFullParams ? '' : `, buf.indexOf(${SlashCode}, ${getIndex(indent, 0, params)}) >>> 0`})`
|
||||||
output += `let s${paramVarName} = str.slice(${getIndex(indent, 0, params)}${branch.isFullParams ? '' : `, buf.indexOf(${SlashCode}, ${getIndex(indent, 0, params)}) >>> 0`})`
|
|
||||||
} else {
|
|
||||||
output += `let s${paramVarName} = buf.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 + `let offset${paramVarName} = s${paramVarName}.length`
|
||||||
output += '\n' + indentation
|
output += '\n' + indentation
|
||||||
params.push([branch.isParams || branch.isFullParams, paramVarName])
|
params.push([branch.isParams || branch.isFullParams, paramVarName])
|
||||||
|
|
||||||
if (branch.isFullParams) {
|
if (branch.isFullParams) {
|
||||||
output += treeIntoCompiledTreeBufferReturnPath(indentation, paths, branch, params, alt)
|
output += treeIntoCompiledCodeReturnPath(indentation, paths, branch, params, alt)
|
||||||
} else if (branch.path) {
|
} else if (branch.path) {
|
||||||
output += '\n' + indentation + `if (buf.length === ${getIndex(indent, 0, params)}) {`
|
output += '\n' + indentation + `if (buf.length === ${getIndex(indent, 0, params)}) {`
|
||||||
output += treeIntoCompiledTreeBufferReturnPath(indentation + ' ', paths, branch, params, alt)
|
output += treeIntoCompiledCodeReturnPath(indentation + ' ', paths, branch, params, alt)
|
||||||
output += '\n' + indentation + `}`
|
output += '\n' + indentation + `}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -235,7 +218,7 @@ function treeIntoCompiledTreeBufferBranch(paths, branches, indent = 0, params =
|
||||||
} else {
|
} else {
|
||||||
output += '\n' + indentation + ' '
|
output += '\n' + indentation + ' '
|
||||||
}
|
}
|
||||||
output += treeIntoCompiledTreeBufferBranch(paths, branch.children, indent + 1, params.slice())
|
output += treeIntoCompiledCodeBranch(paths, branch.children, indent + 1, params.slice())
|
||||||
}
|
}
|
||||||
if (addEndBracket) {
|
if (addEndBracket) {
|
||||||
output += '\n' + indentation + '} '
|
output += '\n' + indentation + '} '
|
||||||
|
@ -244,177 +227,22 @@ function treeIntoCompiledTreeBufferBranch(paths, branches, indent = 0, params =
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
export function treeIntoCompiledTreeBuffer(paths, tree) {
|
export function treeIntoCompiledCode(paths, tree) {
|
||||||
let output = 'return function RBuffer(paths, static, str) {'
|
|
||||||
output += '\n let checkStatic = static.get(str)'
|
|
||||||
output += '\n if(checkStatic) {'
|
|
||||||
output += '\n return checkStatic'
|
|
||||||
output += '\n }'
|
|
||||||
output += '\n var buf = Buffer.from(str)'
|
|
||||||
output += '\n ' + treeIntoCompiledTreeBufferBranch(paths, tree, 1)
|
|
||||||
output += '\n return null'
|
|
||||||
output += '\n}'
|
|
||||||
if (Debug) {
|
|
||||||
console.log(output)
|
|
||||||
}
|
|
||||||
return new Function(output)()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function treeIntoCompiledTreeStringReturnPath(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 = ''
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
function treeIntoCompiledTreeStringBranch(paths, branches, indent = 0, params = []) {
|
|
||||||
let output = ''
|
|
||||||
let indentation = ''.padStart((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 (str.charCodeAt(${getIndex(indent, 0, params)}) === ${branch.char.charCodeAt(0)}) { // ${branch.char}`
|
|
||||||
|
|
||||||
if (branch.path) {
|
|
||||||
output += '\n' + indentation + ` if (str.length === ${getIndex(indent, 1, params)}) {`
|
|
||||||
output += treeIntoCompiledTreeStringReturnPath(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 ? '' : `, str.indexOf('/', ${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 += treeIntoCompiledTreeStringReturnPath(indentation, paths, branch, params)
|
|
||||||
} else if (branch.path) {
|
|
||||||
output += '\n' + indentation + `if (str.length === ${getIndex(indent, 0, params)}) {`
|
|
||||||
output += treeIntoCompiledTreeStringReturnPath(indentation + ' ', paths, branch, params)
|
|
||||||
output += '\n' + indentation + `}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (branch.children.length) {
|
|
||||||
if (branch.path) {
|
|
||||||
output += ' else '
|
|
||||||
} else {
|
|
||||||
output += '\n' + indentation + ' '
|
|
||||||
}
|
|
||||||
output += treeIntoCompiledTreeStringBranch(paths, branch.children, indent + 1, params.slice())
|
|
||||||
}
|
|
||||||
if (addEndBracket) {
|
|
||||||
output += '\n' + indentation + '} '
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
export function treeIntoCompiledTreeString(paths, tree) {
|
|
||||||
let output = 'return function RString(paths, static, str) {'
|
|
||||||
output += '\n let checkStatic = static.get(str)'
|
|
||||||
output += '\n if(checkStatic) {'
|
|
||||||
output += '\n return checkStatic'
|
|
||||||
output += '\n }'
|
|
||||||
output += '\n ' + treeIntoCompiledTreeStringBranch(paths, tree, 1)
|
|
||||||
output += '\n return null'
|
|
||||||
output += '\n}'
|
|
||||||
if (Debug) {
|
|
||||||
console.log(output)
|
|
||||||
}
|
|
||||||
return new Function(output)()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function treeIntoCompiledTreeBufferAltOne(paths, tree) {
|
|
||||||
let output = 'return function RBufferStrSlice(paths, static, str) {'
|
let output = 'return function RBufferStrSlice(paths, static, str) {'
|
||||||
output += '\n let checkStatic = static.get(str)'
|
output += '\n let checkStatic = static.get(str)'
|
||||||
output += '\n if(checkStatic) {'
|
output += '\n if(checkStatic) {'
|
||||||
output += '\n return checkStatic'
|
output += '\n return checkStatic'
|
||||||
output += '\n }'
|
output += '\n }'
|
||||||
output += '\n var buf = Buffer.from(str)'
|
output += '\n var buf = Buffer.from(str)'
|
||||||
output += '\n ' + treeIntoCompiledTreeBufferBranch(paths, tree, 1, [], 1)
|
output += '\n ' + treeIntoCompiledCodeBranch(paths, tree, 1, [], 1)
|
||||||
output += '\n return null'
|
output += '\n return null'
|
||||||
output += '\n}'
|
output += '\n}'
|
||||||
if (Debug) {
|
|
||||||
console.log(output)
|
|
||||||
}
|
|
||||||
return new Function(output)()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function treeIntoCompiledTreeBufferAltTwo(paths, tree) {
|
|
||||||
let output = 'return function RTextEncoderDecoder(textEncoder, textDecoder, paths, static, str) {'
|
|
||||||
output += '\n let checkStatic = static.get(str)'
|
|
||||||
output += '\n if(checkStatic) {'
|
|
||||||
output += '\n return checkStatic'
|
|
||||||
output += '\n }'
|
|
||||||
output += '\n var buf = textEncoder.encode(str)'
|
|
||||||
output += '\n ' + treeIntoCompiledTreeBufferBranch(paths, tree, 1, [], 2)
|
|
||||||
output += '\n return null'
|
|
||||||
output += '\n}'
|
|
||||||
if (Debug) {
|
|
||||||
console.log(output)
|
|
||||||
}
|
|
||||||
return new Function(output)()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function treeIntoCompiledTreeBufferAltThree(paths, tree) {
|
|
||||||
let output = 'return function RTextEncoderStrSlice(textEncoder, paths, static, str) {'
|
|
||||||
output += '\n let checkStatic = static.get(str)'
|
|
||||||
output += '\n if(checkStatic) {'
|
|
||||||
output += '\n return checkStatic'
|
|
||||||
output += '\n }'
|
|
||||||
output += '\n var buf = textEncoder.encode(str)'
|
|
||||||
output += '\n ' + treeIntoCompiledTreeBufferBranch(paths, tree, 1, [], 3)
|
|
||||||
output += '\n return null'
|
|
||||||
output += '\n}'
|
|
||||||
if (Debug) {
|
|
||||||
console.log(output)
|
|
||||||
}
|
|
||||||
return new Function(output)()
|
return new Function(output)()
|
||||||
}
|
}
|
||||||
|
|
||||||
export function compilePaths(paths) {
|
export function compilePaths(paths) {
|
||||||
let splitPaths = splitAndSortPaths(paths)
|
let splitPaths = splitAndSortPaths(paths)
|
||||||
let tree = buildTree(splitPaths.paramsPaths.slice())
|
let tree = buildTree(splitPaths.paramsPaths.slice())
|
||||||
printTree(tree, 0)
|
let compiled = treeIntoCompiledCode(paths, tree)
|
||||||
let compiled = treeIntoCompiledTreeBuffer(paths, tree)
|
return compiled.bind(null, paths, splitPaths.staticPaths)
|
||||||
let compiledString = treeIntoCompiledTreeString(paths, tree)
|
}
|
||||||
let compiledTextOne = treeIntoCompiledTreeBufferAltOne(paths, tree)
|
|
||||||
let compiledTextTwo = treeIntoCompiledTreeBufferAltTwo(paths, tree)
|
|
||||||
let compiledTextThree = treeIntoCompiledTreeBufferAltThree(paths, tree)
|
|
||||||
return [
|
|
||||||
[compiled, compiled.bind(null, paths, splitPaths.staticPaths)],
|
|
||||||
[compiledTextOne, compiledTextOne.bind(null, paths, splitPaths.staticPaths)],
|
|
||||||
[compiledTextTwo, compiledTextTwo.bind(null, new TextEncoder(), new TextDecoder(), paths, splitPaths.staticPaths)],
|
|
||||||
[compiledTextThree, compiledTextThree.bind(null, new TextEncoder(), paths, splitPaths.staticPaths)],
|
|
||||||
[compiledString, compiledString.bind(null, paths, splitPaths.staticPaths)],
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue