Skip to content

Commit

Permalink
add addTypesToCompiler and addTypesToInterperter method for ProtoDef (#…
Browse files Browse the repository at this point in the history
…81)

* add addTypesToCompiler and addTypesToInterperter method for ProtoDef

* add nbtTagName alias to shortString for interperter

* add to types

* add optionalNbt type from node-minecraft-protocol

* fix naming

* add anonOptionalNbt and optionalNbt types from nmp protocol.json, remove broken tagType defaults

* fix optional compiler code

* run benchmark script on test to test addTypesToCompiler/addTypesToInterpreter

* Update zigzag.js

* Update zigzag.js lint

* update nbt.json
  • Loading branch information
extremeheat authored Dec 27, 2023
1 parent 3d02c78 commit e218297
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 35 deletions.
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,16 @@ Prismarine-NBT is a JavaScript parser and serializer for [NBT](http://wiki.vg/NB
#### as a async promise

```js
const fs = require('fs/promises')
const fs = require('fs')
const nbt = require('prismarine-nbt')

async function main(file) {
const buffer = await fs.readFile(file)
const buffer = fs.readFileSync(file)
const { parsed, type } = await nbt.parse(buffer)
console.log('JSON serialized', JSON.stringify(result, null, 2))
fs.createWriteStream('file.nbt').write(nbt.writeUncompressed(result, type)) // Write it back
console.log('JSON serialized', JSON.stringify(parsed, null, 2))
fs.createWriteStream('bigtest.nbt').write(nbt.writeUncompressed(parsed, type)) // Write it back
}

main('file.nbt')
main('bigtest.nbt')
```

#### as a callback
Expand Down Expand Up @@ -85,6 +84,12 @@ Provide the big-endian protodef instance used to parse and serialize nbt.

Provide the little-endian protodef instance used to parse and serialize little endian nbt.

### addTypesToCompiler (type, compiler)
Adds prismarine-nbt types to an ProtoDef compiler instance

### addTypesToInterpreter (type, interperter)
Adds prismarine-nbt types to a ProtoDef interpreter instance

### builder

Provides a way to build complex nbt structures simply:
Expand Down
27 changes: 13 additions & 14 deletions bench/compiled_nbt.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,19 @@ const { performance } = require('perf_hooks')
const assert = require('assert')
const { ProtoDefCompiler } = require('protodef').Compiler
const fs = require('fs')
const nbt = require('../nbt')

const mainType = 'nbt'

fs.readFile('../sample/bigtest.nbt', async (error, buffer) => {
if (error) {
throw error
}

const proto = new ProtoDef()
proto.addTypes(require('../compound'))
proto.addTypes(require('../nbt.json'))
function main (nbTests = 10000) {
const buffer = fs.readFileSync(__dirname + '/../sample/bigtest.nbt') // eslint-disable-line n/no-path-concat
const validate = true
const proto = new ProtoDef(validate)
nbt.addTypesToInterpreter('big', proto)

const compiler = new ProtoDefCompiler()
compiler.addTypes(require('../compiler-compound'))
compiler.addTypesToCompile(require('../nbt.json'))
const compiledProto = await compiler.compileProtoDef()
nbt.addTypesToCompiler('big', compiler)
const compiledProto = compiler.compileProtoDefSync()

const result = compiledProto.parsePacketBuffer(mainType, buffer).data
const result2 = proto.parsePacketBuffer(mainType, buffer).data
Expand All @@ -30,7 +27,6 @@ fs.readFile('../sample/bigtest.nbt', async (error, buffer) => {
assert.deepStrictEqual(result2, result3)
assert.strictEqual(buffer.length, buffer2.length)

const nbTests = 10000
console.log('Running ' + nbTests + ' tests')

let start, time, ps
Expand All @@ -54,7 +50,7 @@ fs.readFile('../sample/bigtest.nbt', async (error, buffer) => {
console.log('read / write parser: ' + time.toFixed(2) + ' ms (' + ps.toFixed(2) + 'k packet/s)')

// Closure optimized:
const optimizedProto = await compiler.compileProtoDef({ optimize: true })
const optimizedProto = compiler.compileProtoDefSync({ optimize: true })
start = performance.now()
for (let i = 0; i < nbTests; i++) {
const result = optimizedProto.parsePacketBuffer(mainType, buffer).data
Expand All @@ -63,4 +59,7 @@ fs.readFile('../sample/bigtest.nbt', async (error, buffer) => {
time = performance.now() - start
ps = nbTests / time
console.log('read / write compiled (+closure): ' + time.toFixed(2) + ' ms (' + ps.toFixed(2) + 'k packet/s)')
})
}

module.exports = main
if (!module.parent) main()
27 changes: 24 additions & 3 deletions nbt.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,37 @@ const beNbtJson = JSON.stringify(require('./nbt.json'))
const leNbtJson = beNbtJson.replace(/([iuf][0-7]+)/g, 'l$1')
const varintJson = JSON.stringify(require('./nbt-varint.json')).replace(/([if][0-7]+)/g, 'l$1')

function createProto (type) {
const compiler = new ProtoDefCompiler()
function addTypesToCompiler (type, compiler) {
compiler.addTypes(require('./compiler-compound'))
compiler.addTypes(require('./compiler-tagname'))
compiler.addTypes(require('./optional').compiler)
compiler.addTypes(require('./zigzag').compiler)
let proto = beNbtJson
if (type === 'littleVarint') {
compiler.addTypes(require('./compiler-zigzag'))
proto = varintJson
} else if (type === 'little') {
proto = leNbtJson
}
compiler.addTypesToCompile(JSON.parse(proto))
}

function addTypesToInterpreter (type, compiler) {
compiler.addTypes(require('./compound'))
compiler.addTypes(require('./optional').interpret)
compiler.addTypes(require('./zigzag').interpret)
let proto = beNbtJson
if (type === 'littleVarint') {
proto = varintJson
} else if (type === 'little') {
proto = leNbtJson
}
compiler.addTypes(JSON.parse(proto))
compiler.types.nbtTagName = compiler.types.shortString
}

function createProto (type) {
const compiler = new ProtoDefCompiler()
addTypesToCompiler(type, compiler)
return compiler.compileProtoDefSync()
}

Expand Down Expand Up @@ -228,6 +247,8 @@ const builder = {
}

module.exports = {
addTypesToCompiler,
addTypesToInterpreter,
writeUncompressed,
parseUncompressed,
simplify,
Expand Down
50 changes: 41 additions & 9 deletions nbt.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@
"i8": "native",
"switch": "native",
"compound": "native",
"nbtTagName": "native",
"i16": "native",
"u16": "native",
"i32": "native",
"i64": "native",
"f32": "native",
"f64": "native",
"pstring": "native",
"shortString": ["pstring",{
"countType":"u16"
}],
"shortString": [
"pstring",
{
"countType": "u16"
}
],
"byteArray": [
"array",
{
Expand All @@ -34,7 +38,12 @@
"array",
{
"countType": "i32",
"type": ["nbtSwitch",{"type":"type"}]
"type": [
"nbtSwitch",
{
"type": "type"
}
]
}
]
}
Expand All @@ -54,7 +63,8 @@
"type": "i64"
}
],
"nbtMapper":["mapper",
"nbtMapper": [
"mapper",
{
"type": "i8",
"mappings": {
Expand All @@ -74,7 +84,7 @@
}
}
],
"nbtSwitch":[
"nbtSwitch": [
"switch",
{
"compareTo": "$type",
Expand Down Expand Up @@ -108,7 +118,12 @@
},
{
"name": "value",
"type": ["nbtSwitch",{"type":"type"}]
"type": [
"nbtSwitch",
{
"type": "type"
}
]
}
]
],
Expand All @@ -121,8 +136,25 @@
},
{
"name": "value",
"type": ["nbtSwitch",{"type":"type"}]
"type": [
"nbtSwitch",
{
"type": "type"
}
]
}
]
],
"anonOptionalNbt": [
"optionalNbtType",
{
"tagType": "anonymousNbt"
}
],
"optionalNbt": [
"optionalNbtType",
{
"tagType": "nbt"
}
]
}
}
54 changes: 54 additions & 0 deletions optional.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
function readOptionalNbt (buffer, offset, { tagType }, rootNode) {
if (offset + 1 > buffer.length) { throw new Error('Read out of bounds') }
if (buffer.readInt8(offset) === 0) return { size: 1 }
return this.read(buffer, offset, tagType, rootNode)
}

function writeOptionalNbt (value, buffer, offset, { tagType }, rootNode) {
if (value === undefined) {
buffer.writeInt8(0, offset)
return offset + 1
}
return this.write(value, buffer, offset, tagType, rootNode)
}

function sizeOfOptionalNbt (value, { tagType }, rootNode) {
if (value === undefined) { return 1 }
return this.sizeOf(value, tagType, tagType, rootNode)
}

const compiler = {
Read: {
optionalNbtType: ['parametrizable', (compiler, { tagType }) => {
return compiler.wrapCode(`
if (offset + 1 > buffer.length) { throw new PartialReadError() }
if (buffer.readInt8(offset) === 0) return { size: 1 }
return ${compiler.callType(tagType)}
`)
}]
},
Write: {
optionalNbtType: ['parametrizable', (compiler, { tagType }) => {
return compiler.wrapCode(`
if (value === undefined) {
buffer.writeInt8(0, offset)
return offset + 1
}
return ${compiler.callType('value', tagType)}
`)
}]
},
SizeOf: {
optionalNbtType: ['parametrizable', (compiler, { tagType }) => {
return compiler.wrapCode(`
if (value === undefined) { return 1 }
return ${compiler.callType('value', tagType)}
`)
}]
}
}

module.exports = {
compiler,
interpret: { optionalNbtType: [readOptionalNbt, writeOptionalNbt, sizeOfOptionalNbt] }
}
7 changes: 7 additions & 0 deletions test/protodef.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* eslint-env mocha */
const bench = require('../bench/compiled_nbt')
describe('protodef', function () {
it('benchmark', () => {
bench(1000)
})
})
4 changes: 4 additions & 0 deletions typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ declare module 'prismarine-nbt'{
export const proto: any;
// Little Endian protocol
export const protoLE: any;
// Adds prismarine-nbt types to an ProtoDef compiler instance
export function addTypesToCompiler(type: NBTFormat, compiler)
// Adds prismarine-nbt types to a ProtoDef interpreter instance
export function addTypesToInterpreter(type: NBTFormat, protodef)

/** @deprecated */
export function writeUncompressed(value: NBT, little?: boolean): Buffer;
Expand Down
12 changes: 9 additions & 3 deletions compiler-zigzag.js → zigzag.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,13 @@ function writeSignedVarInt (value, buffer, offset) {
}

module.exports = {
Read: { zigzag64: ['native', readSignedVarLong], zigzag32: ['native', readSignedVarInt] },
Write: { zigzag64: ['native', writeSignedVarLong], zigzag32: ['native', writeSignedVarInt] },
SizeOf: { zigzag64: ['native', sizeOfVarLong], zigzag32: ['native', sizeOfVarInt] }
compiler: {
Read: { zigzag64: ['native', readSignedVarLong], zigzag32: ['native', readSignedVarInt] },
Write: { zigzag64: ['native', writeSignedVarLong], zigzag32: ['native', writeSignedVarInt] },
SizeOf: { zigzag64: ['native', sizeOfVarLong], zigzag32: ['native', sizeOfVarInt] }
},
interpret: {
zigzag64: [readSignedVarLong, writeSignedVarLong, sizeOfVarLong],
zigzag32: [readSignedVarInt, writeSignedVarInt, sizeOfVarInt]
}
}

0 comments on commit e218297

Please sign in to comment.