awestruct
Library for reading complex binary Buffer structures into objects in Node.js
Usage Example
const Struct = const t = Structtypes // https://github.com/goto-bus-stop/genie-slp/const slpHeader = const headerContents = // → { version: '1.00', ... }
API
Struct(descriptor)
Creates a new Struct
function that reads from Buffer
s according to the described format.
descriptor
is a Struct Descriptor. For example:
var buffer = Buffervar t = Structtypesvar struct = //→ { a: 8208, b: 48 }
A struct descriptor is an array of fields. A field can either be an array with two elements, [name, type]
, or an unnamed raw type
.
Unnamed types are useful if there is some padding you need to skip.
If an unnamed type reads another struct, it is merged into the current one. For example:
var struct =
Now, if the buffer
's first byte is zero, struct(buffer)
will return an object like:
needToReadThing: 0
But if it is nonzero, struct(buffer)
will return an object of this shape:
needToReadThing: 1 value1: 43 value2: 76
struct(buffer, ?parent)
Instances of Struct()
can be called directly to read data from buffers. The first parameter is the
Buffer you want to use. The second (optional) parameter is a parent object for the struct, as shown in Value Paths.
struct.decode(buffer)
abstract-encoding
compatible.
struct.encode(value[, buffer][, start = 0])
Encode value
into a buffer
. Start writing at offset start
. If no buffer
is given, awestruct allocates one.
abstract-encoding
compatible.
struct.encodingLength(value)
Return the size in bytes that would be necessary to encode value
.
abstract-encoding
compatible.
Custom types: Struct.Type(type)
Creates a Struct type object. type
is an object:
var myType = Struct
Custom types can be used like so:
var myStruct = //→ { builtinType: 5, customType: 5000 }
Struct.Type#mapRead(function)
Creates a new type that applies the given transform function when reading values.
var int32 = Structtypesint32var myStruct = //→ { a: 5, b: 10 }
Builtin Types
Number types
These just map straight to the relevant Buffer().read*()
methods. Number types read Little-Endian by default, append -be
if you're dealing with Big-Endian data.
- int8, uint8
- int16, uint16, int16be, uint16be
- int32, uint32, int32be, uint32be
- float, floatbe
- double, doublebe
Other common types
string(n, encoding = 'utf8')
→ Creates a type that decodesn
bytes into a string with the given encoding (defaults to 'utf8')array(n, type)
→ Creates a type that readsn
items of Struct.Typetype
into ann
-length array.dynstring(ntype, encoding = 'utf8')
→ Creates a type that first reads the lengthn
using the type in the first parameter, then decodesn
bytes into a string with the given encoding.dynarray(ntype, type)
→ Creates a type that first reads the lengthn
using the type in the first parameter, then readsn
items of typetype
into ann
-length array.skip(n)
→ Creates a type that skipsn
bytes and returnsundefined
.
The n
parameter in each of those is a Value Path.
Conditional types
-
if(condition, type)
→ Creates a type that decodestype
if thecondition
Value Path is truthy.if()
types also have an.else(type)
method, to specify atype
to decode if thecondition
is falsy.t
Value Paths
Value paths are used to pass values to some type readers, particularly lengths. Value paths can be raw numbers, or depend on other values in the struct.
Value paths take three forms:
- Numbers: produces the given number.
- Strings: looks up the value at the given path.
- Functions: takes the return value of the function.
Buffer → len: 3 string: 'hi!'
A string path can be a plain property name, or a bunch of property names separated by dots ('child.struct.key') to descend into child structs, and can also start with '../' to look back into a "parent" struct.
Functions will be called with the current (possibly incomplete) struct in the first parameter: