descargar pdf y fotos
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2015-present Devon Govett
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+306
@@ -0,0 +1,306 @@
|
||||
# Restructure
|
||||
|
||||
[](https://travis-ci.org/devongovett/restructure)
|
||||
[](https://coveralls.io/r/devongovett/restructure?branch=master)
|
||||
|
||||
Restructure allows you to declaratively encode and decode binary data.
|
||||
It supports a wide variety of types to enable you to express a multitude
|
||||
of binary formats without writing any parsing code.
|
||||
|
||||
Some of the supported features are C-like structures, versioned structures,
|
||||
pointers, arrays of any type, strings of a large number of encodings, enums,
|
||||
bitfields, and more. See the documentation below for more details.
|
||||
|
||||
## Example
|
||||
|
||||
This is just a small example of what Restructure can do. Check out the API documentation
|
||||
below for more information.
|
||||
|
||||
```javascript
|
||||
var r = require('restructure');
|
||||
|
||||
var Person = new r.Struct({
|
||||
name: new r.String(r.uint8, 'utf8'),
|
||||
age: r.uint8
|
||||
});
|
||||
|
||||
// decode a person from a buffer
|
||||
var stream = new r.DecodeStream(buffer);
|
||||
Person.decode(stream); // returns an object with the fields defined above
|
||||
|
||||
// encode a person from an object
|
||||
// pipe the stream to a destination, such as a file
|
||||
var stream = new r.EncodeStream();
|
||||
stream.pipe(fs.createWriteStream('out.bin'));
|
||||
|
||||
Person.encode(stream, {
|
||||
name: 'Devon',
|
||||
age: 21
|
||||
});
|
||||
|
||||
stream.end();
|
||||
```
|
||||
|
||||
|
||||
## API
|
||||
|
||||
All of the following types support three standard methods:
|
||||
|
||||
* `decode(stream)` - decodes an instance of the type from the given DecodeStream
|
||||
* `size(value)` - returns the amount of space the value would take if encoded
|
||||
* `encode(stream, value)` - encodes the given value into the given EncodeStream
|
||||
|
||||
Restructure supports a wide variety of types, but if you need to write your own for
|
||||
some custom use that cannot be represented by them, you can do so by just implementing
|
||||
the above methods. Then you can use your type just as you would any other type, in structures
|
||||
and whatnot.
|
||||
|
||||
### Number Types
|
||||
|
||||
The following built-in number types are available:
|
||||
|
||||
```javascript
|
||||
uint8, uint16, uint24, uint32, int8, int16, int24, int32, float, double, fixed16, fixed32
|
||||
```
|
||||
|
||||
Numbers are big-endian (network order) by default, but little-endian is supported, too:
|
||||
|
||||
```javascript
|
||||
uint16le, uint24le, uint32le, int16le, int24le, int32le, floatle, doublele, fixed16le, fixed32le
|
||||
```
|
||||
|
||||
To avoid ambiguity, big-endian may be used explicitly:
|
||||
|
||||
```javascript
|
||||
uint16be, uint24be, uint32be, int16be, int24be, int32be, floatbe, doublebe, fixed16be, fixed32be
|
||||
```
|
||||
|
||||
### Boolean
|
||||
|
||||
Booleans are encoded as `0` or `1` using one of the above number types.
|
||||
|
||||
```javascript
|
||||
var bool = new r.Boolean(r.uint32);
|
||||
```
|
||||
|
||||
### Reserved
|
||||
|
||||
The `Reserved` type simply skips data in a structure, where there are reserved fields.
|
||||
Encoding produces zeros.
|
||||
|
||||
```javascript
|
||||
// 10 reserved uint8s (default is 1)
|
||||
var reserved = new r.Reserved(r.uint8, 10);
|
||||
```
|
||||
|
||||
### Optional
|
||||
|
||||
The `Optional` type only encodes or decodes when given condition is truthy.
|
||||
|
||||
```javascript
|
||||
// includes field
|
||||
var optional = new r.Optional(r.uint8, true);
|
||||
|
||||
// excludes field
|
||||
var optional = new r.Optional(r.uint8, false);
|
||||
|
||||
// determine whether field is to be included at runtime with a function
|
||||
var optional = new r.Optional(r.uint8, function() {
|
||||
return this.flags & 0x50;
|
||||
});
|
||||
```
|
||||
|
||||
### Enum
|
||||
|
||||
The `Enum` type maps a number to the value at that index in an array.
|
||||
|
||||
```javascript
|
||||
var color = new r.Enum(r.uint8, ['red', 'orange', 'yellow', 'green', 'blue', 'purple']);
|
||||
```
|
||||
|
||||
### Bitfield
|
||||
|
||||
The `Bitfield` type maps a number to an object with boolean keys mapping to each bit in that number,
|
||||
as defined in an array.
|
||||
|
||||
```javascript
|
||||
var bitfield = new r.Bitfield(r.uint8, ['Jack', 'Kack', 'Lack', 'Mack', 'Nack', 'Oack', 'Pack', 'Quack']);
|
||||
bitfield.decode(stream);
|
||||
|
||||
var result = {
|
||||
Jack: true,
|
||||
Kack: false,
|
||||
Lack: false,
|
||||
Mack: true,
|
||||
Nack: true,
|
||||
Oack: false,
|
||||
Pack: true,
|
||||
Quack: true
|
||||
};
|
||||
|
||||
bitfield.encode(stream, result);
|
||||
```
|
||||
|
||||
### Buffer
|
||||
|
||||
Extracts a slice of the buffer to a Node `Buffer`. The length can be a constant, or taken from
|
||||
a previous field in the parent structure.
|
||||
|
||||
```javascript
|
||||
// fixed length
|
||||
var buf = new r.Buffer(2);
|
||||
|
||||
// length from parent structure
|
||||
var struct = new r.Struct({
|
||||
bufLen: r.uint8,
|
||||
buf: new r.Buffer('bufLen')
|
||||
});
|
||||
```
|
||||
|
||||
### String
|
||||
|
||||
A `String` maps a JavaScript string to and from binary encodings. The length can be a constant, taken
|
||||
from a previous field in the parent structure, or encoded using a number type immediately before the string.
|
||||
|
||||
Supported encodings include `'ascii'`, `'utf8'`, `'ucs2'`, `'utf16le'`, `'utf16be'`, and if you also install
|
||||
[iconv-lite](https://github.com/ashtuchkin/iconv-lite), many other legacy codecs.
|
||||
|
||||
```javascript
|
||||
// fixed length, ascii encoding by default
|
||||
var str = new r.String(2);
|
||||
|
||||
// length encoded as number before the string, utf8 encoding
|
||||
var str = new r.String(r.uint8, 'utf8');
|
||||
|
||||
// length from parent structure
|
||||
var struct = new r.Struct({
|
||||
len: r.uint8,
|
||||
str: new r.String('len', 'utf16be')
|
||||
});
|
||||
|
||||
// null-terminated string (also known as C string)
|
||||
var str = new r.String(null, 'utf8')
|
||||
```
|
||||
|
||||
### Array
|
||||
|
||||
An `Array` maps to and from a JavaScript array containing instances of a sub-type. The length can be a constant,
|
||||
taken from a previous field in the parent structure, encoded using a number type immediately
|
||||
before the string, or computed by a function.
|
||||
|
||||
```javascript
|
||||
// fixed length, containing numbers
|
||||
var arr = new r.Array(r.uint16, 2);
|
||||
|
||||
// length encoded as number before the array containing strings
|
||||
var arr = new r.Array(new r.String(10), r.uint8);
|
||||
|
||||
// length computed by a function
|
||||
var arr = new r.Array(r.uint8, function() { return 5 });
|
||||
|
||||
// length from parent structure
|
||||
var struct = new r.Struct({
|
||||
len: r.uint8,
|
||||
arr: new r.Array(r.uint8, 'len')
|
||||
});
|
||||
|
||||
// treat as amount of bytes instead (may be used in all the above scenarios)
|
||||
var arr = new r.Array(r.uint16, 6, 'bytes');
|
||||
```
|
||||
|
||||
### LazyArray
|
||||
|
||||
The `LazyArray` type extends from the `Array` type, and is useful for large arrays that you do not need to access sequentially.
|
||||
It avoids decoding the entire array upfront, and instead only decodes and caches individual items as needed. It only works when
|
||||
the elements inside the array have a fixed size.
|
||||
|
||||
Instead of returning a JavaScript array, the `LazyArray` type returns a custom object that can be used to access the elements.
|
||||
|
||||
```javascript
|
||||
var arr = new r.LazyArray(r.uint16, 2048);
|
||||
var res = arr.decode(stream);
|
||||
|
||||
// get a single element
|
||||
var el = res.get(2);
|
||||
|
||||
// convert to a normal array (decode all elements)
|
||||
var array = res.toArray();
|
||||
```
|
||||
|
||||
### Struct
|
||||
|
||||
A `Struct` maps to and from JavaScript objects, containing keys of various previously discussed types. Sub structures,
|
||||
arrays of structures, and pointers to other types (discussed below) are supported.
|
||||
|
||||
```javascript
|
||||
var Person = new r.Struct({
|
||||
name: new r.String(r.uint8, 'utf8'),
|
||||
age: r.uint8
|
||||
});
|
||||
```
|
||||
|
||||
### VersionedStruct
|
||||
|
||||
A `VersionedStruct` is a `Struct` that has multiple versions. The version is typically encoded at
|
||||
the beginning of the structure, or as a field in a parent structure. There is an optional `header`
|
||||
common to all versions, and separate fields listed for each version number.
|
||||
|
||||
```javascript
|
||||
// the version is read as a uint8 in this example
|
||||
// you could also get the version from a key on the parent struct
|
||||
var Person = new r.VersionedStruct(r.uint8, {
|
||||
// optional header common to all versions
|
||||
header: {
|
||||
name: new r.String(r.uint8, 'utf8')
|
||||
},
|
||||
0: {
|
||||
age: r.uint8
|
||||
},
|
||||
1: {
|
||||
hairColor: r.Enum(r.uint8, ['black', 'brown', 'blonde'])
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Pointer
|
||||
|
||||
Pointers map an address or offset encoded as a number, to a value encoded elsewhere in the buffer.
|
||||
There are a few options you can use: `type`, `relativeTo`, `allowNull`, and `nullValue`.
|
||||
The `type` option has these possible values:
|
||||
|
||||
* `local` (default) - the encoded offset is relative to the start of the containing structure
|
||||
* `immediate` - the encoded offset is relative to the position of the pointer itself
|
||||
* `parent` - the encoded offset is relative to the parent structure of the immediate container
|
||||
* `global` - the encoded offset is global to the start of the file
|
||||
|
||||
The `relativeTo` option accepts a function callback that should return the field on the containing structure which the encoded offset is relative to. The callback is called with the context as parameter.
|
||||
By default, pointers are relative to the start of the containing structure (`local`).
|
||||
|
||||
The `allowNull` option lets you specify whether zero offsets are allowed or should produce `null`. This is
|
||||
set to `true` by default. The `nullValue` option is related, and lets you override the encoded value that
|
||||
represents `null`. By default, the `nullValue` is zero.
|
||||
|
||||
The `lazy` option allows lazy decoding of the pointer's value by defining a getter on the parent object.
|
||||
This only works when the pointer is contained within a Struct, but can be used to speed up decoding
|
||||
quite a bit when not all of the data is needed right away.
|
||||
|
||||
```javascript
|
||||
var Address = new r.Struct({
|
||||
street: new r.String(r.uint8),
|
||||
zip: new r.String(5)
|
||||
});
|
||||
|
||||
var Person = new r.Struct({
|
||||
name: new r.String(r.uint8, 'utf8'),
|
||||
age: r.uint8,
|
||||
ptrStart: r.uint8,
|
||||
address: new r.Pointer(r.uint8, Address)
|
||||
});
|
||||
```
|
||||
|
||||
If the type of a pointer is set to 'void', it is not decoded and the computed address in the buffer
|
||||
is simply returned. To encode a void pointer, create a `new r.VoidPointer(type, value)`.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
exports.EncodeStream = require('./src/EncodeStream');
|
||||
exports.DecodeStream = require('./src/DecodeStream');
|
||||
exports.Array = require('./src/Array');
|
||||
exports.LazyArray = require('./src/LazyArray');
|
||||
exports.Bitfield = require('./src/Bitfield');
|
||||
exports.Boolean = require('./src/Boolean');
|
||||
exports.Buffer = require('./src/Buffer');
|
||||
exports.Enum = require('./src/Enum');
|
||||
exports.Optional = require('./src/Optional');
|
||||
exports.Reserved = require('./src/Reserved');
|
||||
exports.String = require('./src/String');
|
||||
exports.Struct = require('./src/Struct');
|
||||
exports.VersionedStruct = require('./src/VersionedStruct');
|
||||
|
||||
const utils = require('./src/utils');
|
||||
const NumberT = require('./src/Number');
|
||||
const Pointer = require('./src/Pointer');
|
||||
|
||||
Object.assign(exports, utils, NumberT, Pointer);
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "restructure",
|
||||
"version": "2.0.1",
|
||||
"description": "Declaratively encode and decode binary data",
|
||||
"main": "index.js",
|
||||
"devDependencies": {
|
||||
"chai": "~4.2.0",
|
||||
"concat-stream": "~2.0.0",
|
||||
"coveralls": "^3.0.11",
|
||||
"iconv-lite": "^0.5.1",
|
||||
"mocha": "~7.1.1",
|
||||
"nyc": "^15.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha --reporter spec",
|
||||
"cover": "nyc --reporter=html --reporter=text mocha",
|
||||
"coveralls": "nyc report --reporter=text-lcov | coveralls"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/devongovett/restructure.git"
|
||||
},
|
||||
"keywords": [
|
||||
"binary",
|
||||
"struct",
|
||||
"encode",
|
||||
"decode"
|
||||
],
|
||||
"author": "Devon Govett <devongovett@gmail.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/devongovett/restructure/issues"
|
||||
},
|
||||
"homepage": "https://github.com/devongovett/restructure"
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
const {Number:NumberT} = require('./Number');
|
||||
const utils = require('./utils');
|
||||
|
||||
class ArrayT {
|
||||
constructor(type, length, lengthType = 'count') {
|
||||
this.type = type;
|
||||
this.length = length;
|
||||
this.lengthType = lengthType;
|
||||
}
|
||||
|
||||
decode(stream, parent) {
|
||||
let length;
|
||||
const { pos } = stream;
|
||||
|
||||
const res = [];
|
||||
let ctx = parent;
|
||||
|
||||
if (this.length != null) {
|
||||
length = utils.resolveLength(this.length, stream, parent);
|
||||
}
|
||||
|
||||
if (this.length instanceof NumberT) {
|
||||
// define hidden properties
|
||||
Object.defineProperties(res, {
|
||||
parent: { value: parent },
|
||||
_startOffset: { value: pos },
|
||||
_currentOffset: { value: 0, writable: true },
|
||||
_length: { value: length }
|
||||
});
|
||||
|
||||
ctx = res;
|
||||
}
|
||||
|
||||
if ((length == null) || (this.lengthType === 'bytes')) {
|
||||
const target = (length != null) ?
|
||||
stream.pos + length
|
||||
: (parent != null ? parent._length : undefined) ?
|
||||
parent._startOffset + parent._length
|
||||
:
|
||||
stream.length;
|
||||
|
||||
while (stream.pos < target) {
|
||||
res.push(this.type.decode(stream, ctx));
|
||||
}
|
||||
|
||||
} else {
|
||||
for (let i = 0, end = length; i < end; i++) {
|
||||
res.push(this.type.decode(stream, ctx));
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
size(array, ctx) {
|
||||
if (!array) {
|
||||
return this.type.size(null, ctx) * utils.resolveLength(this.length, null, ctx);
|
||||
}
|
||||
|
||||
let size = 0;
|
||||
if (this.length instanceof NumberT) {
|
||||
size += this.length.size();
|
||||
ctx = {parent: ctx};
|
||||
}
|
||||
|
||||
for (let item of array) {
|
||||
size += this.type.size(item, ctx);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
encode(stream, array, parent) {
|
||||
let ctx = parent;
|
||||
if (this.length instanceof NumberT) {
|
||||
ctx = {
|
||||
pointers: [],
|
||||
startOffset: stream.pos,
|
||||
parent
|
||||
};
|
||||
|
||||
ctx.pointerOffset = stream.pos + this.size(array, ctx);
|
||||
this.length.encode(stream, array.length);
|
||||
}
|
||||
|
||||
for (let item of array) {
|
||||
this.type.encode(stream, item, ctx);
|
||||
}
|
||||
|
||||
if (this.length instanceof NumberT) {
|
||||
let i = 0;
|
||||
while (i < ctx.pointers.length) {
|
||||
const ptr = ctx.pointers[i++];
|
||||
ptr.type.encode(stream, ptr.val);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ArrayT;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
class Bitfield {
|
||||
constructor(type, flags = []) {
|
||||
this.type = type;
|
||||
this.flags = flags;
|
||||
}
|
||||
decode(stream) {
|
||||
const val = this.type.decode(stream);
|
||||
|
||||
const res = {};
|
||||
for (let i = 0; i < this.flags.length; i++) {
|
||||
const flag = this.flags[i];
|
||||
if (flag != null) {
|
||||
res[flag] = !!(val & (1 << i));
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
size() {
|
||||
return this.type.size();
|
||||
}
|
||||
|
||||
encode(stream, keys) {
|
||||
let val = 0;
|
||||
for (let i = 0; i < this.flags.length; i++) {
|
||||
const flag = this.flags[i];
|
||||
if (flag != null) {
|
||||
if (keys[flag]) { val |= (1 << i); }
|
||||
}
|
||||
}
|
||||
|
||||
return this.type.encode(stream, val);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Bitfield;
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
class BooleanT {
|
||||
constructor(type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
decode(stream, parent) {
|
||||
return !!this.type.decode(stream, parent);
|
||||
}
|
||||
|
||||
size(val, parent) {
|
||||
return this.type.size(val, parent);
|
||||
}
|
||||
|
||||
encode(stream, val, parent) {
|
||||
return this.type.encode(stream, +val, parent);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BooleanT;
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
const utils = require('./utils');
|
||||
const {Number:NumberT} = require('./Number');
|
||||
|
||||
class BufferT {
|
||||
constructor(length) {
|
||||
this.length = length;
|
||||
}
|
||||
decode(stream, parent) {
|
||||
const length = utils.resolveLength(this.length, stream, parent);
|
||||
return stream.readBuffer(length);
|
||||
}
|
||||
|
||||
size(val, parent) {
|
||||
if (!val) {
|
||||
return utils.resolveLength(this.length, null, parent);
|
||||
}
|
||||
|
||||
return val.length;
|
||||
}
|
||||
|
||||
encode(stream, buf, parent) {
|
||||
if (this.length instanceof NumberT) {
|
||||
this.length.encode(stream, buf.length);
|
||||
}
|
||||
|
||||
return stream.writeBuffer(buf);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BufferT;
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
let iconv;
|
||||
try { iconv = require('iconv-lite'); } catch (error) {}
|
||||
|
||||
class DecodeStream {
|
||||
constructor(buffer) {
|
||||
this.buffer = buffer;
|
||||
this.pos = 0;
|
||||
this.length = this.buffer.length;
|
||||
}
|
||||
|
||||
readString(length, encoding = 'ascii') {
|
||||
switch (encoding) {
|
||||
case 'utf16le': case 'ucs2': case 'utf8': case 'ascii':
|
||||
return this.buffer.toString(encoding, this.pos, (this.pos += length));
|
||||
|
||||
case 'utf16be':
|
||||
var buf = Buffer.from(this.readBuffer(length));
|
||||
|
||||
// swap the bytes
|
||||
for (let i = 0, end = buf.length - 1; i < end; i += 2) {
|
||||
const byte = buf[i];
|
||||
buf[i] = buf[i + 1];
|
||||
buf[i + 1] = byte;
|
||||
}
|
||||
|
||||
return buf.toString('utf16le');
|
||||
|
||||
default:
|
||||
buf = this.readBuffer(length);
|
||||
if (iconv) {
|
||||
try {
|
||||
return iconv.decode(buf, encoding);
|
||||
} catch (error1) {}
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
readBuffer(length) {
|
||||
return this.buffer.slice(this.pos, (this.pos += length));
|
||||
}
|
||||
|
||||
readUInt24BE() {
|
||||
return (this.readUInt16BE() << 8) + this.readUInt8();
|
||||
}
|
||||
|
||||
readUInt24LE() {
|
||||
return this.readUInt16LE() + (this.readUInt8() << 16);
|
||||
}
|
||||
|
||||
readInt24BE() {
|
||||
return (this.readInt16BE() << 8) + this.readUInt8();
|
||||
}
|
||||
|
||||
readInt24LE() {
|
||||
return this.readUInt16LE() + (this.readInt8() << 16);
|
||||
}
|
||||
}
|
||||
|
||||
DecodeStream.TYPES = {
|
||||
UInt8: 1,
|
||||
UInt16: 2,
|
||||
UInt24: 3,
|
||||
UInt32: 4,
|
||||
Int8: 1,
|
||||
Int16: 2,
|
||||
Int24: 3,
|
||||
Int32: 4,
|
||||
Float: 4,
|
||||
Double: 8
|
||||
};
|
||||
|
||||
for (let key in Buffer.prototype) {
|
||||
if (key.slice(0, 4) === 'read') {
|
||||
const bytes = DecodeStream.TYPES[key.replace(/read|[BL]E/g, '')];
|
||||
DecodeStream.prototype[key] = function() {
|
||||
const ret = this.buffer[key](this.pos);
|
||||
this.pos += bytes;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DecodeStream;
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
let iconv;
|
||||
const stream = require('stream');
|
||||
const DecodeStream = require('./DecodeStream');
|
||||
try { iconv = require('iconv-lite'); } catch (error) {}
|
||||
|
||||
class EncodeStream extends stream.Readable {
|
||||
constructor(bufferSize = 65536) {
|
||||
super(...arguments);
|
||||
this.buffer = Buffer.alloc(bufferSize);
|
||||
this.bufferOffset = 0;
|
||||
this.pos = 0;
|
||||
}
|
||||
|
||||
// do nothing, required by node
|
||||
_read() {}
|
||||
|
||||
ensure(bytes) {
|
||||
if ((this.bufferOffset + bytes) > this.buffer.length) {
|
||||
return this.flush();
|
||||
}
|
||||
}
|
||||
|
||||
flush() {
|
||||
if (this.bufferOffset > 0) {
|
||||
this.push(Buffer.from(this.buffer.slice(0, this.bufferOffset)));
|
||||
return this.bufferOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
writeBuffer(buffer) {
|
||||
this.flush();
|
||||
this.push(buffer);
|
||||
return this.pos += buffer.length;
|
||||
}
|
||||
|
||||
writeString(string, encoding = 'ascii') {
|
||||
switch (encoding) {
|
||||
case 'utf16le': case 'ucs2': case 'utf8': case 'ascii':
|
||||
return this.writeBuffer(Buffer.from(string, encoding));
|
||||
|
||||
case 'utf16be':
|
||||
var buf = Buffer.from(string, 'utf16le');
|
||||
|
||||
// swap the bytes
|
||||
for (let i = 0, end = buf.length - 1; i < end; i += 2) {
|
||||
const byte = buf[i];
|
||||
buf[i] = buf[i + 1];
|
||||
buf[i + 1] = byte;
|
||||
}
|
||||
|
||||
return this.writeBuffer(buf);
|
||||
|
||||
default:
|
||||
if (iconv) {
|
||||
return this.writeBuffer(iconv.encode(string, encoding));
|
||||
} else {
|
||||
throw new Error('Install iconv-lite to enable additional string encodings.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writeUInt24BE(val) {
|
||||
this.ensure(3);
|
||||
this.buffer[this.bufferOffset++] = (val >>> 16) & 0xff;
|
||||
this.buffer[this.bufferOffset++] = (val >>> 8) & 0xff;
|
||||
this.buffer[this.bufferOffset++] = val & 0xff;
|
||||
return this.pos += 3;
|
||||
}
|
||||
|
||||
writeUInt24LE(val) {
|
||||
this.ensure(3);
|
||||
this.buffer[this.bufferOffset++] = val & 0xff;
|
||||
this.buffer[this.bufferOffset++] = (val >>> 8) & 0xff;
|
||||
this.buffer[this.bufferOffset++] = (val >>> 16) & 0xff;
|
||||
return this.pos += 3;
|
||||
}
|
||||
|
||||
writeInt24BE(val) {
|
||||
if (val >= 0) {
|
||||
return this.writeUInt24BE(val);
|
||||
} else {
|
||||
return this.writeUInt24BE(val + 0xffffff + 1);
|
||||
}
|
||||
}
|
||||
|
||||
writeInt24LE(val) {
|
||||
if (val >= 0) {
|
||||
return this.writeUInt24LE(val);
|
||||
} else {
|
||||
return this.writeUInt24LE(val + 0xffffff + 1);
|
||||
}
|
||||
}
|
||||
|
||||
fill(val, length) {
|
||||
if (length < this.buffer.length) {
|
||||
this.ensure(length);
|
||||
this.buffer.fill(val, this.bufferOffset, this.bufferOffset + length);
|
||||
this.bufferOffset += length;
|
||||
return this.pos += length;
|
||||
} else {
|
||||
const buf = Buffer.alloc(length);
|
||||
buf.fill(val);
|
||||
return this.writeBuffer(buf);
|
||||
}
|
||||
}
|
||||
|
||||
end() {
|
||||
this.flush();
|
||||
return this.push(null);
|
||||
}
|
||||
}
|
||||
|
||||
for (let key in Buffer.prototype) {
|
||||
if (key.slice(0, 5) === 'write') {
|
||||
const bytes = +DecodeStream.TYPES[key.replace(/write|[BL]E/g, '')];
|
||||
EncodeStream.prototype[key] = function(value) {
|
||||
this.ensure(bytes);
|
||||
this.buffer[key](value, this.bufferOffset);
|
||||
this.bufferOffset += bytes;
|
||||
return this.pos += bytes;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EncodeStream;
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
class Enum {
|
||||
constructor(type, options = []) {
|
||||
this.type = type;
|
||||
this.options = options;
|
||||
}
|
||||
decode(stream) {
|
||||
const index = this.type.decode(stream);
|
||||
return this.options[index] || index;
|
||||
}
|
||||
|
||||
size() {
|
||||
return this.type.size();
|
||||
}
|
||||
|
||||
encode(stream, val) {
|
||||
const index = this.options.indexOf(val);
|
||||
if (index === -1) {
|
||||
throw new Error(`Unknown option in enum: ${val}`);
|
||||
}
|
||||
|
||||
return this.type.encode(stream, index);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Enum;
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
const ArrayT = require('./Array');
|
||||
const {Number:NumberT} = require('./Number');
|
||||
const utils = require('./utils');
|
||||
const {inspect} = require('util');
|
||||
|
||||
class LazyArrayT extends ArrayT {
|
||||
decode(stream, parent) {
|
||||
const { pos } = stream;
|
||||
const length = utils.resolveLength(this.length, stream, parent);
|
||||
|
||||
if (this.length instanceof NumberT) {
|
||||
parent = {
|
||||
parent,
|
||||
_startOffset: pos,
|
||||
_currentOffset: 0,
|
||||
_length: length
|
||||
};
|
||||
}
|
||||
|
||||
const res = new LazyArray(this.type, length, stream, parent);
|
||||
|
||||
stream.pos += length * this.type.size(null, parent);
|
||||
return res;
|
||||
}
|
||||
|
||||
size(val, ctx) {
|
||||
if (val instanceof LazyArray) {
|
||||
val = val.toArray();
|
||||
}
|
||||
|
||||
return super.size(val, ctx);
|
||||
}
|
||||
|
||||
encode(stream, val, ctx) {
|
||||
if (val instanceof LazyArray) {
|
||||
val = val.toArray();
|
||||
}
|
||||
|
||||
return super.encode(stream, val, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
class LazyArray {
|
||||
constructor(type, length, stream, ctx) {
|
||||
this.type = type;
|
||||
this.length = length;
|
||||
this.stream = stream;
|
||||
this.ctx = ctx;
|
||||
this.base = this.stream.pos;
|
||||
this.items = [];
|
||||
}
|
||||
|
||||
get(index) {
|
||||
if ((index < 0) || (index >= this.length)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (this.items[index] == null) {
|
||||
const { pos } = this.stream;
|
||||
this.stream.pos = this.base + (this.type.size(null, this.ctx) * index);
|
||||
this.items[index] = this.type.decode(this.stream, this.ctx);
|
||||
this.stream.pos = pos;
|
||||
}
|
||||
|
||||
return this.items[index];
|
||||
}
|
||||
|
||||
toArray() {
|
||||
const result = [];
|
||||
for (let i = 0, end = this.length; i < end; i++) {
|
||||
result.push(this.get(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inspect() {
|
||||
return inspect(this.toArray());
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LazyArrayT;
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
const DecodeStream = require('./DecodeStream');
|
||||
|
||||
class NumberT {
|
||||
constructor(type, endian = 'BE') {
|
||||
this.type = type;
|
||||
this.endian = endian;
|
||||
this.fn = this.type;
|
||||
if (this.type[this.type.length - 1] !== '8') {
|
||||
this.fn += this.endian;
|
||||
}
|
||||
}
|
||||
|
||||
size() {
|
||||
return DecodeStream.TYPES[this.type];
|
||||
}
|
||||
|
||||
decode(stream) {
|
||||
return stream[`read${this.fn}`]();
|
||||
}
|
||||
|
||||
encode(stream, val) {
|
||||
return stream[`write${this.fn}`](val);
|
||||
}
|
||||
}
|
||||
|
||||
exports.Number = NumberT;
|
||||
exports.uint8 = new NumberT('UInt8');
|
||||
exports.uint16be = (exports.uint16 = new NumberT('UInt16', 'BE'));
|
||||
exports.uint16le = new NumberT('UInt16', 'LE');
|
||||
exports.uint24be = (exports.uint24 = new NumberT('UInt24', 'BE'));
|
||||
exports.uint24le = new NumberT('UInt24', 'LE');
|
||||
exports.uint32be = (exports.uint32 = new NumberT('UInt32', 'BE'));
|
||||
exports.uint32le = new NumberT('UInt32', 'LE');
|
||||
exports.int8 = new NumberT('Int8');
|
||||
exports.int16be = (exports.int16 = new NumberT('Int16', 'BE'));
|
||||
exports.int16le = new NumberT('Int16', 'LE');
|
||||
exports.int24be = (exports.int24 = new NumberT('Int24', 'BE'));
|
||||
exports.int24le = new NumberT('Int24', 'LE');
|
||||
exports.int32be = (exports.int32 = new NumberT('Int32', 'BE'));
|
||||
exports.int32le = new NumberT('Int32', 'LE');
|
||||
exports.floatbe = (exports.float = new NumberT('Float', 'BE'));
|
||||
exports.floatle = new NumberT('Float', 'LE');
|
||||
exports.doublebe = (exports.double = new NumberT('Double', 'BE'));
|
||||
exports.doublele = new NumberT('Double', 'LE');
|
||||
|
||||
class Fixed extends NumberT {
|
||||
constructor(size, endian, fracBits = size >> 1) {
|
||||
super(`Int${size}`, endian);
|
||||
this._point = 1 << fracBits;
|
||||
}
|
||||
|
||||
decode(stream) {
|
||||
return super.decode(stream) / this._point;
|
||||
}
|
||||
|
||||
encode(stream, val) {
|
||||
return super.encode(stream, (val * this._point) | 0);
|
||||
}
|
||||
}
|
||||
|
||||
exports.Fixed = Fixed;
|
||||
exports.fixed16be = (exports.fixed16 = new Fixed(16, 'BE'));
|
||||
exports.fixed16le = new Fixed(16, 'LE');
|
||||
exports.fixed32be = (exports.fixed32 =new Fixed(32, 'BE'));
|
||||
exports.fixed32le = new Fixed(32, 'LE');
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
class Optional {
|
||||
constructor(type, condition = true) {
|
||||
this.type = type;
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
decode(stream, parent) {
|
||||
let { condition } = this;
|
||||
if (typeof condition === 'function') {
|
||||
condition = condition.call(parent, parent);
|
||||
}
|
||||
|
||||
if (condition) {
|
||||
return this.type.decode(stream, parent);
|
||||
}
|
||||
}
|
||||
|
||||
size(val, parent) {
|
||||
let { condition } = this;
|
||||
if (typeof condition === 'function') {
|
||||
condition = condition.call(parent, parent);
|
||||
}
|
||||
|
||||
if (condition) {
|
||||
return this.type.size(val, parent);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
encode(stream, val, parent) {
|
||||
let { condition } = this;
|
||||
if (typeof condition === 'function') {
|
||||
condition = condition.call(parent, parent);
|
||||
}
|
||||
|
||||
if (condition) {
|
||||
return this.type.encode(stream, val, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Optional;
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
const utils = require('./utils');
|
||||
|
||||
class Pointer {
|
||||
constructor(offsetType, type, options = {}) {
|
||||
this.offsetType = offsetType;
|
||||
this.type = type;
|
||||
this.options = options;
|
||||
if (this.type === 'void') { this.type = null; }
|
||||
if (this.options.type == null) { this.options.type = 'local'; }
|
||||
if (this.options.allowNull == null) { this.options.allowNull = true; }
|
||||
if (this.options.nullValue == null) { this.options.nullValue = 0; }
|
||||
if (this.options.lazy == null) { this.options.lazy = false; }
|
||||
if (this.options.relativeTo) {
|
||||
if (typeof this.options.relativeTo !== 'function') {
|
||||
throw new Error('relativeTo option must be a function');
|
||||
}
|
||||
this.relativeToGetter = options.relativeTo;
|
||||
}
|
||||
}
|
||||
|
||||
decode(stream, ctx) {
|
||||
const offset = this.offsetType.decode(stream, ctx);
|
||||
|
||||
// handle NULL pointers
|
||||
if ((offset === this.options.nullValue) && this.options.allowNull) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let relative;
|
||||
switch (this.options.type) {
|
||||
case 'local': relative = ctx._startOffset; break;
|
||||
case 'immediate': relative = stream.pos - this.offsetType.size(); break;
|
||||
case 'parent': relative = ctx.parent._startOffset; break;
|
||||
default:
|
||||
var c = ctx;
|
||||
while (c.parent) {
|
||||
c = c.parent;
|
||||
}
|
||||
|
||||
relative = c._startOffset || 0;
|
||||
}
|
||||
|
||||
if (this.options.relativeTo) {
|
||||
relative += this.relativeToGetter(ctx);
|
||||
}
|
||||
|
||||
const ptr = offset + relative;
|
||||
|
||||
if (this.type != null) {
|
||||
let val = null;
|
||||
const decodeValue = () => {
|
||||
if (val != null) { return val; }
|
||||
|
||||
const { pos } = stream;
|
||||
stream.pos = ptr;
|
||||
val = this.type.decode(stream, ctx);
|
||||
stream.pos = pos;
|
||||
return val;
|
||||
};
|
||||
|
||||
// If this is a lazy pointer, define a getter to decode only when needed.
|
||||
// This obviously only works when the pointer is contained by a Struct.
|
||||
if (this.options.lazy) {
|
||||
return new utils.PropertyDescriptor({
|
||||
get: decodeValue});
|
||||
}
|
||||
|
||||
return decodeValue();
|
||||
} else {
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
|
||||
size(val, ctx) {
|
||||
const parent = ctx;
|
||||
switch (this.options.type) {
|
||||
case 'local': case 'immediate':
|
||||
break;
|
||||
case 'parent':
|
||||
ctx = ctx.parent;
|
||||
break;
|
||||
default: // global
|
||||
while (ctx.parent) {
|
||||
ctx = ctx.parent;
|
||||
}
|
||||
}
|
||||
|
||||
let { type } = this;
|
||||
if (type == null) {
|
||||
if (!(val instanceof VoidPointer)) {
|
||||
throw new Error("Must be a VoidPointer");
|
||||
}
|
||||
|
||||
({ type } = val);
|
||||
val = val.value;
|
||||
}
|
||||
|
||||
if (val && ctx) {
|
||||
ctx.pointerSize += type.size(val, parent);
|
||||
}
|
||||
|
||||
return this.offsetType.size();
|
||||
}
|
||||
|
||||
encode(stream, val, ctx) {
|
||||
let relative;
|
||||
const parent = ctx;
|
||||
if ((val == null)) {
|
||||
this.offsetType.encode(stream, this.options.nullValue);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this.options.type) {
|
||||
case 'local':
|
||||
relative = ctx.startOffset;
|
||||
break;
|
||||
case 'immediate':
|
||||
relative = stream.pos + this.offsetType.size(val, parent);
|
||||
break;
|
||||
case 'parent':
|
||||
ctx = ctx.parent;
|
||||
relative = ctx.startOffset;
|
||||
break;
|
||||
default: // global
|
||||
relative = 0;
|
||||
while (ctx.parent) {
|
||||
ctx = ctx.parent;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.relativeTo) {
|
||||
relative += this.relativeToGetter(parent.val);
|
||||
}
|
||||
|
||||
this.offsetType.encode(stream, ctx.pointerOffset - relative);
|
||||
|
||||
let { type } = this;
|
||||
if (type == null) {
|
||||
if (!(val instanceof VoidPointer)) {
|
||||
throw new Error("Must be a VoidPointer");
|
||||
}
|
||||
|
||||
({ type } = val);
|
||||
val = val.value;
|
||||
}
|
||||
|
||||
ctx.pointers.push({
|
||||
type,
|
||||
val,
|
||||
parent
|
||||
});
|
||||
|
||||
return ctx.pointerOffset += type.size(val, parent);
|
||||
}
|
||||
}
|
||||
|
||||
// A pointer whose type is determined at decode time
|
||||
class VoidPointer {
|
||||
constructor(type, value) {
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
exports.Pointer = Pointer;
|
||||
exports.VoidPointer = VoidPointer;
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
const utils = require('./utils');
|
||||
|
||||
class Reserved {
|
||||
constructor(type, count = 1) {
|
||||
this.type = type;
|
||||
this.count = count;
|
||||
}
|
||||
decode(stream, parent) {
|
||||
stream.pos += this.size(null, parent);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
size(data, parent) {
|
||||
const count = utils.resolveLength(this.count, null, parent);
|
||||
return this.type.size() * count;
|
||||
}
|
||||
|
||||
encode(stream, val, parent) {
|
||||
return stream.fill(0, this.size(val, parent));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Reserved;
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
const {Number:NumberT} = require('./Number');
|
||||
const utils = require('./utils');
|
||||
|
||||
class StringT {
|
||||
constructor(length, encoding = 'ascii') {
|
||||
this.length = length;
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
decode(stream, parent) {
|
||||
let length, pos;
|
||||
|
||||
if (this.length != null) {
|
||||
length = utils.resolveLength(this.length, stream, parent);
|
||||
} else {
|
||||
let buffer;
|
||||
({buffer, length, pos} = stream);
|
||||
|
||||
while ((pos < length) && (buffer[pos] !== 0x00)) {
|
||||
++pos;
|
||||
}
|
||||
|
||||
length = pos - stream.pos;
|
||||
}
|
||||
|
||||
let { encoding } = this;
|
||||
if (typeof encoding === 'function') {
|
||||
encoding = encoding.call(parent, parent) || 'ascii';
|
||||
}
|
||||
|
||||
const string = stream.readString(length, encoding);
|
||||
|
||||
if ((this.length == null) && (stream.pos < stream.length)) {
|
||||
stream.pos++;
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
size(val, parent) {
|
||||
// Use the defined value if no value was given
|
||||
if (!val) {
|
||||
return utils.resolveLength(this.length, null, parent);
|
||||
}
|
||||
|
||||
let { encoding } = this;
|
||||
if (typeof encoding === 'function') {
|
||||
encoding = encoding.call(parent != null ? parent.val : undefined, parent != null ? parent.val : undefined) || 'ascii';
|
||||
}
|
||||
|
||||
if (encoding === 'utf16be') {
|
||||
encoding = 'utf16le';
|
||||
}
|
||||
|
||||
let size = Buffer.byteLength(val, encoding);
|
||||
if (this.length instanceof NumberT) {
|
||||
size += this.length.size();
|
||||
}
|
||||
|
||||
if ((this.length == null)) {
|
||||
size++;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
encode(stream, val, parent) {
|
||||
let { encoding } = this;
|
||||
if (typeof encoding === 'function') {
|
||||
encoding = encoding.call(parent != null ? parent.val : undefined, parent != null ? parent.val : undefined) || 'ascii';
|
||||
}
|
||||
|
||||
if (this.length instanceof NumberT) {
|
||||
this.length.encode(stream, Buffer.byteLength(val, encoding));
|
||||
}
|
||||
|
||||
stream.writeString(val, encoding);
|
||||
|
||||
if ((this.length == null)) {
|
||||
return stream.writeUInt8(0x00);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = StringT;
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
const utils = require('./utils');
|
||||
|
||||
class Struct {
|
||||
constructor(fields = {}) {
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
decode(stream, parent, length = 0) {
|
||||
const res = this._setup(stream, parent, length);
|
||||
this._parseFields(stream, res, this.fields);
|
||||
|
||||
if (this.process != null) {
|
||||
this.process.call(res, stream);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
_setup(stream, parent, length) {
|
||||
const res = {};
|
||||
|
||||
// define hidden properties
|
||||
Object.defineProperties(res, {
|
||||
parent: { value: parent },
|
||||
_startOffset: { value: stream.pos },
|
||||
_currentOffset: { value: 0, writable: true },
|
||||
_length: { value: length }
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
_parseFields(stream, res, fields) {
|
||||
for (let key in fields) {
|
||||
var val;
|
||||
const type = fields[key];
|
||||
if (typeof type === 'function') {
|
||||
val = type.call(res, res);
|
||||
} else {
|
||||
val = type.decode(stream, res);
|
||||
}
|
||||
|
||||
if (val !== undefined) {
|
||||
if (val instanceof utils.PropertyDescriptor) {
|
||||
Object.defineProperty(res, key, val);
|
||||
} else {
|
||||
res[key] = val;
|
||||
}
|
||||
}
|
||||
|
||||
res._currentOffset = stream.pos - res._startOffset;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
size(val, parent, includePointers) {
|
||||
if (val == null) { val = {}; }
|
||||
if (includePointers == null) { includePointers = true; }
|
||||
const ctx = {
|
||||
parent,
|
||||
val,
|
||||
pointerSize: 0
|
||||
};
|
||||
|
||||
let size = 0;
|
||||
for (let key in this.fields) {
|
||||
const type = this.fields[key];
|
||||
if (type.size != null) {
|
||||
size += type.size(val[key], ctx);
|
||||
}
|
||||
}
|
||||
|
||||
if (includePointers) {
|
||||
size += ctx.pointerSize;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
encode(stream, val, parent) {
|
||||
let type;
|
||||
if (this.preEncode != null) {
|
||||
this.preEncode.call(val, stream);
|
||||
}
|
||||
|
||||
const ctx = {
|
||||
pointers: [],
|
||||
startOffset: stream.pos,
|
||||
parent,
|
||||
val,
|
||||
pointerSize: 0
|
||||
};
|
||||
|
||||
ctx.pointerOffset = stream.pos + this.size(val, ctx, false);
|
||||
|
||||
for (let key in this.fields) {
|
||||
type = this.fields[key];
|
||||
if (type.encode != null) {
|
||||
type.encode(stream, val[key], ctx);
|
||||
}
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
while (i < ctx.pointers.length) {
|
||||
const ptr = ctx.pointers[i++];
|
||||
ptr.type.encode(stream, ptr.val, ptr.parent);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Struct;
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
const Struct = require('./Struct');
|
||||
|
||||
const getPath = (object, pathArray) => {
|
||||
return pathArray.reduce((prevObj, key) => prevObj && prevObj[key], object);
|
||||
};
|
||||
|
||||
class VersionedStruct extends Struct {
|
||||
constructor(type, versions = {}) {
|
||||
super();
|
||||
this.type = type;
|
||||
this.versions = versions;
|
||||
if (typeof type === 'string') {
|
||||
this.versionPath = type.split('.');
|
||||
}
|
||||
}
|
||||
|
||||
decode(stream, parent, length = 0) {
|
||||
const res = this._setup(stream, parent, length);
|
||||
|
||||
if (typeof this.type === 'string') {
|
||||
res.version = getPath(parent, this.versionPath);
|
||||
} else {
|
||||
res.version = this.type.decode(stream);
|
||||
}
|
||||
|
||||
if (this.versions.header) {
|
||||
this._parseFields(stream, res, this.versions.header);
|
||||
}
|
||||
|
||||
const fields = this.versions[res.version];
|
||||
if ((fields == null)) {
|
||||
throw new Error(`Unknown version ${res.version}`);
|
||||
}
|
||||
|
||||
if (fields instanceof VersionedStruct) {
|
||||
return fields.decode(stream, parent);
|
||||
}
|
||||
|
||||
this._parseFields(stream, res, fields);
|
||||
|
||||
if (this.process != null) {
|
||||
this.process.call(res, stream);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
size(val, parent, includePointers = true) {
|
||||
let key, type;
|
||||
if (!val) {
|
||||
throw new Error('Not a fixed size');
|
||||
}
|
||||
|
||||
const ctx = {
|
||||
parent,
|
||||
val,
|
||||
pointerSize: 0
|
||||
};
|
||||
|
||||
let size = 0;
|
||||
if (typeof this.type !== 'string') {
|
||||
size += this.type.size(val.version, ctx);
|
||||
}
|
||||
|
||||
if (this.versions.header) {
|
||||
for (key in this.versions.header) {
|
||||
type = this.versions.header[key];
|
||||
if (type.size != null) {
|
||||
size += type.size(val[key], ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fields = this.versions[val.version];
|
||||
if ((fields == null)) {
|
||||
throw new Error(`Unknown version ${val.version}`);
|
||||
}
|
||||
|
||||
for (key in fields) {
|
||||
type = fields[key];
|
||||
if (type.size != null) {
|
||||
size += type.size(val[key], ctx);
|
||||
}
|
||||
}
|
||||
|
||||
if (includePointers) {
|
||||
size += ctx.pointerSize;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
encode(stream, val, parent) {
|
||||
let key, type;
|
||||
if (this.preEncode != null) {
|
||||
this.preEncode.call(val, stream);
|
||||
}
|
||||
|
||||
const ctx = {
|
||||
pointers: [],
|
||||
startOffset: stream.pos,
|
||||
parent,
|
||||
val,
|
||||
pointerSize: 0
|
||||
};
|
||||
|
||||
ctx.pointerOffset = stream.pos + this.size(val, ctx, false);
|
||||
|
||||
if (typeof this.type !== 'string') {
|
||||
this.type.encode(stream, val.version);
|
||||
}
|
||||
|
||||
if (this.versions.header) {
|
||||
for (key in this.versions.header) {
|
||||
type = this.versions.header[key];
|
||||
if (type.encode != null) {
|
||||
type.encode(stream, val[key], ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fields = this.versions[val.version];
|
||||
for (key in fields) {
|
||||
type = fields[key];
|
||||
if (type.encode != null) {
|
||||
type.encode(stream, val[key], ctx);
|
||||
}
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
while (i < ctx.pointers.length) {
|
||||
const ptr = ctx.pointers[i++];
|
||||
ptr.type.encode(stream, ptr.val, ptr.parent);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = VersionedStruct;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
const {Number:NumberT} = require('./Number');
|
||||
|
||||
exports.resolveLength = function(length, stream, parent) {
|
||||
let res;
|
||||
if (typeof length === 'number') {
|
||||
res = length;
|
||||
|
||||
} else if (typeof length === 'function') {
|
||||
res = length.call(parent, parent);
|
||||
|
||||
} else if (parent && (typeof length === 'string')) {
|
||||
res = parent[length];
|
||||
|
||||
} else if (stream && length instanceof NumberT) {
|
||||
res = length.decode(stream);
|
||||
}
|
||||
|
||||
if (isNaN(res)) {
|
||||
throw new Error('Not a fixed size');
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
class PropertyDescriptor {
|
||||
constructor(opts = {}) {
|
||||
this.enumerable = true;
|
||||
this.configurable = true;
|
||||
|
||||
for (let key in opts) {
|
||||
const val = opts[key];
|
||||
this[key] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports.PropertyDescriptor = PropertyDescriptor;
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
const {Array:ArrayT, Pointer, uint8, uint16, DecodeStream, EncodeStream} = require('../');
|
||||
const should = require('chai').should();
|
||||
const concat = require('concat-stream');
|
||||
|
||||
describe('Array', function() {
|
||||
describe('decode', function() {
|
||||
it('should decode fixed length', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1, 2, 3, 4, 5]));
|
||||
const array = new ArrayT(uint8, 4);
|
||||
return array.decode(stream).should.deep.equal([1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
it('should decode fixed amount of bytes', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1, 2, 3, 4, 5]));
|
||||
const array = new ArrayT(uint16, 4, 'bytes');
|
||||
return array.decode(stream).should.deep.equal([258, 772]);
|
||||
});
|
||||
|
||||
it('should decode length from parent key', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1, 2, 3, 4, 5]));
|
||||
const array = new ArrayT(uint8, 'len');
|
||||
return array.decode(stream, {len: 4}).should.deep.equal([1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
it('should decode amount of bytes from parent key', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1, 2, 3, 4, 5]));
|
||||
const array = new ArrayT(uint16, 'len', 'bytes');
|
||||
return array.decode(stream, {len: 4}).should.deep.equal([258, 772]);
|
||||
});
|
||||
|
||||
it('should decode length as number before array', function() {
|
||||
const stream = new DecodeStream(Buffer.from([4, 1, 2, 3, 4, 5]));
|
||||
const array = new ArrayT(uint8, uint8);
|
||||
return array.decode(stream).should.deep.equal([1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
it('should decode amount of bytes as number before array', function() {
|
||||
const stream = new DecodeStream(Buffer.from([4, 1, 2, 3, 4, 5]));
|
||||
const array = new ArrayT(uint16, uint8, 'bytes');
|
||||
return array.decode(stream).should.deep.equal([258, 772]);
|
||||
});
|
||||
|
||||
it('should decode length from function', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1, 2, 3, 4, 5]));
|
||||
const array = new ArrayT(uint8, function() { return 4; });
|
||||
return array.decode(stream).should.deep.equal([1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
it('should decode amount of bytes from function', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1, 2, 3, 4, 5]));
|
||||
const array = new ArrayT(uint16, (function() { return 4; }), 'bytes');
|
||||
return array.decode(stream).should.deep.equal([258, 772]);
|
||||
});
|
||||
|
||||
it('should decode to the end of the parent if no length is given', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1, 2, 3, 4, 5]));
|
||||
const array = new ArrayT(uint8);
|
||||
return array.decode(stream, {_length: 4, _startOffset: 0}).should.deep.equal([1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
return it('should decode to the end of the stream if no parent and length is given', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1, 2, 3, 4]));
|
||||
const array = new ArrayT(uint8);
|
||||
return array.decode(stream).should.deep.equal([1, 2, 3, 4]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('size', function() {
|
||||
it('should use array length', function() {
|
||||
const array = new ArrayT(uint8, 10);
|
||||
return array.size([1, 2, 3, 4]).should.equal(4);
|
||||
});
|
||||
|
||||
it('should add size of length field before string', function() {
|
||||
const array = new ArrayT(uint8, uint8);
|
||||
return array.size([1, 2, 3, 4]).should.equal(5);
|
||||
});
|
||||
|
||||
return it('should use defined length if no value given', function() {
|
||||
const array = new ArrayT(uint8, 10);
|
||||
return array.size().should.equal(10);
|
||||
});
|
||||
});
|
||||
|
||||
return describe('encode', function() {
|
||||
it('should encode using array length', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([1, 2, 3, 4]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const array = new ArrayT(uint8, 10);
|
||||
array.encode(stream, [1, 2, 3, 4]);
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should encode length as number before array', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([4, 1, 2, 3, 4]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const array = new ArrayT(uint8, uint8);
|
||||
array.encode(stream, [1, 2, 3, 4]);
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
return it('should add pointers after array if length is encoded at start', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([4, 5, 6, 7, 8, 1, 2, 3, 4]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const array = new ArrayT(new Pointer(uint8, uint8), uint8);
|
||||
array.encode(stream, [1, 2, 3, 4]);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
const {Bitfield, uint8, DecodeStream, EncodeStream} = require('../');
|
||||
const should = require('chai').should();
|
||||
const concat = require('concat-stream');
|
||||
|
||||
describe('Bitfield', function() {
|
||||
const bitfield = new Bitfield(uint8, ['Jack', 'Kack', 'Lack', 'Mack', 'Nack', 'Oack', 'Pack', 'Quack']);
|
||||
const JACK = 1 << 0;
|
||||
const KACK = 1 << 1;
|
||||
const LACK = 1 << 2;
|
||||
const MACK = 1 << 3;
|
||||
const NACK = 1 << 4;
|
||||
const OACK = 1 << 5;
|
||||
const PACK = 1 << 6;
|
||||
const QUACK = 1 << 7;
|
||||
|
||||
it('should have the right size', () => bitfield.size().should.equal(1));
|
||||
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([JACK | MACK | PACK | NACK | QUACK]));
|
||||
return bitfield.decode(stream).should.deep.equal({
|
||||
Jack: true, Kack: false, Lack: false, Mack: true, Nack: true, Oack: false, Pack: true, Quack: true});
|
||||
});
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([JACK | MACK | PACK | NACK | QUACK]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
bitfield.encode(stream, {Jack: true, Kack: false, Lack: false, Mack: true, Nack: true, Oack: false, Pack: true, Quack: true});
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
const {Boolean, uint8, DecodeStream, EncodeStream} = require('../');
|
||||
const should = require('chai').should();
|
||||
const concat = require('concat-stream');
|
||||
|
||||
describe('Boolean', function() {
|
||||
describe('decode', function() {
|
||||
it('should decode 0 as false', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0]));
|
||||
const boolean = new Boolean(uint8);
|
||||
return boolean.decode(stream).should.equal(false);
|
||||
});
|
||||
|
||||
return it('should decode 1 as true', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1]));
|
||||
const boolean = new Boolean(uint8);
|
||||
return boolean.decode(stream).should.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('size', () =>
|
||||
it('should return given type size', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0]));
|
||||
const boolean = new Boolean(uint8);
|
||||
return boolean.size().should.equal(1);
|
||||
})
|
||||
);
|
||||
|
||||
return describe('encode', function() {
|
||||
it('should encode false as 0', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
const boolean = new Boolean(uint8);
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
boolean.encode(stream, false);
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
return it('should encode true as 1', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
const boolean = new Boolean(uint8);
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([1]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
boolean.encode(stream, true);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
const {Buffer:BufferT, DecodeStream, EncodeStream, uint8} = require('../');
|
||||
const should = require('chai').should();
|
||||
const concat = require('concat-stream');
|
||||
|
||||
describe('Buffer', function() {
|
||||
describe('decode', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0xab, 0xff, 0x1f, 0xb6]));
|
||||
const buf = new BufferT(2);
|
||||
buf.decode(stream).should.deep.equal(Buffer.from([0xab, 0xff]));
|
||||
return buf.decode(stream).should.deep.equal(Buffer.from([0x1f, 0xb6]));
|
||||
});
|
||||
|
||||
return it('should decode with parent key length', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0xab, 0xff, 0x1f, 0xb6]));
|
||||
const buf = new BufferT('len');
|
||||
buf.decode(stream, {len: 3}).should.deep.equal(Buffer.from([0xab, 0xff, 0x1f]));
|
||||
return buf.decode(stream, {len: 1}).should.deep.equal(Buffer.from([0xb6]));
|
||||
});
|
||||
});
|
||||
|
||||
describe('size', function() {
|
||||
it('should return size', function() {
|
||||
const buf = new BufferT(2);
|
||||
return buf.size(Buffer.from([0xab, 0xff])).should.equal(2);
|
||||
});
|
||||
|
||||
return it('should use defined length if no value given', function() {
|
||||
const array = new BufferT(10);
|
||||
return array.size().should.equal(10);
|
||||
});
|
||||
});
|
||||
|
||||
return describe('encode', function() {
|
||||
it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0xab, 0xff, 0x1f, 0xb6]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const buf = new BufferT(2);
|
||||
buf.encode(stream, Buffer.from([0xab, 0xff]));
|
||||
buf.encode(stream, Buffer.from([0x1f, 0xb6]));
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
return it('should encode length before buffer', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([2, 0xab, 0xff]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const buf = new BufferT(uint8);
|
||||
buf.encode(stream, Buffer.from([0xab, 0xff]));
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
const {DecodeStream} = require('../');
|
||||
const should = require('chai').should();
|
||||
|
||||
describe('DecodeStream', function() {
|
||||
it('should read a buffer', function() {
|
||||
const buf = Buffer.from([1,2,3]);
|
||||
const stream = new DecodeStream(buf);
|
||||
return stream.readBuffer(buf.length).should.deep.equal(Buffer.from([1,2,3]));
|
||||
});
|
||||
|
||||
it('should readUInt16BE', function() {
|
||||
const buf = Buffer.from([0xab, 0xcd]);
|
||||
const stream = new DecodeStream(buf);
|
||||
return stream.readUInt16BE().should.deep.equal(0xabcd);
|
||||
});
|
||||
|
||||
it('should readUInt16LE', function() {
|
||||
const buf = Buffer.from([0xab, 0xcd]);
|
||||
const stream = new DecodeStream(buf);
|
||||
return stream.readUInt16LE().should.deep.equal(0xcdab);
|
||||
});
|
||||
|
||||
it('should readUInt24BE', function() {
|
||||
const buf = Buffer.from([0xab, 0xcd, 0xef]);
|
||||
const stream = new DecodeStream(buf);
|
||||
return stream.readUInt24BE().should.deep.equal(0xabcdef);
|
||||
});
|
||||
|
||||
it('should readUInt24LE', function() {
|
||||
const buf = Buffer.from([0xab, 0xcd, 0xef]);
|
||||
const stream = new DecodeStream(buf);
|
||||
return stream.readUInt24LE().should.deep.equal(0xefcdab);
|
||||
});
|
||||
|
||||
it('should readInt24BE', function() {
|
||||
const buf = Buffer.from([0xff, 0xab, 0x24]);
|
||||
const stream = new DecodeStream(buf);
|
||||
return stream.readInt24BE().should.deep.equal(-21724);
|
||||
});
|
||||
|
||||
it('should readInt24LE', function() {
|
||||
const buf = Buffer.from([0x24, 0xab, 0xff]);
|
||||
const stream = new DecodeStream(buf);
|
||||
return stream.readInt24LE().should.deep.equal(-21724);
|
||||
});
|
||||
|
||||
return describe('readString', function() {
|
||||
it('should decode ascii by default', function() {
|
||||
const buf = Buffer.from('some text', 'ascii');
|
||||
const stream = new DecodeStream(buf);
|
||||
return stream.readString(buf.length).should.equal('some text');
|
||||
});
|
||||
|
||||
it('should decode ascii', function() {
|
||||
const buf = Buffer.from('some text', 'ascii');
|
||||
const stream = new DecodeStream(buf);
|
||||
return stream.readString(buf.length, 'ascii').should.equal('some text');
|
||||
});
|
||||
|
||||
it('should decode utf8', function() {
|
||||
const buf = Buffer.from('unicode! 👍', 'utf8');
|
||||
const stream = new DecodeStream(buf);
|
||||
return stream.readString(buf.length, 'utf8').should.equal('unicode! 👍');
|
||||
});
|
||||
|
||||
it('should decode utf16le', function() {
|
||||
const buf = Buffer.from('unicode! 👍', 'utf16le');
|
||||
const stream = new DecodeStream(buf);
|
||||
return stream.readString(buf.length, 'utf16le').should.equal('unicode! 👍');
|
||||
});
|
||||
|
||||
it('should decode ucs2', function() {
|
||||
const buf = Buffer.from('unicode! 👍', 'ucs2');
|
||||
const stream = new DecodeStream(buf);
|
||||
return stream.readString(buf.length, 'ucs2').should.equal('unicode! 👍');
|
||||
});
|
||||
|
||||
it('should decode utf16be', function() {
|
||||
const buf = Buffer.from('unicode! 👍', 'utf16le');
|
||||
for (let i = 0, end = buf.length - 1; i < end; i += 2) {
|
||||
const byte = buf[i];
|
||||
buf[i] = buf[i + 1];
|
||||
buf[i + 1] = byte;
|
||||
}
|
||||
|
||||
const stream = new DecodeStream(buf);
|
||||
return stream.readString(buf.length, 'utf16be').should.equal('unicode! 👍');
|
||||
});
|
||||
|
||||
it('should decode macroman', function() {
|
||||
const buf = Buffer.from([0x8a, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x20, 0x63, 0x68, 0x87, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73]);
|
||||
const stream = new DecodeStream(buf);
|
||||
return stream.readString(buf.length, 'mac').should.equal('äccented cháracters');
|
||||
});
|
||||
|
||||
return it('should return a buffer for unsupported encodings', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1, 2, 3]));
|
||||
return stream.readString(3, 'unsupported').should.deep.equal(Buffer.from([1, 2, 3]));
|
||||
});
|
||||
});
|
||||
});
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
const {EncodeStream} = require('../');
|
||||
const should = require('chai').should();
|
||||
const concat = require('concat-stream');
|
||||
|
||||
describe('EncodeStream', function() {
|
||||
it('should write a buffer', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([1,2,3]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
stream.writeBuffer(Buffer.from([1,2,3]));
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should writeUInt16BE', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0xab, 0xcd]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
stream.writeUInt16BE(0xabcd);
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should writeUInt16LE', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0xab, 0xcd]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
stream.writeUInt16LE(0xcdab);
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should writeUInt24BE', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0xab, 0xcd, 0xef]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
stream.writeUInt24BE(0xabcdef);
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should writeUInt24LE', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0xef, 0xcd, 0xab]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
stream.writeUInt24LE(0xabcdef);
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should writeInt24BE', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0xff, 0xab, 0x24, 0xab, 0xcd, 0xef]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
stream.writeInt24BE(-21724);
|
||||
stream.writeInt24BE(0xabcdef);
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should writeInt24LE', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0x24, 0xab, 0xff, 0xef, 0xcd, 0xab]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
stream.writeInt24LE(-21724);
|
||||
stream.writeInt24LE(0xabcdef);
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should fill', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([10, 10, 10, 10, 10]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
stream.fill(10, 5);
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
return describe('writeString', function() {
|
||||
it('should encode ascii by default', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from('some text', 'ascii'));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
stream.writeString('some text');
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should encode ascii', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from('some text', 'ascii'));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
stream.writeString('some text');
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should encode utf8', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from('unicode! 👍', 'utf8'));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
stream.writeString('unicode! 👍', 'utf8');
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should encode utf16le', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from('unicode! 👍', 'utf16le'));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
stream.writeString('unicode! 👍', 'utf16le');
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should encode ucs2', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from('unicode! 👍', 'ucs2'));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
stream.writeString('unicode! 👍', 'ucs2');
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should encode utf16be', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(out) {
|
||||
const buf = Buffer.from('unicode! 👍', 'utf16le');
|
||||
for (let i = 0, end = buf.length - 1; i < end; i += 2) {
|
||||
const byte = buf[i];
|
||||
buf[i] = buf[i + 1];
|
||||
buf[i + 1] = byte;
|
||||
}
|
||||
|
||||
out.should.deep.equal(buf);
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
stream.writeString('unicode! 👍', 'utf16be');
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
return it('should encode macroman', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(out) {
|
||||
const buf = Buffer.from([0x8a, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x20, 0x63, 0x68, 0x87, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73]);
|
||||
out.should.deep.equal(buf);
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
stream.writeString('äccented cháracters', 'mac');
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
const {Enum, uint8, DecodeStream, EncodeStream} = require('../');
|
||||
const should = require('chai').should();
|
||||
const concat = require('concat-stream');
|
||||
|
||||
describe('Enum', function() {
|
||||
const e = new Enum(uint8, ['foo', 'bar', 'baz']);
|
||||
it('should have the right size', () => e.size().should.equal(1));
|
||||
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1, 2, 0]));
|
||||
e.decode(stream).should.equal('bar');
|
||||
e.decode(stream).should.equal('baz');
|
||||
return e.decode(stream).should.equal('foo');
|
||||
});
|
||||
|
||||
it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([1, 2, 0]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
e.encode(stream, 'bar');
|
||||
e.encode(stream, 'baz');
|
||||
e.encode(stream, 'foo');
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
return it('should throw on unknown option', function() {
|
||||
const stream = new EncodeStream;
|
||||
return should.throw(() => e.encode(stream, 'unknown'));
|
||||
});
|
||||
});
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
const {LazyArray, Pointer, uint8, uint16, DecodeStream, EncodeStream} = require('../');
|
||||
const should = require('chai').should();
|
||||
const concat = require('concat-stream');
|
||||
|
||||
describe('LazyArray', function() {
|
||||
describe('decode', function() {
|
||||
it('should decode items lazily', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1, 2, 3, 4, 5]));
|
||||
const array = new LazyArray(uint8, 4);
|
||||
|
||||
const arr = array.decode(stream);
|
||||
arr.should.not.be.an.instanceof(Array);
|
||||
arr.should.have.length(4);
|
||||
stream.pos.should.equal(4);
|
||||
|
||||
arr.get(0).should.equal(1);
|
||||
arr.get(1).should.equal(2);
|
||||
arr.get(2).should.equal(3);
|
||||
arr.get(3).should.equal(4);
|
||||
|
||||
should.not.exist(arr.get(-1));
|
||||
return should.not.exist(arr.get(5));
|
||||
});
|
||||
|
||||
it('should be able to convert to an array', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1, 2, 3, 4, 5]));
|
||||
const array = new LazyArray(uint8, 4);
|
||||
|
||||
const arr = array.decode(stream);
|
||||
return arr.toArray().should.deep.equal([1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
it('should have an inspect method', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1, 2, 3, 4, 5]));
|
||||
const array = new LazyArray(uint8, 4);
|
||||
|
||||
const arr = array.decode(stream);
|
||||
return arr.inspect().should.equal('[ 1, 2, 3, 4 ]');
|
||||
});
|
||||
|
||||
return it('should decode length as number before array', function() {
|
||||
const stream = new DecodeStream(Buffer.from([4, 1, 2, 3, 4, 5]));
|
||||
const array = new LazyArray(uint8, uint8);
|
||||
const arr = array.decode(stream);
|
||||
|
||||
return arr.toArray().should.deep.equal([1, 2, 3, 4]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('size', () =>
|
||||
it('should work with LazyArrays', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1, 2, 3, 4, 5]));
|
||||
const array = new LazyArray(uint8, 4);
|
||||
const arr = array.decode(stream);
|
||||
|
||||
return array.size(arr).should.equal(4);
|
||||
})
|
||||
);
|
||||
|
||||
return describe('encode', () =>
|
||||
it('should work with LazyArrays', function(done) {
|
||||
const stream = new DecodeStream(Buffer.from([1, 2, 3, 4, 5]));
|
||||
const array = new LazyArray(uint8, 4);
|
||||
const arr = array.decode(stream);
|
||||
|
||||
const enc = new EncodeStream;
|
||||
enc.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([1, 2, 3, 4]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
array.encode(enc, arr);
|
||||
return enc.end();
|
||||
})
|
||||
);
|
||||
});
|
||||
+525
@@ -0,0 +1,525 @@
|
||||
const {
|
||||
uint8,
|
||||
uint16, uint16be, uint16le,
|
||||
uint24, uint24be, uint24le,
|
||||
uint32, uint32be, uint32le,
|
||||
int8,
|
||||
int16, int16be, int16le,
|
||||
int24, int24be, int24le,
|
||||
int32, int32be, int32le,
|
||||
float, floatbe, floatle,
|
||||
double, doublebe, doublele,
|
||||
fixed16, fixed16be, fixed16le,
|
||||
fixed32, fixed32be, fixed32le,
|
||||
DecodeStream, EncodeStream
|
||||
} = require('../');
|
||||
const should = require('chai').should();
|
||||
const concat = require('concat-stream');
|
||||
|
||||
describe('Number', function() {
|
||||
describe('uint8', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0xab, 0xff]));
|
||||
uint8.decode(stream).should.equal(0xab);
|
||||
return uint8.decode(stream).should.equal(0xff);
|
||||
});
|
||||
|
||||
it('should have a size', () => uint8.size().should.equal(1));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0xab, 0xff]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
uint8.encode(stream, 0xab);
|
||||
uint8.encode(stream, 0xff);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('uint16', () =>
|
||||
it('is an alias for uint16be', () => uint16.should.equal(uint16be))
|
||||
);
|
||||
|
||||
describe('uint16be', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0xab, 0xff]));
|
||||
return uint16be.decode(stream).should.equal(0xabff);
|
||||
});
|
||||
|
||||
it('should have a size', () => uint16be.size().should.equal(2));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0xab, 0xff]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
uint16be.encode(stream, 0xabff);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('uint16le', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0xff, 0xab]));
|
||||
return uint16le.decode(stream).should.equal(0xabff);
|
||||
});
|
||||
|
||||
it('should have a size', () => uint16le.size().should.equal(2));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0xff, 0xab]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
uint16le.encode(stream, 0xabff);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('uint24', () =>
|
||||
it('is an alias for uint24be', () => uint24.should.equal(uint24be))
|
||||
);
|
||||
|
||||
describe('uint24be', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0xff, 0xab, 0x24]));
|
||||
return uint24be.decode(stream).should.equal(0xffab24);
|
||||
});
|
||||
|
||||
it('should have a size', () => uint24be.size().should.equal(3));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0xff, 0xab, 0x24]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
uint24be.encode(stream, 0xffab24);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('uint24le', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0x24, 0xab, 0xff]));
|
||||
return uint24le.decode(stream).should.equal(0xffab24);
|
||||
});
|
||||
|
||||
it('should have a size', () => uint24le.size().should.equal(3));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0x24, 0xab, 0xff]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
uint24le.encode(stream, 0xffab24);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('uint32', () =>
|
||||
it('is an alias for uint32be', () => uint32.should.equal(uint32be))
|
||||
);
|
||||
|
||||
describe('uint32be', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0xff, 0xab, 0x24, 0xbf]));
|
||||
return uint32be.decode(stream).should.equal(0xffab24bf);
|
||||
});
|
||||
|
||||
it('should have a size', () => uint32be.size().should.equal(4));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0xff, 0xab, 0x24, 0xbf]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
uint32be.encode(stream, 0xffab24bf);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('uint32le', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0xbf, 0x24, 0xab, 0xff]));
|
||||
return uint32le.decode(stream).should.equal(0xffab24bf);
|
||||
});
|
||||
|
||||
it('should have a size', () => uint32le.size().should.equal(4));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0xbf, 0x24, 0xab, 0xff]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
uint32le.encode(stream, 0xffab24bf);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('int8', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0x7f, 0xff]));
|
||||
int8.decode(stream).should.equal(127);
|
||||
return int8.decode(stream).should.equal(-1);
|
||||
});
|
||||
|
||||
it('should have a size', () => int8.size().should.equal(1));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0x7f, 0xff]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
int8.encode(stream, 127);
|
||||
int8.encode(stream, -1);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('int16', () =>
|
||||
it('is an alias for int16be', () => int16.should.equal(int16be))
|
||||
);
|
||||
|
||||
describe('int16be', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0xff, 0xab]));
|
||||
return int16be.decode(stream).should.equal(-85);
|
||||
});
|
||||
|
||||
it('should have a size', () => int16be.size().should.equal(2));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0xff, 0xab]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
int16be.encode(stream, -85);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('int16le', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0xab, 0xff]));
|
||||
return int16le.decode(stream).should.equal(-85);
|
||||
});
|
||||
|
||||
it('should have a size', () => int16le.size().should.equal(2));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0xab, 0xff]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
int16le.encode(stream, -85);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('int24', () =>
|
||||
it('is an alias for int24be', () => int24.should.equal(int24be))
|
||||
);
|
||||
|
||||
describe('int24be', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0xff, 0xab, 0x24]));
|
||||
return int24be.decode(stream).should.equal(-21724);
|
||||
});
|
||||
|
||||
it('should have a size', () => int24be.size().should.equal(3));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0xff, 0xab, 0x24]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
int24be.encode(stream, -21724);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('int24le', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0x24, 0xab, 0xff]));
|
||||
return int24le.decode(stream).should.equal(-21724);
|
||||
});
|
||||
|
||||
it('should have a size', () => int24le.size().should.equal(3));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0x24, 0xab, 0xff]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
int24le.encode(stream, -21724);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('int32', () =>
|
||||
it('is an alias for int32be', () => int32.should.equal(int32be))
|
||||
);
|
||||
|
||||
describe('int32be', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0xff, 0xab, 0x24, 0xbf]));
|
||||
return int32be.decode(stream).should.equal(-5561153);
|
||||
});
|
||||
|
||||
it('should have a size', () => int32be.size().should.equal(4));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0xff, 0xab, 0x24, 0xbf]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
int32be.encode(stream, -5561153);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('int32le', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0xbf, 0x24, 0xab, 0xff]));
|
||||
return int32le.decode(stream).should.equal(-5561153);
|
||||
});
|
||||
|
||||
it('should have a size', () => int32le.size().should.equal(4));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0xbf, 0x24, 0xab, 0xff]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
int32le.encode(stream, -5561153);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('float', () =>
|
||||
it('is an alias for floatbe', () => float.should.equal(floatbe))
|
||||
);
|
||||
|
||||
describe('floatbe', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0x43, 0x7a, 0x8c, 0xcd]));
|
||||
return floatbe.decode(stream).should.be.closeTo(250.55, 0.005);
|
||||
});
|
||||
|
||||
it('should have a size', () => floatbe.size().should.equal(4));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0x43, 0x7a, 0x8c, 0xcd]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
floatbe.encode(stream, 250.55);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('floatle', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0xcd, 0x8c, 0x7a, 0x43]));
|
||||
return floatle.decode(stream).should.be.closeTo(250.55, 0.005);
|
||||
});
|
||||
|
||||
it('should have a size', () => floatle.size().should.equal(4));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0xcd, 0x8c, 0x7a, 0x43]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
floatle.encode(stream, 250.55);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('double', () =>
|
||||
it('is an alias for doublebe', () => double.should.equal(doublebe))
|
||||
);
|
||||
|
||||
describe('doublebe', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0x40, 0x93, 0x4a, 0x3d, 0x70, 0xa3, 0xd7, 0x0a]));
|
||||
return doublebe.decode(stream).should.be.equal(1234.56);
|
||||
});
|
||||
|
||||
it('should have a size', () => doublebe.size().should.equal(8));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0x40, 0x93, 0x4a, 0x3d, 0x70, 0xa3, 0xd7, 0x0a]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
doublebe.encode(stream, 1234.56);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('doublele', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0x0a, 0xd7, 0xa3, 0x70, 0x3d, 0x4a, 0x93, 0x40]));
|
||||
return doublele.decode(stream).should.be.equal(1234.56);
|
||||
});
|
||||
|
||||
it('should have a size', () => doublele.size().should.equal(8));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0x0a, 0xd7, 0xa3, 0x70, 0x3d, 0x4a, 0x93, 0x40]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
doublele.encode(stream, 1234.56);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('fixed16', () =>
|
||||
it('is an alias for fixed16be', () => fixed16.should.equal(fixed16be))
|
||||
);
|
||||
|
||||
describe('fixed16be', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0x19, 0x57]));
|
||||
return fixed16be.decode(stream).should.be.closeTo(25.34, 0.005);
|
||||
});
|
||||
|
||||
it('should have a size', () => fixed16be.size().should.equal(2));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0x19, 0x57]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
fixed16be.encode(stream, 25.34);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('fixed16le', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0x57, 0x19]));
|
||||
return fixed16le.decode(stream).should.be.closeTo(25.34, 0.005);
|
||||
});
|
||||
|
||||
it('should have a size', () => fixed16le.size().should.equal(2));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0x57, 0x19]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
fixed16le.encode(stream, 25.34);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('fixed32', () =>
|
||||
it('is an alias for fixed32be', () => fixed32.should.equal(fixed32be))
|
||||
);
|
||||
|
||||
describe('fixed32be', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0x00, 0xfa, 0x8c, 0xcc]));
|
||||
return fixed32be.decode(stream).should.be.closeTo(250.55, 0.005);
|
||||
});
|
||||
|
||||
it('should have a size', () => fixed32be.size().should.equal(4));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0x00, 0xfa, 0x8c, 0xcc]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
fixed32be.encode(stream, 250.55);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
|
||||
return describe('fixed32le', function() {
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0xcc, 0x8c, 0xfa, 0x00]));
|
||||
return fixed32le.decode(stream).should.be.closeTo(250.55, 0.005);
|
||||
});
|
||||
|
||||
it('should have a size', () => fixed32le.size().should.equal(4));
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0xcc, 0x8c, 0xfa, 0x00]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
fixed32le.encode(stream, 250.55);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
const {Optional, uint8, DecodeStream, EncodeStream} = require('../');
|
||||
const should = require('chai').should();
|
||||
const concat = require('concat-stream');
|
||||
|
||||
describe('Optional', function() {
|
||||
describe('decode', function() {
|
||||
it('should not decode when condition is falsy', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0]));
|
||||
const optional = new Optional(uint8, false);
|
||||
should.not.exist(optional.decode(stream));
|
||||
return stream.pos.should.equal(0);
|
||||
});
|
||||
|
||||
it('should not decode when condition is a function and falsy', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0]));
|
||||
const optional = new Optional(uint8, function() { return false; });
|
||||
should.not.exist(optional.decode(stream));
|
||||
return stream.pos.should.equal(0);
|
||||
});
|
||||
|
||||
it('should decode when condition is omitted', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0]));
|
||||
const optional = new Optional(uint8);
|
||||
should.exist(optional.decode(stream));
|
||||
return stream.pos.should.equal(1);
|
||||
});
|
||||
|
||||
it('should decode when condition is truthy', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0]));
|
||||
const optional = new Optional(uint8, true);
|
||||
should.exist(optional.decode(stream));
|
||||
return stream.pos.should.equal(1);
|
||||
});
|
||||
|
||||
return it('should decode when condition is a function and truthy', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0]));
|
||||
const optional = new Optional(uint8, function() { return true; });
|
||||
should.exist(optional.decode(stream));
|
||||
return stream.pos.should.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('size', function() {
|
||||
it('should return 0 when condition is falsy', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0]));
|
||||
const optional = new Optional(uint8, false);
|
||||
return optional.size().should.equal(0);
|
||||
});
|
||||
|
||||
it('should return 0 when condition is a function and falsy', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0]));
|
||||
const optional = new Optional(uint8, function() { return false; });
|
||||
return optional.size().should.equal(0);
|
||||
});
|
||||
|
||||
it('should return given type size when condition is omitted', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0]));
|
||||
const optional = new Optional(uint8);
|
||||
return optional.size().should.equal(1);
|
||||
});
|
||||
|
||||
it('should return given type size when condition is truthy', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0]));
|
||||
const optional = new Optional(uint8, true);
|
||||
return optional.size().should.equal(1);
|
||||
});
|
||||
|
||||
return it('should return given type size when condition is a function and truthy', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0]));
|
||||
const optional = new Optional(uint8, function() { return true; });
|
||||
return optional.size().should.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
return describe('encode', function() {
|
||||
it('should not encode when condition is falsy', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
const optional = new Optional(uint8, false);
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal([]);
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
optional.encode(stream, 128);
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should not encode when condition is a function and falsy', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
const optional = new Optional(uint8, function() { return false; });
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal([]);
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
optional.encode(stream, 128);
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should encode when condition is omitted', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
const optional = new Optional(uint8);
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([128]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
optional.encode(stream, 128);
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should encode when condition is truthy', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
const optional = new Optional(uint8, true);
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([128]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
optional.encode(stream, 128);
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
return it('should encode when condition is a function and truthy', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
const optional = new Optional(uint8, function() { return true; });
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([128]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
optional.encode(stream, 128);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
+337
@@ -0,0 +1,337 @@
|
||||
const {Pointer, VoidPointer, uint8, DecodeStream, EncodeStream, Struct} = require('../');
|
||||
const should = require('chai').should();
|
||||
const concat = require('concat-stream');
|
||||
|
||||
describe('Pointer', function() {
|
||||
describe('decode', function() {
|
||||
it('should handle null pointers', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0]));
|
||||
const pointer = new Pointer(uint8, uint8);
|
||||
return should.not.exist(pointer.decode(stream, {_startOffset: 50}));
|
||||
});
|
||||
|
||||
it('should use local offsets from start of parent by default', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1, 53]));
|
||||
const pointer = new Pointer(uint8, uint8);
|
||||
return pointer.decode(stream, {_startOffset: 0}).should.equal(53);
|
||||
});
|
||||
|
||||
it('should support immediate offsets', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1, 53]));
|
||||
const pointer = new Pointer(uint8, uint8, {type: 'immediate'});
|
||||
return pointer.decode(stream).should.equal(53);
|
||||
});
|
||||
|
||||
it('should support offsets relative to the parent', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0, 0, 1, 53]));
|
||||
stream.pos = 2;
|
||||
const pointer = new Pointer(uint8, uint8, {type: 'parent'});
|
||||
return pointer.decode(stream, {parent: {_startOffset: 2}}).should.equal(53);
|
||||
});
|
||||
|
||||
it('should support global offsets', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1, 2, 4, 0, 0, 0, 53]));
|
||||
const pointer = new Pointer(uint8, uint8, {type: 'global'});
|
||||
stream.pos = 2;
|
||||
return pointer.decode(stream, {parent: {parent: {_startOffset: 2}}}).should.equal(53);
|
||||
});
|
||||
|
||||
it('should support offsets relative to a property on the parent', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1, 0, 0, 0, 0, 53]));
|
||||
const pointer = new Pointer(uint8, uint8, {relativeTo: ctx => ctx.parent.ptr});
|
||||
return pointer.decode(stream, {_startOffset: 0, parent: {ptr: 4}}).should.equal(53);
|
||||
});
|
||||
|
||||
it('should throw when passing a non function relativeTo option', function() {
|
||||
return should.throw(() => new Pointer(uint8, uint8, {relativeTo: 'parent.ptr'}));
|
||||
});
|
||||
|
||||
it('should support returning pointer if there is no decode type', function() {
|
||||
const stream = new DecodeStream(Buffer.from([4]));
|
||||
const pointer = new Pointer(uint8, 'void');
|
||||
return pointer.decode(stream, {_startOffset: 0}).should.equal(4);
|
||||
});
|
||||
|
||||
return it('should support decoding pointers lazily', function() {
|
||||
const stream = new DecodeStream(Buffer.from([1, 53]));
|
||||
const struct = new Struct({
|
||||
ptr: new Pointer(uint8, uint8, {lazy: true})});
|
||||
|
||||
const res = struct.decode(stream);
|
||||
Object.getOwnPropertyDescriptor(res, 'ptr').get.should.be.a('function');
|
||||
Object.getOwnPropertyDescriptor(res, 'ptr').enumerable.should.equal(true);
|
||||
return res.ptr.should.equal(53);
|
||||
});
|
||||
});
|
||||
|
||||
describe('size', function() {
|
||||
it('should add to local pointerSize', function() {
|
||||
const pointer = new Pointer(uint8, uint8);
|
||||
const ctx = {pointerSize: 0};
|
||||
pointer.size(10, ctx).should.equal(1);
|
||||
return ctx.pointerSize.should.equal(1);
|
||||
});
|
||||
|
||||
it('should add to immediate pointerSize', function() {
|
||||
const pointer = new Pointer(uint8, uint8, {type: 'immediate'});
|
||||
const ctx = {pointerSize: 0};
|
||||
pointer.size(10, ctx).should.equal(1);
|
||||
return ctx.pointerSize.should.equal(1);
|
||||
});
|
||||
|
||||
it('should add to parent pointerSize', function() {
|
||||
const pointer = new Pointer(uint8, uint8, {type: 'parent'});
|
||||
const ctx = {parent: {pointerSize: 0}};
|
||||
pointer.size(10, ctx).should.equal(1);
|
||||
return ctx.parent.pointerSize.should.equal(1);
|
||||
});
|
||||
|
||||
it('should add to global pointerSize', function() {
|
||||
const pointer = new Pointer(uint8, uint8, {type: 'global'});
|
||||
const ctx = {parent: {parent: {parent: {pointerSize: 0}}}};
|
||||
pointer.size(10, ctx).should.equal(1);
|
||||
return ctx.parent.parent.parent.pointerSize.should.equal(1);
|
||||
});
|
||||
|
||||
it('should handle void pointers', function() {
|
||||
const pointer = new Pointer(uint8, 'void');
|
||||
const ctx = {pointerSize: 0};
|
||||
pointer.size(new VoidPointer(uint8, 50), ctx).should.equal(1);
|
||||
return ctx.pointerSize.should.equal(1);
|
||||
});
|
||||
|
||||
it('should throw if no type and not a void pointer', function() {
|
||||
const pointer = new Pointer(uint8, 'void');
|
||||
const ctx = {pointerSize: 0};
|
||||
return should.throw(() => pointer.size(30, ctx).should.equal(1));
|
||||
});
|
||||
|
||||
return it('should return a fixed size without a value', function() {
|
||||
const pointer = new Pointer(uint8, uint8);
|
||||
return pointer.size().should.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
return describe('encode', function() {
|
||||
it('should handle null pointers', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const ptr = new Pointer(uint8, uint8);
|
||||
const ctx = {
|
||||
pointerSize: 0,
|
||||
startOffset: 0,
|
||||
pointerOffset: 0,
|
||||
pointers: []
|
||||
};
|
||||
|
||||
ptr.encode(stream, null, ctx);
|
||||
ctx.pointerSize.should.equal(0);
|
||||
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should handle local offsets', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([1]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const ptr = new Pointer(uint8, uint8);
|
||||
const ctx = {
|
||||
pointerSize: 0,
|
||||
startOffset: 0,
|
||||
pointerOffset: 1,
|
||||
pointers: []
|
||||
};
|
||||
|
||||
ptr.encode(stream, 10, ctx);
|
||||
ctx.pointerOffset.should.equal(2);
|
||||
ctx.pointers.should.deep.equal([
|
||||
{ type: uint8, val: 10, parent: ctx }
|
||||
]);
|
||||
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should handle immediate offsets', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const ptr = new Pointer(uint8, uint8, {type: 'immediate'});
|
||||
const ctx = {
|
||||
pointerSize: 0,
|
||||
startOffset: 0,
|
||||
pointerOffset: 1,
|
||||
pointers: []
|
||||
};
|
||||
|
||||
ptr.encode(stream, 10, ctx);
|
||||
ctx.pointerOffset.should.equal(2);
|
||||
ctx.pointers.should.deep.equal([
|
||||
{ type: uint8, val: 10, parent: ctx }
|
||||
]);
|
||||
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should handle immediate offsets', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const ptr = new Pointer(uint8, uint8, {type: 'immediate'});
|
||||
const ctx = {
|
||||
pointerSize: 0,
|
||||
startOffset: 0,
|
||||
pointerOffset: 1,
|
||||
pointers: []
|
||||
};
|
||||
|
||||
ptr.encode(stream, 10, ctx);
|
||||
ctx.pointerOffset.should.equal(2);
|
||||
ctx.pointers.should.deep.equal([
|
||||
{ type: uint8, val: 10, parent: ctx }
|
||||
]);
|
||||
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should handle offsets relative to parent', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([2]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const ptr = new Pointer(uint8, uint8, {type: 'parent'});
|
||||
const ctx = {
|
||||
parent: {
|
||||
pointerSize: 0,
|
||||
startOffset: 3,
|
||||
pointerOffset: 5,
|
||||
pointers: []
|
||||
}
|
||||
};
|
||||
|
||||
ptr.encode(stream, 10, ctx);
|
||||
ctx.parent.pointerOffset.should.equal(6);
|
||||
ctx.parent.pointers.should.deep.equal([
|
||||
{ type: uint8, val: 10, parent: ctx }
|
||||
]);
|
||||
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should handle global offsets', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([5]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const ptr = new Pointer(uint8, uint8, {type: 'global'});
|
||||
const ctx = {
|
||||
parent: {
|
||||
parent: {
|
||||
parent: {
|
||||
pointerSize: 0,
|
||||
startOffset: 3,
|
||||
pointerOffset: 5,
|
||||
pointers: []
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ptr.encode(stream, 10, ctx);
|
||||
ctx.parent.parent.parent.pointerOffset.should.equal(6);
|
||||
ctx.parent.parent.parent.pointers.should.deep.equal([
|
||||
{ type: uint8, val: 10, parent: ctx }
|
||||
]);
|
||||
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should support offsets relative to a property on the parent', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([6]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const ptr = new Pointer(uint8, uint8, {relativeTo: ctx => ctx.ptr});
|
||||
const ctx = {
|
||||
pointerSize: 0,
|
||||
startOffset: 0,
|
||||
pointerOffset: 10,
|
||||
pointers: [],
|
||||
val: {
|
||||
ptr: 4
|
||||
}
|
||||
};
|
||||
|
||||
ptr.encode(stream, 10, ctx);
|
||||
ctx.pointerOffset.should.equal(11);
|
||||
ctx.pointers.should.deep.equal([
|
||||
{ type: uint8, val: 10, parent: ctx }
|
||||
]);
|
||||
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should support void pointers', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([1]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const ptr = new Pointer(uint8, 'void');
|
||||
const ctx = {
|
||||
pointerSize: 0,
|
||||
startOffset: 0,
|
||||
pointerOffset: 1,
|
||||
pointers: []
|
||||
};
|
||||
|
||||
ptr.encode(stream, new VoidPointer(uint8, 55), ctx);
|
||||
ctx.pointerOffset.should.equal(2);
|
||||
ctx.pointers.should.deep.equal([
|
||||
{ type: uint8, val: 55, parent: ctx }
|
||||
]);
|
||||
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
return it('should throw if not a void pointer instance', function() {
|
||||
const stream = new EncodeStream;
|
||||
const ptr = new Pointer(uint8, 'void');
|
||||
const ctx = {
|
||||
pointerSize: 0,
|
||||
startOffset: 0,
|
||||
pointerOffset: 1,
|
||||
pointers: []
|
||||
};
|
||||
|
||||
return should.throw(() => ptr.encode(stream, 44, ctx));
|
||||
});
|
||||
});
|
||||
});
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
const {Reserved, uint8, uint16, DecodeStream, EncodeStream} = require('../');
|
||||
const should = require('chai').should();
|
||||
const concat = require('concat-stream');
|
||||
|
||||
describe('Reserved', function() {
|
||||
it('should have a default count of 1', function() {
|
||||
const reserved = new Reserved(uint8);
|
||||
return reserved.size().should.equal(1);
|
||||
});
|
||||
|
||||
it('should allow custom counts and types', function() {
|
||||
const reserved = new Reserved(uint16, 10);
|
||||
return reserved.size().should.equal(20);
|
||||
});
|
||||
|
||||
it('should decode', function() {
|
||||
const stream = new DecodeStream(Buffer.from([0, 0]));
|
||||
const reserved = new Reserved(uint16);
|
||||
should.not.exist(reserved.decode(stream));
|
||||
return stream.pos.should.equal(2);
|
||||
});
|
||||
|
||||
return it('should encode', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
const reserved = new Reserved(uint16);
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from([0, 0]));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
reserved.encode(stream);
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
const {String:StringT, uint8, DecodeStream, EncodeStream} = require('../');
|
||||
const should = require('chai').should();
|
||||
const concat = require('concat-stream');
|
||||
|
||||
describe('String', function() {
|
||||
describe('decode', function() {
|
||||
it('should decode fixed length', function() {
|
||||
const stream = new DecodeStream(Buffer.from('testing'));
|
||||
const string = new StringT(7);
|
||||
return string.decode(stream).should.equal('testing');
|
||||
});
|
||||
|
||||
it('should decode length from parent key', function() {
|
||||
const stream = new DecodeStream(Buffer.from('testing'));
|
||||
const string = new StringT('len');
|
||||
return string.decode(stream, {len: 7}).should.equal('testing');
|
||||
});
|
||||
|
||||
it('should decode length as number before string', function() {
|
||||
const stream = new DecodeStream(Buffer.from('\x07testing'));
|
||||
const string = new StringT(uint8);
|
||||
return string.decode(stream).should.equal('testing');
|
||||
});
|
||||
|
||||
it('should decode utf8', function() {
|
||||
const stream = new DecodeStream(Buffer.from('🍻'));
|
||||
const string = new StringT(4, 'utf8');
|
||||
return string.decode(stream).should.equal('🍻');
|
||||
});
|
||||
|
||||
it('should decode encoding computed from function', function() {
|
||||
const stream = new DecodeStream(Buffer.from('🍻'));
|
||||
const string = new StringT(4, function() { return 'utf8'; });
|
||||
return string.decode(stream).should.equal('🍻');
|
||||
});
|
||||
|
||||
it('should decode null-terminated string and read past terminator', function() {
|
||||
const stream = new DecodeStream(Buffer.from('🍻\x00'));
|
||||
const string = new StringT(null, 'utf8');
|
||||
string.decode(stream).should.equal('🍻');
|
||||
return stream.pos.should.equal(5);
|
||||
});
|
||||
|
||||
return it('should decode remainder of buffer when null-byte missing', function() {
|
||||
const stream = new DecodeStream(Buffer.from('🍻'));
|
||||
const string = new StringT(null, 'utf8');
|
||||
return string.decode(stream).should.equal('🍻');
|
||||
});
|
||||
});
|
||||
|
||||
describe('size', function() {
|
||||
it('should use string length', function() {
|
||||
const string = new StringT(7);
|
||||
return string.size('testing').should.equal(7);
|
||||
});
|
||||
|
||||
it('should use correct encoding', function() {
|
||||
const string = new StringT(10, 'utf8');
|
||||
return string.size('🍻').should.equal(4);
|
||||
});
|
||||
|
||||
it('should use encoding from function', function() {
|
||||
const string = new StringT(10, function() { return 'utf8'; });
|
||||
return string.size('🍻').should.equal(4);
|
||||
});
|
||||
|
||||
it('should add size of length field before string', function() {
|
||||
const string = new StringT(uint8, 'utf8');
|
||||
return string.size('🍻').should.equal(5);
|
||||
});
|
||||
|
||||
it('should work with utf16be encoding', function() {
|
||||
const string = new StringT(10, 'utf16be');
|
||||
return string.size('🍻').should.equal(4);
|
||||
});
|
||||
|
||||
it('should take null-byte into account', function() {
|
||||
const string = new StringT(null, 'utf8');
|
||||
return string.size('🍻').should.equal(5);
|
||||
});
|
||||
|
||||
return it('should use defined length if no value given', function() {
|
||||
const array = new StringT(10);
|
||||
return array.size().should.equal(10);
|
||||
});
|
||||
});
|
||||
|
||||
return describe('encode', function() {
|
||||
it('should encode using string length', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from('testing'));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const string = new StringT(7);
|
||||
string.encode(stream, 'testing');
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should encode length as number before string', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from('\x07testing'));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const string = new StringT(uint8);
|
||||
string.encode(stream, 'testing');
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should encode length as number before string utf8', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from('\x0ctesting 😜', 'utf8'));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const string = new StringT(uint8, 'utf8');
|
||||
string.encode(stream, 'testing 😜');
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should encode utf8', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from('🍻'));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const string = new StringT(4, 'utf8');
|
||||
string.encode(stream, '🍻');
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should encode encoding computed from function', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from('🍻'));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const string = new StringT(4, function() { return 'utf8'; });
|
||||
string.encode(stream, '🍻');
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
return it('should encode null-terminated string', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from('🍻\x00'));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const string = new StringT(null, 'utf8');
|
||||
string.encode(stream, '🍻');
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
const {Struct, String:StringT, Pointer, uint8, DecodeStream, EncodeStream} = require('../');
|
||||
const should = require('chai').should();
|
||||
const concat = require('concat-stream');
|
||||
|
||||
describe('Struct', function() {
|
||||
describe('decode', function() {
|
||||
it('should decode into an object', function() {
|
||||
const stream = new DecodeStream(Buffer.from('\x05devon\x15'));
|
||||
const struct = new Struct({
|
||||
name: new StringT(uint8),
|
||||
age: uint8
|
||||
});
|
||||
|
||||
return struct.decode(stream).should.deep.equal({
|
||||
name: 'devon',
|
||||
age: 21
|
||||
});
|
||||
});
|
||||
|
||||
it('should support process hook', function() {
|
||||
const stream = new DecodeStream(Buffer.from('\x05devon\x20'));
|
||||
const struct = new Struct({
|
||||
name: new StringT(uint8),
|
||||
age: uint8
|
||||
});
|
||||
|
||||
struct.process = function() {
|
||||
return this.canDrink = this.age >= 21;
|
||||
};
|
||||
|
||||
return struct.decode(stream).should.deep.equal({
|
||||
name: 'devon',
|
||||
age: 32,
|
||||
canDrink: true
|
||||
});
|
||||
});
|
||||
|
||||
return it('should support function keys', function() {
|
||||
const stream = new DecodeStream(Buffer.from('\x05devon\x20'));
|
||||
const struct = new Struct({
|
||||
name: new StringT(uint8),
|
||||
age: uint8,
|
||||
canDrink() { return this.age >= 21; }
|
||||
});
|
||||
|
||||
return struct.decode(stream).should.deep.equal({
|
||||
name: 'devon',
|
||||
age: 32,
|
||||
canDrink: true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('size', function() {
|
||||
it('should compute the correct size', function() {
|
||||
const struct = new Struct({
|
||||
name: new StringT(uint8),
|
||||
age: uint8
|
||||
});
|
||||
|
||||
return struct.size({name: 'devon', age: 21}).should.equal(7);
|
||||
});
|
||||
|
||||
it('should compute the correct size with pointers', function() {
|
||||
const struct = new Struct({
|
||||
name: new StringT(uint8),
|
||||
age: uint8,
|
||||
ptr: new Pointer(uint8, new StringT(uint8))
|
||||
});
|
||||
|
||||
const size = struct.size({
|
||||
name: 'devon',
|
||||
age: 21,
|
||||
ptr: 'hello'
|
||||
});
|
||||
|
||||
return size.should.equal(14);
|
||||
});
|
||||
|
||||
it('should get the correct size when no value is given', function() {
|
||||
const struct = new Struct({
|
||||
name: new StringT(4),
|
||||
age: uint8
|
||||
});
|
||||
|
||||
return struct.size().should.equal(5);
|
||||
});
|
||||
|
||||
return it('should throw when getting non-fixed length size and no value is given', function() {
|
||||
const struct = new Struct({
|
||||
name: new StringT(uint8),
|
||||
age: uint8
|
||||
});
|
||||
|
||||
return should.throw(() => struct.size()
|
||||
, /not a fixed size/i);
|
||||
});
|
||||
});
|
||||
|
||||
return describe('encode', function() {
|
||||
it('should encode objects to buffers', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from('\x05devon\x15'));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const struct = new Struct({
|
||||
name: new StringT(uint8),
|
||||
age: uint8
|
||||
});
|
||||
|
||||
struct.encode(stream, {
|
||||
name: 'devon',
|
||||
age: 21
|
||||
}
|
||||
);
|
||||
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should support preEncode hook', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from('\x05devon\x15'));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const struct = new Struct({
|
||||
nameLength: uint8,
|
||||
name: new StringT('nameLength'),
|
||||
age: uint8
|
||||
});
|
||||
|
||||
struct.preEncode = function() {
|
||||
return this.nameLength = this.name.length;
|
||||
};
|
||||
|
||||
struct.encode(stream, {
|
||||
name: 'devon',
|
||||
age: 21
|
||||
}
|
||||
);
|
||||
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
return it('should encode pointer data after structure', function(done) {
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from('\x05devon\x15\x08\x05hello'));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
const struct = new Struct({
|
||||
name: new StringT(uint8),
|
||||
age: uint8,
|
||||
ptr: new Pointer(uint8, new StringT(uint8))
|
||||
});
|
||||
|
||||
struct.encode(stream, {
|
||||
name: 'devon',
|
||||
age: 21,
|
||||
ptr: 'hello'
|
||||
}
|
||||
);
|
||||
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+533
@@ -0,0 +1,533 @@
|
||||
const {VersionedStruct, String:StringT, Pointer, uint8, DecodeStream, EncodeStream} = require('../');
|
||||
const should = require('chai').should();
|
||||
const concat = require('concat-stream');
|
||||
|
||||
describe('VersionedStruct', function() {
|
||||
describe('decode', function() {
|
||||
it('should get version from number type', function() {
|
||||
const struct = new VersionedStruct(uint8, {
|
||||
0: {
|
||||
name: new StringT(uint8, 'ascii'),
|
||||
age: uint8
|
||||
},
|
||||
1: {
|
||||
name: new StringT(uint8, 'utf8'),
|
||||
age: uint8,
|
||||
gender: uint8
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let stream = new DecodeStream(Buffer.from('\x00\x05devon\x15'));
|
||||
struct.decode(stream).should.deep.equal({
|
||||
version: 0,
|
||||
name: 'devon',
|
||||
age: 21
|
||||
});
|
||||
|
||||
stream = new DecodeStream(Buffer.from('\x01\x0adevon 👍\x15\x00', 'utf8'));
|
||||
return struct.decode(stream).should.deep.equal({
|
||||
version: 1,
|
||||
name: 'devon 👍',
|
||||
age: 21,
|
||||
gender: 0
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw for unknown version', function() {
|
||||
const struct = new VersionedStruct(uint8, {
|
||||
0: {
|
||||
name: new StringT(uint8, 'ascii'),
|
||||
age: uint8
|
||||
},
|
||||
1: {
|
||||
name: new StringT(uint8, 'utf8'),
|
||||
age: uint8,
|
||||
gender: uint8
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const stream = new DecodeStream(Buffer.from('\x05\x05devon\x15'));
|
||||
return should.throw(() => struct.decode(stream));
|
||||
});
|
||||
|
||||
it('should support common header block', function() {
|
||||
const struct = new VersionedStruct(uint8, {
|
||||
header: {
|
||||
age: uint8,
|
||||
alive: uint8
|
||||
},
|
||||
0: {
|
||||
name: new StringT(uint8, 'ascii')
|
||||
},
|
||||
1: {
|
||||
name: new StringT(uint8, 'utf8'),
|
||||
gender: uint8
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let stream = new DecodeStream(Buffer.from('\x00\x15\x01\x05devon'));
|
||||
struct.decode(stream).should.deep.equal({
|
||||
version: 0,
|
||||
age: 21,
|
||||
alive: 1,
|
||||
name: 'devon'
|
||||
});
|
||||
|
||||
stream = new DecodeStream(Buffer.from('\x01\x15\x01\x0adevon 👍\x00', 'utf8'));
|
||||
return struct.decode(stream).should.deep.equal({
|
||||
version: 1,
|
||||
age: 21,
|
||||
alive: 1,
|
||||
name: 'devon 👍',
|
||||
gender: 0
|
||||
});
|
||||
});
|
||||
|
||||
it('should support parent version key', function() {
|
||||
const struct = new VersionedStruct('version', {
|
||||
0: {
|
||||
name: new StringT(uint8, 'ascii'),
|
||||
age: uint8
|
||||
},
|
||||
1: {
|
||||
name: new StringT(uint8, 'utf8'),
|
||||
age: uint8,
|
||||
gender: uint8
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let stream = new DecodeStream(Buffer.from('\x05devon\x15'));
|
||||
struct.decode(stream, {version: 0}).should.deep.equal({
|
||||
version: 0,
|
||||
name: 'devon',
|
||||
age: 21
|
||||
});
|
||||
|
||||
stream = new DecodeStream(Buffer.from('\x0adevon 👍\x15\x00', 'utf8'));
|
||||
return struct.decode(stream, {version: 1}).should.deep.equal({
|
||||
version: 1,
|
||||
name: 'devon 👍',
|
||||
age: 21,
|
||||
gender: 0
|
||||
});
|
||||
});
|
||||
|
||||
it('should support parent version nested key', function() {
|
||||
const struct = new VersionedStruct('obj.version', {
|
||||
0: {
|
||||
name: new StringT(uint8, 'ascii'),
|
||||
age: uint8
|
||||
},
|
||||
1: {
|
||||
name: new StringT(uint8, 'utf8'),
|
||||
age: uint8,
|
||||
gender: uint8
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let stream = new DecodeStream(Buffer.from('\x05devon\x15'));
|
||||
struct.decode(stream, {obj: {version: 0}}).should.deep.equal({
|
||||
version: 0,
|
||||
name: 'devon',
|
||||
age: 21
|
||||
});
|
||||
|
||||
stream = new DecodeStream(Buffer.from('\x0adevon 👍\x15\x00', 'utf8'));
|
||||
return struct.decode(stream, {obj: {version: 1}}).should.deep.equal({
|
||||
version: 1,
|
||||
name: 'devon 👍',
|
||||
age: 21,
|
||||
gender: 0
|
||||
});
|
||||
});
|
||||
|
||||
it('should support sub versioned structs', function() {
|
||||
const struct = new VersionedStruct(uint8, {
|
||||
0: {
|
||||
name: new StringT(uint8, 'ascii'),
|
||||
age: uint8
|
||||
},
|
||||
1: new VersionedStruct(uint8, {
|
||||
0: {
|
||||
name: new StringT(uint8)
|
||||
},
|
||||
1: {
|
||||
name: new StringT(uint8),
|
||||
isDesert: uint8
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
);
|
||||
|
||||
let stream = new DecodeStream(Buffer.from('\x00\x05devon\x15'));
|
||||
struct.decode(stream, {version: 0}).should.deep.equal({
|
||||
version: 0,
|
||||
name: 'devon',
|
||||
age: 21
|
||||
});
|
||||
|
||||
stream = new DecodeStream(Buffer.from('\x01\x00\x05pasta'));
|
||||
struct.decode(stream, {version: 0}).should.deep.equal({
|
||||
version: 0,
|
||||
name: 'pasta'
|
||||
});
|
||||
|
||||
stream = new DecodeStream(Buffer.from('\x01\x01\x09ice cream\x01'));
|
||||
return struct.decode(stream, {version: 0}).should.deep.equal({
|
||||
version: 1,
|
||||
name: 'ice cream',
|
||||
isDesert: 1
|
||||
});
|
||||
});
|
||||
|
||||
return it('should support process hook', function() {
|
||||
const struct = new VersionedStruct(uint8, {
|
||||
0: {
|
||||
name: new StringT(uint8, 'ascii'),
|
||||
age: uint8
|
||||
},
|
||||
1: {
|
||||
name: new StringT(uint8, 'utf8'),
|
||||
age: uint8,
|
||||
gender: uint8
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
struct.process = function() {
|
||||
return this.processed = true;
|
||||
};
|
||||
|
||||
const stream = new DecodeStream(Buffer.from('\x00\x05devon\x15'));
|
||||
return struct.decode(stream).should.deep.equal({
|
||||
version: 0,
|
||||
name: 'devon',
|
||||
age: 21,
|
||||
processed: true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('size', function() {
|
||||
it('should compute the correct size', function() {
|
||||
const struct = new VersionedStruct(uint8, {
|
||||
0: {
|
||||
name: new StringT(uint8, 'ascii'),
|
||||
age: uint8
|
||||
},
|
||||
1: {
|
||||
name: new StringT(uint8, 'utf8'),
|
||||
age: uint8,
|
||||
gender: uint8
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let size = struct.size({
|
||||
version: 0,
|
||||
name: 'devon',
|
||||
age: 21
|
||||
});
|
||||
|
||||
size.should.equal(8);
|
||||
|
||||
size = struct.size({
|
||||
version: 1,
|
||||
name: 'devon 👍',
|
||||
age: 21,
|
||||
gender: 0
|
||||
});
|
||||
|
||||
return size.should.equal(14);
|
||||
});
|
||||
|
||||
it('should throw for unknown version', function() {
|
||||
const struct = new VersionedStruct(uint8, {
|
||||
0: {
|
||||
name: new StringT(uint8, 'ascii'),
|
||||
age: uint8
|
||||
},
|
||||
1: {
|
||||
name: new StringT(uint8, 'utf8'),
|
||||
age: uint8,
|
||||
gender: uint8
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return should.throw(() =>
|
||||
struct.size({
|
||||
version: 5,
|
||||
name: 'devon',
|
||||
age: 21
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should support common header block', function() {
|
||||
const struct = new VersionedStruct(uint8, {
|
||||
header: {
|
||||
age: uint8,
|
||||
alive: uint8
|
||||
},
|
||||
0: {
|
||||
name: new StringT(uint8, 'ascii')
|
||||
},
|
||||
1: {
|
||||
name: new StringT(uint8, 'utf8'),
|
||||
gender: uint8
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let size = struct.size({
|
||||
version: 0,
|
||||
age: 21,
|
||||
alive: 1,
|
||||
name: 'devon'
|
||||
});
|
||||
|
||||
size.should.equal(9);
|
||||
|
||||
size = struct.size({
|
||||
version: 1,
|
||||
age: 21,
|
||||
alive: 1,
|
||||
name: 'devon 👍',
|
||||
gender: 0
|
||||
});
|
||||
|
||||
return size.should.equal(15);
|
||||
});
|
||||
|
||||
it('should compute the correct size with pointers', function() {
|
||||
const struct = new VersionedStruct(uint8, {
|
||||
0: {
|
||||
name: new StringT(uint8, 'ascii'),
|
||||
age: uint8
|
||||
},
|
||||
1: {
|
||||
name: new StringT(uint8, 'utf8'),
|
||||
age: uint8,
|
||||
ptr: new Pointer(uint8, new StringT(uint8))
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const size = struct.size({
|
||||
version: 1,
|
||||
name: 'devon',
|
||||
age: 21,
|
||||
ptr: 'hello'
|
||||
});
|
||||
|
||||
return size.should.equal(15);
|
||||
});
|
||||
|
||||
return it('should throw if no value is given', function() {
|
||||
const struct = new VersionedStruct(uint8, {
|
||||
0: {
|
||||
name: new StringT(4, 'ascii'),
|
||||
age: uint8
|
||||
},
|
||||
1: {
|
||||
name: new StringT(4, 'utf8'),
|
||||
age: uint8,
|
||||
gender: uint8
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return should.throw(() => struct.size()
|
||||
, /not a fixed size/i);
|
||||
});
|
||||
});
|
||||
|
||||
return describe('encode', function() {
|
||||
it('should encode objects to buffers', function(done) {
|
||||
const struct = new VersionedStruct(uint8, {
|
||||
0: {
|
||||
name: new StringT(uint8, 'ascii'),
|
||||
age: uint8
|
||||
},
|
||||
1: {
|
||||
name: new StringT(uint8, 'utf8'),
|
||||
age: uint8,
|
||||
gender: uint8
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from('\x00\x05devon\x15\x01\x0adevon 👍\x15\x00', 'utf8'));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
struct.encode(stream, {
|
||||
version: 0,
|
||||
name: 'devon',
|
||||
age: 21
|
||||
}
|
||||
);
|
||||
|
||||
struct.encode(stream, {
|
||||
version: 1,
|
||||
name: 'devon 👍',
|
||||
age: 21,
|
||||
gender: 0
|
||||
}
|
||||
);
|
||||
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should throw for unknown version', function() {
|
||||
const struct = new VersionedStruct(uint8, {
|
||||
0: {
|
||||
name: new StringT(uint8, 'ascii'),
|
||||
age: uint8
|
||||
},
|
||||
1: {
|
||||
name: new StringT(uint8, 'utf8'),
|
||||
age: uint8,
|
||||
gender: uint8
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const stream = new EncodeStream;
|
||||
return should.throw(() =>
|
||||
struct.encode(stream, {
|
||||
version: 5,
|
||||
name: 'devon',
|
||||
age: 21
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it('should support common header block', function(done) {
|
||||
const struct = new VersionedStruct(uint8, {
|
||||
header: {
|
||||
age: uint8,
|
||||
alive: uint8
|
||||
},
|
||||
0: {
|
||||
name: new StringT(uint8, 'ascii')
|
||||
},
|
||||
1: {
|
||||
name: new StringT(uint8, 'utf8'),
|
||||
gender: uint8
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from('\x00\x15\x01\x05devon\x01\x15\x01\x0adevon 👍\x00', 'utf8'));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
struct.encode(stream, {
|
||||
version: 0,
|
||||
age: 21,
|
||||
alive: 1,
|
||||
name: 'devon'
|
||||
}
|
||||
);
|
||||
|
||||
struct.encode(stream, {
|
||||
version: 1,
|
||||
age: 21,
|
||||
alive: 1,
|
||||
name: 'devon 👍',
|
||||
gender: 0
|
||||
}
|
||||
);
|
||||
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
it('should encode pointer data after structure', function(done) {
|
||||
const struct = new VersionedStruct(uint8, {
|
||||
0: {
|
||||
name: new StringT(uint8, 'ascii'),
|
||||
age: uint8
|
||||
},
|
||||
1: {
|
||||
name: new StringT(uint8, 'utf8'),
|
||||
age: uint8,
|
||||
ptr: new Pointer(uint8, new StringT(uint8))
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from('\x01\x05devon\x15\x09\x05hello', 'utf8'));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
struct.encode(stream, {
|
||||
version: 1,
|
||||
name: 'devon',
|
||||
age: 21,
|
||||
ptr: 'hello'
|
||||
}
|
||||
);
|
||||
|
||||
return stream.end();
|
||||
});
|
||||
|
||||
return it('should support preEncode hook', function(done) {
|
||||
const struct = new VersionedStruct(uint8, {
|
||||
0: {
|
||||
name: new StringT(uint8, 'ascii'),
|
||||
age: uint8
|
||||
},
|
||||
1: {
|
||||
name: new StringT(uint8, 'utf8'),
|
||||
age: uint8,
|
||||
gender: uint8
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
struct.preEncode = function() {
|
||||
return this.version = (this.gender != null) ? 1 : 0;
|
||||
};
|
||||
|
||||
const stream = new EncodeStream;
|
||||
stream.pipe(concat(function(buf) {
|
||||
buf.should.deep.equal(Buffer.from('\x00\x05devon\x15\x01\x0adevon 👍\x15\x00', 'utf8'));
|
||||
return done();
|
||||
})
|
||||
);
|
||||
|
||||
struct.encode(stream, {
|
||||
name: 'devon',
|
||||
age: 21
|
||||
}
|
||||
);
|
||||
|
||||
struct.encode(stream, {
|
||||
name: 'devon 👍',
|
||||
age: 21,
|
||||
gender: 0
|
||||
}
|
||||
);
|
||||
|
||||
return stream.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user