BinaryEncoder writes directly to ArrayBuffer, improves memory consumption

This commit is contained in:
Kevin Jahns 2018-05-23 16:06:38 +02:00
parent 4ef36ab81c
commit c122bdc750
3 changed files with 73 additions and 19 deletions

View File

@ -14,7 +14,8 @@
"dist": "rollup -c rollup.browser.js; rollup -c rollup.node.js", "dist": "rollup -c rollup.browser.js; rollup -c rollup.node.js",
"watch": "concurrently 'rollup -wc rollup.browser.js' 'rollup -wc rollup.node.js'", "watch": "concurrently 'rollup -wc rollup.browser.js' 'rollup -wc rollup.node.js'",
"postversion": "npm run dist", "postversion": "npm run dist",
"postpublish": "tag-dist-files --overwrite-existing-tag" "postpublish": "tag-dist-files --overwrite-existing-tag",
"demos": "concurrently 'node --experimental-modules src/Connectors/WebsocketsConnector/server.mjs' 'http-server'"
}, },
"files": [ "files": [
"y.*", "y.*",

View File

@ -10,30 +10,78 @@ export default class BinaryEncoder {
constructor () { constructor () {
// TODO: implement chained Uint8Array buffers instead of Array buffer // TODO: implement chained Uint8Array buffers instead of Array buffer
// TODO: Rewrite all methods as functions! // TODO: Rewrite all methods as functions!
this.data = [] this._currentPos = 0
this._currentBuffer = new Uint8Array(1000)
this._data = []
} }
/** /**
* The current length of the encoded data. * The current length of the encoded data.
*/ */
get length () { get length () {
return this.data.length let len = 0
for (let i = 0; i < this._data.length; i++) {
len += this._data[i].length
}
len += this._currentPos
return len
} }
/** /**
* The current write pointer (the same as {@link length}). * The current write pointer (the same as {@link length}).
*/ */
get pos () { get pos () {
return this.data.length return this.length
} }
/** /**
* Create an ArrayBuffer. * Transform to ArrayBuffer.
* *
* @return {Uint8Array} A Uint8Array that represents the written data. * @return {ArrayBuffer} The created ArrayBuffer.
*/ */
createBuffer () { createBuffer () {
return Uint8Array.from(this.data).buffer const len = this.length
const uint8array = new Uint8Array(len)
let curPos = 0
for (let i = 0; i < this._data.length; i++) {
let d = this._data[i]
uint8array.set(d, curPos)
curPos += d.length
}
uint8array.set(new Uint8Array(this._currentBuffer.buffer, 0, this._currentPos), curPos)
return uint8array.buffer
}
/**
* Write one byte to the encoder.
*
* @param {number} num The byte that is to be encoded.
*/
write (num) {
if (this._currentPos === this._currentBuffer.length) {
this._data.push(this._currentBuffer)
this._currentBuffer = new Uint8Array(this._currentBuffer.length * 2)
this._currentPos = 0
}
this._currentBuffer[this._currentPos++] = num
}
set (pos, num) {
let buffer = null
// iterate all buffers and adjust position
for (let i = 0; i < this._data.length && buffer === null; i++) {
const b = this._data[i]
if (pos < b.length) {
buffer = b // found buffer
} else {
pos -= b.length
}
}
if (buffer === null) {
// use current buffer
buffer = this._currentBuffer
}
buffer[pos] = num
} }
/** /**
@ -42,7 +90,7 @@ export default class BinaryEncoder {
* @param {number} num The number that is to be encoded. * @param {number} num The number that is to be encoded.
*/ */
writeUint8 (num) { writeUint8 (num) {
this.data.push(num & bits8) this.write(num & bits8)
} }
/** /**
@ -52,7 +100,7 @@ export default class BinaryEncoder {
* @param {number} num The number that is to be encoded. * @param {number} num The number that is to be encoded.
*/ */
setUint8 (pos, num) { setUint8 (pos, num) {
this.data[pos] = num & bits8 this.set(pos, num & bits8)
} }
/** /**
@ -61,7 +109,8 @@ export default class BinaryEncoder {
* @param {number} num The number that is to be encoded. * @param {number} num The number that is to be encoded.
*/ */
writeUint16 (num) { writeUint16 (num) {
this.data.push(num & bits8, (num >>> 8) & bits8) this.write(num & bits8)
this.write((num >>> 8) & bits8)
} }
/** /**
* Write two bytes as an unsigned integer at a specific location. * Write two bytes as an unsigned integer at a specific location.
@ -70,8 +119,8 @@ export default class BinaryEncoder {
* @param {number} num The number that is to be encoded. * @param {number} num The number that is to be encoded.
*/ */
setUint16 (pos, num) { setUint16 (pos, num) {
this.data[pos] = num & bits8 this.set(pos, num & bits8)
this.data[pos + 1] = (num >>> 8) & bits8 this.set(pos + 1, (num >>> 8) & bits8)
} }
/** /**
@ -81,7 +130,7 @@ export default class BinaryEncoder {
*/ */
writeUint32 (num) { writeUint32 (num) {
for (let i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {
this.data.push(num & bits8) this.write(num & bits8)
num >>>= 8 num >>>= 8
} }
} }
@ -94,7 +143,7 @@ export default class BinaryEncoder {
*/ */
setUint32 (pos, num) { setUint32 (pos, num) {
for (let i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {
this.data[pos + i] = num & bits8 this.set(pos + i, num & bits8)
num >>>= 8 num >>>= 8
} }
} }
@ -106,10 +155,10 @@ export default class BinaryEncoder {
*/ */
writeVarUint (num) { writeVarUint (num) {
while (num >= 0b10000000) { while (num >= 0b10000000) {
this.data.push(0b10000000 | (bits7 & num)) this.write(0b10000000 | (bits7 & num))
num >>>= 7 num >>>= 7
} }
this.data.push(bits7 & num) this.write(bits7 & num)
} }
/** /**
@ -123,7 +172,7 @@ export default class BinaryEncoder {
let len = bytes.length let len = bytes.length
this.writeVarUint(len) this.writeVarUint(len)
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
this.data.push(bytes[i]) this.write(bytes[i])
} }
} }
@ -133,7 +182,11 @@ export default class BinaryEncoder {
* @param encoder The BinaryEncoder to be written. * @param encoder The BinaryEncoder to be written.
*/ */
writeBinaryEncoder (encoder) { writeBinaryEncoder (encoder) {
this.data = this.data.concat(encoder.data) const prevBufferLen = this._currentBuffer.length
this._data.push(new Uint8Array(this._currentBuffer.buffer, 0, this._currentPos))
this._data.push(new Uint8Array(encoder.createBuffer()))
this._currentBuffer = new Uint8Array(prevBufferLen)
this._currentPos = 0
} }
/** /**

View File

@ -9,7 +9,7 @@ function testEncoding (t, write, read, val) {
write(encoder, val) write(encoder, val)
let reader = new BinaryDecoder(encoder.createBuffer()) let reader = new BinaryDecoder(encoder.createBuffer())
let result = read(reader) let result = read(reader)
t.log(`string encode: ${JSON.stringify(val).length} bytes / binary encode: ${encoder.data.length} bytes`) t.log(`string encode: ${JSON.stringify(val).length} bytes / binary encode: ${encoder.length} bytes`)
t.compare(val, result, 'Compare results') t.compare(val, result, 'Compare results')
} }