Skip to content

Commit

Permalink
Trying to optimize interpreter
Browse files Browse the repository at this point in the history
  • Loading branch information
Saiv46 committed Jun 4, 2020
1 parent 5252c7d commit 1c5d3d1
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 248 deletions.
2 changes: 0 additions & 2 deletions .npmignore

This file was deleted.

10 changes: 8 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"description": "A simple yet powerful way to define binary protocols",
"main": "index.js",
"author": "roblabla <[email protected]>",
"sideEffects": false,
"scripts": {
"prepare": "require-self",
"lint": "standard",
Expand All @@ -22,7 +23,7 @@
"readable-stream": "^3.0.3"
},
"engines": {
"node": ">=6"
"node": ">=12"
},
"bugs": {
"url": "https://github.com/ProtoDef-io/node-protodef/issues"
Expand All @@ -39,5 +40,10 @@
"mocha": "^5.2.0",
"require-self": "^0.1.0",
"standard": "^12.0.1"
}
},
"files": [
"src/",
"ProtoDef/schemas/",
"*.js"
]
}
4 changes: 2 additions & 2 deletions src/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,13 @@ class CompiledProtodef {
}

parsePacketBuffer (type, buffer) {
const { value, size } = tryCatch(() => this.read(buffer, 0, type),
const { value: data, size } = tryCatch(() => this.read(buffer, 0, type),
(e) => {
e.message = `Read error for ${e.field} : ${e.message}`
throw e
})
return {
data: value,
data,
metadata: { size },
buffer: buffer.slice(0, size)
}
Expand Down
69 changes: 41 additions & 28 deletions src/datatypes/conditional.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,69 @@
const { getField, getFieldInfo, tryDoc, PartialReadError } = require('../utils')

module.exports = {
'switch': [readSwitch, writeSwitch, sizeOfSwitch, require('../../ProtoDef/schemas/conditional.json')['switch']],
'option': [readOption, writeOption, sizeOfOption, require('../../ProtoDef/schemas/conditional.json')['option']]
}

function readSwitch (buffer, offset, { compareTo, fields, compareToValue, 'default': defVal }, rootNode) {
compareTo = compareToValue !== undefined ? compareToValue : getField(compareTo, rootNode)
if (typeof fields[compareTo] === 'undefined' && typeof defVal === 'undefined') { throw new Error(compareTo + ' has no associated fieldInfo in switch') }

const caseDefault = typeof fields[compareTo] === 'undefined'
const resultingType = caseDefault ? defVal : fields[compareTo]
const fieldInfo = getFieldInfo(resultingType)
return tryDoc(() => this.read(buffer, offset, fieldInfo, rootNode), caseDefault ? 'default' : compareTo)
if (fields[compareTo] === undefined) {
compareTo = 'default'
fields[compareTo] = defVal
if (defVal === undefined) {
throw new Error(`${compareTo} has no associated fieldInfo in switch`)
}
}
const fieldInfo = getFieldInfo(fields[compareTo])
return tryDoc(() => this.read(buffer, offset, fieldInfo, rootNode), compareTo)
}

function writeSwitch (value, buffer, offset, { compareTo, fields, compareToValue, 'default': defVal }, rootNode) {
compareTo = compareToValue !== undefined ? compareToValue : getField(compareTo, rootNode)
if (typeof fields[compareTo] === 'undefined' && typeof defVal === 'undefined') { throw new Error(compareTo + ' has no associated fieldInfo in switch') }

const caseDefault = typeof fields[compareTo] === 'undefined'
const fieldInfo = getFieldInfo(caseDefault ? defVal : fields[compareTo])
return tryDoc(() => this.write(value, buffer, offset, fieldInfo, rootNode), caseDefault ? 'default' : compareTo)
if (fields[compareTo] === undefined) {
compareTo = 'default'
fields[compareTo] = defVal
if (defVal === undefined) {
throw new Error(`${compareTo} has no associated fieldInfo in switch`)
}
}
const fieldInfo = getFieldInfo(fields[compareTo])
return tryDoc(() => this.write(value, buffer, offset, fieldInfo, rootNode), compareTo)
}

function sizeOfSwitch (value, { compareTo, fields, compareToValue, 'default': defVal }, rootNode) {
compareTo = compareToValue !== undefined ? compareToValue : getField(compareTo, rootNode)
if (typeof fields[compareTo] === 'undefined' && typeof defVal === 'undefined') { throw new Error(compareTo + ' has no associated fieldInfo in switch') }

const caseDefault = typeof fields[compareTo] === 'undefined'
const fieldInfo = getFieldInfo(caseDefault ? defVal : fields[compareTo])
return tryDoc(() => this.sizeOf(value, fieldInfo, rootNode), caseDefault ? 'default' : compareTo)
if (fields[compareTo] === undefined) {
compareTo = 'default'
fields[compareTo] = defVal
if (defVal === undefined) {
throw new Error(`${compareTo} has no associated fieldInfo in switch`)
}
}
const fieldInfo = getFieldInfo(fields[compareTo])
return tryDoc(() => this.sizeOf(value, fieldInfo, rootNode), compareTo)
}

function readOption (buffer, offset, typeArgs, context) {
if (buffer.length < offset + 1) { throw new PartialReadError() }
const val = buffer.readUInt8(offset++)
if (val !== 0) {
const isPresent = buffer.readUInt8(offset++) !== 0
if (isPresent) {
const retval = this.read(buffer, offset, typeArgs, context)
retval.size++
return retval
} else { return { size: 1 } }
}
return { size: 1 }
}

function writeOption (value, buffer, offset, typeArgs, context) {
if (value != null) {
buffer.writeUInt8(1, offset++)
const isPresent = value != null
buffer.writeUInt8(isPresent & 1, offset++)
if (isPresent) {
offset = this.write(value, buffer, offset, typeArgs, context)
} else { buffer.writeUInt8(0, offset++) }
}
return offset
}

function sizeOfOption (value, typeArgs, context) {
return value == null ? 1 : this.sizeOf(value, typeArgs, context) + 1
return (value != null && this.sizeOf(value, typeArgs, context)) + 1
}

module.exports = {
'switch': [readSwitch, writeSwitch, sizeOfSwitch, require('../../ProtoDef/schemas/conditional.json')['switch']],
'option': [readOption, writeOption, sizeOfOption, require('../../ProtoDef/schemas/conditional.json')['option']]
}
48 changes: 24 additions & 24 deletions src/datatypes/numeric.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,22 +56,6 @@ function writeLU64 (value, buffer, offset) {
return offset + 8
}

function generateFunctions (bufferReader, bufferWriter, size, schema) {
const reader = (buffer, offset) => {
if (offset + size > buffer.length) { throw new PartialReadError() }
const value = buffer[bufferReader](offset)
return {
value: value,
size: size
}
}
const writer = (value, buffer, offset) => {
buffer[bufferWriter](value, offset)
return offset + size
}
return [reader, writer, size, schema]
}

const nums = {
'i8': ['readInt8', 'writeInt8', 1],
'u8': ['readUInt8', 'writeUInt8', 1],
Expand All @@ -91,13 +75,29 @@ const nums = {
'lf64': ['readDoubleLE', 'writeDoubleLE', 8]
}

const types = Object.keys(nums).reduce((types, num) => {
types[num] = generateFunctions(nums[num][0], nums[num][1], nums[num][2], require('../../ProtoDef/schemas/numeric.json')[num])
return types
}, {})
types['i64'] = [readI64, writeI64, 8, require('../../ProtoDef/schemas/numeric.json')['i64']]
types['li64'] = [readLI64, writeLI64, 8, require('../../ProtoDef/schemas/numeric.json')['li64']]
types['u64'] = [readU64, writeU64, 8, require('../../ProtoDef/schemas/numeric.json')['u64']]
types['lu64'] = [readLU64, writeLU64, 8, require('../../ProtoDef/schemas/numeric.json')['lu64']]
const types = {
i64: [readI64, writeI64, 8, require('../../ProtoDef/schemas/numeric.json')['i64']],
li64: [readLI64, writeLI64, 8, require('../../ProtoDef/schemas/numeric.json')['li64']],
u64: [readU64, writeU64, 8, require('../../ProtoDef/schemas/numeric.json')['u64']],
lu64: [readLU64, writeLU64, 8, require('../../ProtoDef/schemas/numeric.json')['lu64']]
}

for (const num in nums) {
const [ bufferReader, bufferWriter, size ] = nums[num]
types[num] = [
function readIntN (buffer, offset) {
if (offset + size > buffer.length) throw new PartialReadError()
return {
value: buffer[bufferReader](offset),
size
}
},
function writeIntN (value, buffer, offset) {
return buffer[bufferWriter](value, offset)
},
size,
require('../../ProtoDef/schemas/numeric.json')[num]
]
}

module.exports = types
124 changes: 67 additions & 57 deletions src/datatypes/structures.js
Original file line number Diff line number Diff line change
@@ -1,76 +1,31 @@
const { getField, getCount, sendCount, calcCount, tryDoc } = require('../utils')

module.exports = {
'array': [readArray, writeArray, sizeOfArray, require('../../ProtoDef/schemas/structures.json')['array']],
'count': [readCount, writeCount, sizeOfCount, require('../../ProtoDef/schemas/structures.json')['count']],
'container': [readContainer, writeContainer, sizeOfContainer, require('../../ProtoDef/schemas/structures.json')['container']]
}

function readArray (buffer, offset, typeArgs, rootNode) {
const results = {
value: [],
size: 0
}
let value
const value = []
let { count, size } = getCount.call(this, buffer, offset, typeArgs, rootNode)
offset += size
results.size += size
for (let i = 0; i < count; i++) {
({ size, value } = tryDoc(() => this.read(buffer, offset, typeArgs.type, rootNode), i))
results.size += size
offset += size
results.value.push(value)
const { size: s, value: v } = tryDoc(() => this.read(buffer, offset, typeArgs.type, rootNode), i)
size += s
offset += s
value.push(v)
}
return results
return { value, size }
}

function writeArray (value, buffer, offset, typeArgs, rootNode) {
offset = sendCount.call(this, value.length, buffer, offset, typeArgs, rootNode)
return value.reduce((offset, v, index) => tryDoc(() => this.write(v, buffer, offset, typeArgs.type, rootNode), index), offset)
for (let i = 0, l = value.length; i < l; i++) {
offset = tryDoc(() => this.write(value[i], buffer, offset, typeArgs.type, rootNode), i)
}
return offset
}

function sizeOfArray (value, typeArgs, rootNode) {
let size = calcCount.call(this, value.length, typeArgs, rootNode)
size = value.reduce((size, v, index) => tryDoc(() => size + this.sizeOf(v, typeArgs.type, rootNode), index), size)
return size
}

function readContainer (buffer, offset, typeArgs, context) {
const results = {
value: { '..': context },
size: 0
for (let i = 0, l = value.length; i < l; i++) {
size += tryDoc(() => this.sizeOf(value[i], typeArgs.type, rootNode), i)
}
typeArgs.forEach(({ type, name, anon }) => {
tryDoc(() => {
const readResults = this.read(buffer, offset, type, results.value)
results.size += readResults.size
offset += readResults.size
if (anon) {
if (readResults.value !== undefined) {
Object.keys(readResults.value).forEach(key => {
results.value[key] = readResults.value[key]
})
}
} else { results.value[name] = readResults.value }
}, name || 'unknown')
})
delete results.value['..']
return results
}

function writeContainer (value, buffer, offset, typeArgs, context) {
value['..'] = context
offset = typeArgs.reduce((offset, { type, name, anon }) =>
tryDoc(() => this.write(anon ? value : value[name], buffer, offset, type, value), name || 'unknown'), offset)
delete value['..']
return offset
}

function sizeOfContainer (value, typeArgs, context) {
value['..'] = context
const size = typeArgs.reduce((size, { type, name, anon }) =>
size + tryDoc(() => this.sizeOf(anon ? value : value[name], type, value), name || 'unknown'), 0)
delete value['..']
return size
}

Expand All @@ -88,3 +43,58 @@ function sizeOfCount (value, { countFor, type }, rootNode) {
// TODO : should I use value or getField().length ?
return this.sizeOf(getField(countFor, rootNode).length, type, rootNode)
}

function readContainer (buffer, offset, typeArgs, context) {
const value = {}
let size = 0
Object.defineProperty(value, '..', {
enumerable: false,
value: context
})
for (const { type, name, anon } of typeArgs) {
tryDoc(() => {
const { size: s, value: v } = this.read(buffer, offset, type, value)
size += s
offset += s
if (anon && v !== undefined) {
for (const k in v) {
value[k] = v[k]
}
}
value[name] = v
}, name || 'unknown')
}
context = undefined
return { value, size }
}

function writeContainer (value, buffer, offset, typeArgs, context) {
Object.defineProperty(value, '..', {
enumerable: false,
configurable: true,
value: context
})
for (const { type, name, anon } of typeArgs) {
offset = tryDoc(() => this.write(anon ? value : value[name], buffer, offset, type, value), name || 'unknown')
}
return offset
}

function sizeOfContainer (value, typeArgs, context) {
Object.defineProperty(value, '..', {
enumerable: false,
configurable: true,
value: context
})
let size = 0
for (const { type, name, anon } of typeArgs) {
size += tryDoc(() => this.sizeOf(anon ? value : value[name], type, value), name || 'unknown')
}
return size
}

module.exports = {
'array': [readArray, writeArray, sizeOfArray, require('../../ProtoDef/schemas/structures.json')['array']],
'count': [readCount, writeCount, sizeOfCount, require('../../ProtoDef/schemas/structures.json')['count']],
'container': [readContainer, writeContainer, sizeOfContainer, require('../../ProtoDef/schemas/structures.json')['container']]
}
Loading

0 comments on commit 1c5d3d1

Please sign in to comment.