binary-data
Advanced tools
Comparing version 0.5.0 to 0.6.0
'use strict'; | ||
const { types, decode } = require('..'); | ||
const binary = require('binary'); // eslint-disable-line node/no-unpublished-require | ||
@@ -62,4 +63,41 @@ /* eslint-disable no-useless-concat */ | ||
function testBinary(i) { | ||
while (--i > 0) { | ||
parseBinary(); | ||
} | ||
} | ||
function parseBinary() { | ||
// Incomplete ClientHello packet | ||
const record = binary | ||
.parse(packet) | ||
.word8u('contentType') | ||
.word16bu('version') | ||
.word16bu('epoch') | ||
.buffer('sequenceNumber', 6) | ||
.word16bu('length') | ||
.buffer('body', 'length').vars; | ||
const handshake = binary | ||
.parse(record.body) | ||
.word8u('type') | ||
.buffer('length', 3) | ||
.word16bu('messageSeq') | ||
.buffer('fragment_offset', 3) | ||
.buffer('fragment_length', 3) | ||
.word16bu('clientVersion') | ||
.word32bu('gmtunixtime') | ||
.buffer('randomBytes', 28) | ||
.word8u('sessionId_length') | ||
.buffer('sessionId', 'sessionId_length') | ||
.word8u('cookie_length') | ||
.buffer('cookie', 'cookie_length').vars; | ||
return [record, handshake]; | ||
} | ||
console.time('binary data'); | ||
test(count); | ||
console.timeEnd('binary data'); | ||
console.time('binary'); | ||
testBinary(count); | ||
console.timeEnd('binary'); |
{ | ||
"name": "binary-data", | ||
"version": "0.5.0", | ||
"description": "Declarative encoder/decoder of various binary data.", | ||
"version": "0.6.0", | ||
"description": "Declarative binary data encoder / decoder.", | ||
"main": "src/index.js", | ||
@@ -25,3 +25,4 @@ "scripts": { | ||
"bin-protocol", | ||
"restructure" | ||
"restructure", | ||
"varstruct" | ||
], | ||
@@ -43,2 +44,4 @@ "author": "Dmitry Tsvettsikh <me@reklatsmasters.com>", | ||
"@nodertc/eslint-config": "0.2.1", | ||
"binary": "^0.3.0", | ||
"bl": "^2.1.2", | ||
"eslint": "^5.6.1", | ||
@@ -45,0 +48,0 @@ "jest": "^23.6.0", |
111
README.md
@@ -10,4 +10,8 @@ # binary-data | ||
Declarative encoder/decoder of various binary data. This module works almost like as [`binary`](https://www.npmjs.com/package/binary) or [`restructure`](https://www.npmjs.com/package/restructure) but provided modern and clean api. | ||
Declarative binary data encoder / decoder. This module works almost like as [`binary`](https://www.npmjs.com/package/binary) or [`restructure`](https://www.npmjs.com/package/restructure) but provided modern and clean api. It inspired by [abstract-encoding](https://github.com/mafintosh/abstract-encoding) interface. | ||
### Support | ||
[![Buy Me A Coffee](https://www.buymeacoffee.com/assets/img/custom_images/purple_img.png)](https://www.buymeacoffee.com/reklatsmasters) | ||
## Usage | ||
@@ -18,5 +22,5 @@ | ||
```js | ||
const { decode, createDecodeStream, types: { uint8, array, string } } = require('binary-data') | ||
const { decode, createDecode, types: { uint8, array, string } } = require('binary-data') | ||
// 1.1 define your own schema as plain object | ||
// 1. Define your own schema as plain object | ||
const protocol = { | ||
@@ -27,17 +31,16 @@ type: uint8, | ||
socket.on('message', (message) => { | ||
// 1.2 decode message | ||
const packet = decode(message, protocol) | ||
}) | ||
const message = Buffer.from([1, 2, 3, 4, 5, 6, 0]); | ||
// 2.1 also you may decode messages from streams | ||
const unicast = require('unicast') | ||
// Just decode message | ||
const packet = decode(message, protocol) | ||
const socket = unicast.createSocket({ /* options */ }) | ||
// 2 Also you may decode messages from streams | ||
const net = require('net'); | ||
// 2.2 create stream | ||
const input = createDecodeStream(protocol) | ||
const socket = net.createConnection({ port: 8124 }); | ||
const istream = createDecode(protocol); | ||
// 2.3 connect streams | ||
socket.pipe(input).on('data', packet => { /* do stuff */ }) | ||
socket.pipe(istream).on('data', packet => { | ||
console.log(packet.type, packet.value); | ||
}); | ||
``` | ||
@@ -48,30 +51,37 @@ | ||
```js | ||
const { encode, createEncodeStream, types: { uint8, buffer } } = require('binary-data') | ||
const { encode, createEncode, types: { uint8, string } } = require('binary-data') | ||
// 1. define schema | ||
const protocol = { | ||
type: uint8, | ||
data: buffer(uint8) | ||
value: string(uint8) | ||
} | ||
// 2. create data object (string, array - what you want) | ||
const hello = { | ||
type: 12, | ||
data: Buffer.from('my random data') | ||
value: 'my random data' | ||
} | ||
// 3. create encode stream | ||
const wstream = createEncodeStream(protocol) | ||
// Just encode message | ||
const ostream = encode(hello, protocol); | ||
const packet = ostream.slice(); | ||
// 4. connect streams | ||
wstream.pipe(socket) | ||
// Or you may encode messages into a stream | ||
const net = require('net'); | ||
// 5.1. encode all your data | ||
wstream.write(hello) | ||
const ostream = createEncode(protocol); | ||
const socket = net.createConnection({ port: 8124 }, () => { | ||
ostream.write(hello); | ||
}); | ||
// 5.2 or use another schema | ||
encode(anotherPacket, wstream, anotherSchema) | ||
ostream.pipe(socket); | ||
// 5.3 or convert to a buffer | ||
const buf = wstream.slice() | ||
// You may combine multiple schemes into one stream | ||
const ostream = createEncode(); | ||
encode(obj1, ostream, protocol1); | ||
encode(obj2, ostream, protocol2); | ||
encode(obj3, ostream, protocol3); | ||
const packet = ostream.slice(); | ||
``` | ||
@@ -81,9 +91,20 @@ | ||
## Perfomance | ||
Decoding DTLS ClientHello packet, *nodejs 10.14.1 / Ubuntu 16.04 x64* | ||
|name|time| | ||
|---|---| | ||
|binary data|637.900ms| | ||
|binary|2229.218ms| | ||
## API | ||
* [`decode(rstream: DecodeStream|Buffer, type: PrimitiveType|Object): any`](#decode) | ||
* [`encode(item: any, wstream: EncodeStream, type: PrimitiveType|Object): void`](#encode) | ||
* [`encodingLength(item: any, type: PrimitiveType|Object): Number`](#encoding-length) | ||
* [`createEncodeStream(): EncodeStream`](#create-encode-stream) | ||
* [`createDecodeStream([buf: Buffer]): DecodeStream`](#create-decode-stream) | ||
* [`encode(obj: any, [target: BinaryStream], type: Object): BinaryStream`](#encode) | ||
* [`decode(source: BinaryStream|Buffer, type: Object): any`](#decode) | ||
* [`encodingLength(item: any, type: Object): Number`](#encoding-length) | ||
* [`createEncodeStream([type: Object]): BinaryStream`](#create-encode-stream) | ||
* [`createDecodeStream([type: Object|Buffer]): BinaryStream`](#create-decode-stream) | ||
* [`createEncode([type: Object]): BinaryStream`](#create-encode-stream) | ||
* [`createDecode([type: Object|Buffer]): BinaryStream`](#create-decode-stream) | ||
* [Types](#types) | ||
@@ -101,3 +122,3 @@ * [`(u)int(8, 16, 24, 32, 40, 48)(be, le)`](#types-int) | ||
#### `decode(rstream: DecodeStream|Buffer, type: PrimitiveType|Object): any` | ||
#### `decode(source: BinaryStream|Buffer, type: Object): any` | ||
@@ -108,9 +129,9 @@ Reads any data from stream `rstream` using data type `type`. See examples above. | ||
#### `encode(item: any, wstream: EncodeStream, type: PrimitiveType|Object): void` | ||
#### `encode(obj: any, [target: BinaryStream], type: Object): BinaryStream` | ||
Writes any data `item` to stream `wstream` using data type `type`. See examples above. | ||
Writes any data `obj` to stream `target` using data type `type`. See examples above. | ||
<a name='encoding-length' /> | ||
#### `encodingLength(item: any, type: PrimitiveType|Object): Number` | ||
#### `encodingLength(item: any, type: Object): Number` | ||
@@ -121,11 +142,13 @@ Return the amount of bytes needed to encode `item` using `type`. | ||
#### `createEncodeStream(): EncodeStream` | ||
#### `createEncodeStream([type: Object]): BinaryStream` | ||
#### `createEncode([type: Object]): BinaryStream` | ||
Create instance of EncodeStream. | ||
Create instance of BinaryStream. | ||
<a name='create-decode-stream' /> | ||
#### `createDecodeStream([buf: Buffer]): DecodeStream` | ||
#### `createDecodeStream([type: Object|Buffer]): BinaryStream` | ||
#### `createDecode([type: Object|Buffer]): BinaryStream` | ||
Create instance of DecodeStream using buffer `buf`. | ||
Create instance of BinaryStream. | ||
@@ -217,5 +240,5 @@ <a name='types' /> | ||
#### `buffer(length)` | ||
#### `buffer(length: Object|null|number)` | ||
Low-level buffer type. Argument `length` can be _number_, number _type_ for size-prefixed data or _function_. | ||
Low-level buffer type. Argument `length` can be _number_, number _type_ for size-prefixed data, _function_ or _null_. | ||
@@ -222,0 +245,0 @@ ```js |
'use strict'; | ||
const EncodeStream = require('streams/encode'); | ||
const DecodeStream = require('streams/decode'); | ||
const BinaryStream = require('lib/binary-stream'); | ||
const array = require('types/array'); | ||
@@ -33,2 +32,4 @@ const buffer = require('types/buffer'); | ||
const kschema = Symbol('schema'); | ||
/** | ||
@@ -40,6 +41,10 @@ * Create transform stream to encode objects into Buffer. | ||
function createEncodeStream(schema) { | ||
return new EncodeStream({ | ||
schema, | ||
const stream = new BinaryStream({ | ||
readableObjectMode: false, | ||
writableObjectMode: true, | ||
transform: transformEncode, | ||
}); | ||
stream[kschema] = schema; | ||
return stream; | ||
} | ||
@@ -60,7 +65,10 @@ | ||
const stream = new DecodeStream({ | ||
schema, | ||
const stream = new BinaryStream({ | ||
transform: transformDecode, | ||
readableObjectMode: true, | ||
writableObjectMode: false, | ||
}); | ||
stream[kschema] = schema; | ||
if (isBuffer) { | ||
@@ -81,3 +89,3 @@ stream.append(bufOrSchema); | ||
try { | ||
encode(chunk, this, this.schema); | ||
encode(chunk, this[kschema], this); | ||
@@ -105,3 +113,3 @@ const buf = this.slice(); | ||
const transaction = new Transaction(this); | ||
const data = decode(transaction, this.schema); | ||
const data = decode(transaction, this[kschema]); | ||
@@ -130,2 +138,6 @@ transaction.commit(); | ||
/* aliases */ | ||
createEncode: createEncodeStream, | ||
createDecode: createDecodeStream, | ||
/* Data types */ | ||
@@ -135,5 +147,4 @@ types, | ||
/* Re-export utils */ | ||
EncodeStream, | ||
DecodeStream, | ||
BinaryStream, | ||
NotEnoughDataError, | ||
}; |
@@ -28,3 +28,3 @@ 'use strict'; | ||
* Adds an additional buffer or BufferList to the internal list. | ||
* @param {Buffer|Buffer[]} buf | ||
* @param {Buffer|Buffer[]|BufferList|BufferList[]} buf | ||
*/ | ||
@@ -45,2 +45,23 @@ append(buf) { | ||
} | ||
} else if (buf instanceof BufferList) { | ||
if (this.offset > 0) { | ||
const head = this.queue.shift(); | ||
this.queue.unshift(head.slice(this.offset)); | ||
this.offset = 0; | ||
} | ||
if (buf.offset > 0) { | ||
const head = buf.queue.shift(); | ||
buf.queue.unshift(head.slice(buf.offset)); | ||
buf.offset = 0; | ||
} | ||
let leaf = buf.queue.head; | ||
while (leaf) { | ||
this.queue.push(leaf.buffer); | ||
leaf = leaf.next; | ||
} | ||
} | ||
@@ -122,11 +143,13 @@ } | ||
const bufs = new Array(subset.count); | ||
let leaf = subset.head; | ||
const target = Buffer.allocUnsafe(subset.length); | ||
let offset = 0; | ||
for (let i = 0; i < bufs.length; i += 1) { | ||
bufs[i] = leaf.buffer; | ||
for (let i = 0; i < subset.count; i += 1) { | ||
target.set(leaf.buffer, offset); | ||
offset += leaf.buffer.length; | ||
leaf = leaf.next; | ||
} | ||
return Buffer.concat(bufs, subset.length); | ||
return target; | ||
/* eslint-enable no-param-reassign */ | ||
@@ -167,2 +190,76 @@ } | ||
} | ||
/** | ||
* Returns the first (least) index of an element | ||
* within the list equal to the specified value, | ||
* or -1 if none is found. | ||
* @param {number} byte | ||
* @param {number} [offset] | ||
* @returns {number} | ||
*/ | ||
indexOf(byte, offset = 0) { | ||
/* eslint-disable no-param-reassign */ | ||
if (!Number.isInteger(byte)) { | ||
throw new TypeError('Invalid argument 1'); | ||
} | ||
if (byte < 0 || byte > 0xff) { | ||
throw new Error('Invalid argument 1'); | ||
} | ||
if (!Number.isInteger(offset)) { | ||
offset = 0; | ||
} | ||
while (offset >= this.length) { | ||
offset -= this.length; | ||
} | ||
while (offset < 0) { | ||
offset += this.length; | ||
} | ||
let leaf = this.queue.head; | ||
let bias = 0; | ||
const next = () => { | ||
bias += leaf.buffer.length; | ||
leaf = leaf.next; | ||
}; | ||
while (leaf) { | ||
let byteOffset = 0; | ||
if (leaf === this.queue.head) { | ||
byteOffset += this.offset; | ||
} | ||
// `offset` is point to next chunk | ||
if (offset >= leaf.buffer.length - byteOffset) { | ||
offset -= leaf.buffer.length - byteOffset; | ||
next(); | ||
continue; // eslint-disable-line no-continue | ||
} | ||
// `offset` is point to current chunk | ||
if (offset < leaf.buffer.length) { | ||
byteOffset += offset; | ||
} | ||
const index = leaf.buffer.indexOf(byte, byteOffset); | ||
if (index > -1) { | ||
return index + bias - this.offset; | ||
} | ||
next(); | ||
if (byteOffset > this.offset) { | ||
offset = 0; | ||
} | ||
} | ||
return -1; | ||
/* eslint-enable no-param-reassign */ | ||
} | ||
} | ||
@@ -169,0 +266,0 @@ |
'use strict'; | ||
const { isType, isUserType, isDecodeType } = require('lib/util'); | ||
const DecodeStream = require('streams/decode'); | ||
const BinaryStream = require('lib/binary-stream'); | ||
const symbols = require('internal/symbols'); | ||
@@ -15,3 +15,3 @@ const Metadata = require('internal/meta'); | ||
* Decode any data from provided stream using schema. | ||
* @param {DecodeStream} rstream Read stream to decode. | ||
* @param {BinaryStream} rstream Read stream to decode. | ||
* @param {Object} typeOrSchema Builtin data type or schema. | ||
@@ -24,3 +24,4 @@ * @returns {*} | ||
if (Buffer.isBuffer(rstream)) { | ||
decodeStream = new DecodeStream(rstream); | ||
decodeStream = new BinaryStream(); | ||
decodeStream.append(rstream); | ||
} | ||
@@ -39,3 +40,3 @@ | ||
* @private | ||
* @param {DecodeStream|Buffer} rstream | ||
* @param {BinaryStream|Buffer} rstream | ||
* @param {Object} typeOrSchema | ||
@@ -57,3 +58,3 @@ * @param {Metadata} meta | ||
* @private | ||
* @param {DecodeStream} rstream | ||
* @param {BinaryStream} rstream | ||
* @param {Object} schema | ||
@@ -60,0 +61,0 @@ * @param {Metadata} meta |
@@ -6,2 +6,3 @@ 'use strict'; | ||
const Metadata = require('internal/meta'); | ||
const BinaryStream = require('lib/binary-stream'); | ||
@@ -14,13 +15,27 @@ module.exports = { | ||
/** | ||
* @param {any} object | ||
* @param {EncodeStream} wstream | ||
* @param {any} typeOrSchema | ||
* @param {any} obj | ||
* @param {any} type | ||
* @param {BinaryStream} [target] | ||
* @returns {BinaryStream} | ||
*/ | ||
function encode(object, wstream, typeOrSchema) { | ||
function encode(obj, type, target) { | ||
const meta = new Metadata(); | ||
encodeCommon(object, wstream, typeOrSchema, meta); | ||
// Check for legacy interface. | ||
if (type instanceof BinaryStream) { | ||
const tmp = target; | ||
target = type; // eslint-disable-line no-param-reassign | ||
type = tmp; // eslint-disable-line no-param-reassign | ||
} | ||
if (!(target instanceof BinaryStream)) { | ||
target = new BinaryStream(); // eslint-disable-line no-param-reassign | ||
} | ||
encodeCommon(obj, target, type, meta); | ||
encode.bytes = meta.bytes; | ||
Metadata.clean(meta); | ||
return target; | ||
} | ||
@@ -27,0 +42,0 @@ |
@@ -82,2 +82,11 @@ 'use strict'; | ||
} | ||
/** | ||
* @param {number} byte | ||
* @param {number} [offset] | ||
* @returns {number} | ||
*/ | ||
indexOf(byte, offset = 0) { | ||
return this.stream.indexOf(byte, this.index + offset) - this.index; | ||
} | ||
} | ||
@@ -84,0 +93,0 @@ |
'use strict'; | ||
const { isType, isFunction } = require('lib/util'); | ||
const NotEnoughDataError = require('lib/not-enough-data-error'); | ||
const BinaryStream = require('lib/binary-stream'); | ||
@@ -16,4 +18,5 @@ module.exports = buffer; | ||
const isfunc = isFunction(length); | ||
const isNull = length === null; | ||
if (!isnum && !istype && !isfunc) { | ||
if (!isnum && !istype && !isfunc && !isNull) { | ||
throw new TypeError('Unknown type of argument #1.'); | ||
@@ -56,4 +59,9 @@ } | ||
wstream.writeBuffer(buf); | ||
wstream.writeBuffer(Buffer.isBuffer(buf) ? buf : buf.buffer); | ||
encode.bytes += buf.length; | ||
if (isNull) { | ||
wstream.writeUInt8(0); | ||
encode.bytes += 1; | ||
} | ||
} | ||
@@ -83,2 +91,8 @@ | ||
checkLengthType(size); | ||
} else if (isNull) { | ||
size = rstream.indexOf(0); | ||
if (size === -1) { | ||
throw new NotEnoughDataError(rstream.length + 1, rstream.length); | ||
} | ||
} | ||
@@ -89,2 +103,7 @@ | ||
if (isNull) { | ||
decode.bytes += 1; | ||
rstream.consume(1); | ||
} | ||
return buf; | ||
@@ -106,4 +125,6 @@ } | ||
if (istype) { | ||
size += length.encodingLength(buf.length); | ||
if (isNull) { | ||
size = 1; | ||
} else if (istype) { | ||
size = length.encodingLength(buf.length); | ||
} | ||
@@ -121,4 +142,4 @@ | ||
function checkBuffer(buf) { | ||
if (!Buffer.isBuffer(buf)) { | ||
throw new TypeError('Argument 1 should be a Buffer.'); | ||
if (!Buffer.isBuffer(buf) && !(buf instanceof BinaryStream)) { | ||
throw new TypeError('Argument 1 should be a Buffer or a BinaryStream.'); | ||
} | ||
@@ -125,0 +146,0 @@ } |
@@ -93,10 +93,6 @@ 'use strict'; | ||
return function decode(rstream) { | ||
let bytes = 0; | ||
const bytes = rstream.indexOf(0); | ||
while (rstream.get(bytes) !== 0) { | ||
bytes += 1; | ||
if (bytes >= rstream.length) { | ||
throw new NotEnoughDataError(bytes, rstream.length); | ||
} | ||
if (bytes === -1) { | ||
throw new NotEnoughDataError(rstream.length + 1, rstream.length); | ||
} | ||
@@ -103,0 +99,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
1246693
110
2377
296
0
6