Dissolve
Parse and consume binary streams with a neat DSL.
Overview
Dissolve allows you to parse packed binary data into numbers, buffers, strings
and more*! With a simple syntax inspired by node-binary
and a solid, minimal implementation, you can be up and running in no time.
(* implementing "more" is left as an exercise to the reader)
If you want to produce binary data, might I suggest concentrate?
Features
- Accurate handling of [u]int{8,16,32} numbers in both signed and unsigned
variants using fast, built-in Buffer
methods
- Fast approximation of [u]int64 numbers in signed and unsigned variants
- Extendable base class for building your own parsers and implementing
custom types
- Tiny (~250 LoC) implementation, allowing for easy debugging
Installation
Available via npm:
$ npm install dissolve
Or via git:
$ git clone git://github.com/deoxxa/dissolve.git node_modules/dissolve
Usage
Also see example.js,
example-complex.js
and example-loop.js.
#!/usr/bin/env node
var Dissolve = require("./index");
var parser = Dissolve().loop(function(end) {
this.uint8("id").tap(function() {
if (this.vars.id === 0x01) {
this.uint16be("a").uint16be("b");
} else if (this.vars.id === 0x02) {
this.uint32be("x").uint32be("y");
}
}).tap(function() {
this.push(this.vars);
this.vars = {};
});
});
parser.on("readable", function() {
var e;
while (e = parser.read()) {
console.log(e);
}
});
parser.write(new Buffer([0x01, 0x00, 0x02, 0x00, 0x03]));
parser.write(new Buffer([0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05]));
parser.write(new Buffer([0x01]));
parser.write(new Buffer([0x00, 0x02, 0x00]));
parser.write(new Buffer([0x03]));
Methods
All parser methods are chainable and return the same parser instance they were
called on.
Tap
tap(name, callback)
This method allows you to "tap" into the parser at an arbitrary point. The
callback will be called bound to the parser instance, so you can use parser
methods on this
. Any additional parser steps you introduce inside the callback
will be executed before any existing steps that are already scheduled to run
after the tap
call.
If you provide a name
parameter, all actions performed in the callback will be
applied to a child object that will be put into a new property named after
name
. Note that in the callback, even if you provide a name
parameter, you
can still pretend you were in the outer "scope" because of some prototype
trickery done with the vars
object under the hood. You don't need to worry
about that too much, the examples should make it a bit clearer.
Loop
loop(name, callback)
This method is like tap
except that the callback is called over and over until
signalled to stop. You do this by calling the end
function that's provided as
the first argument to your callback. When you call the end
function, you can
provide an optional truthy/non-truthy flag to tell Dissolve to ignore the result
of the iteration of the loop where end
was called. This is useful if you are
reading until a null entry or similar.
If you provide a name
parameter, a new array will be placed into a property
named for that parameter, and after each iteration of the loop, any new values
will be appended to the array as an object. As with the name
stuff on tap
,
the examples will make that explanation a lot clearer.
The same semantics for job ordering and "scoping" apply as for tap
.
Basic Parsing Methods
For each basic parsing method, the name
value is the key under which the value
will be attached to this.vars
.
Buffer/String Methods
For these methods, the length
parameter tells the parser how many bytes to
pull out. If it's a string, it will be assumed that it is the name of a
previously-set this.vars
entry. If it's a number, it will be used as-is.
buffer(name, length)
- binary slice
string(name, length)
- utf8 string slice
Numeric Methods
int8(name)
- signed 8 bit integer
sint8(name)
- signed 8 bit integer
uint8(name)
- unsigned 8 bit integer
int16(name)
- signed, little endian 16 bit integer
int16le(name)
- signed, little endian 16 bit integer
int16be(name)
- signed, big endian 16 bit integer
sint16(name)
- signed, little endian 16 bit integer
sint16le(name)
- signed, little endian 16 bit integer
sint16be(name)
- signed, big endian 16 bit integer
uint16(name)
- unsigned, little endian 16 bit integer
uint16le(name)
- unsigned, little endian 16 bit integer
uint16be(name)
- unsigned, big endian 16 bit integer
int32(name)
- signed, little endian 32 bit integer
int32le(name)
- signed, little endian 32 bit integer
int32be(name)
- signed, big endian 32 bit integer
sint32(name)
- signed, little endian 32 bit integer
sint32le(name)
- signed, little endian 32 bit integer
sint32be(name)
- signed, big endian 32 bit integer
uint32(name)
- unsigned, little endian 32 bit integer
uint32le(name)
- unsigned, little endian 32 bit integer
uint32be(name)
- unsigned, big endian 32 bit integer
int64(name)
- signed, little endian 64 bit integer
int64le(name)
- signed, little endian 64 bit integer
int64be(name)
- signed, big endian 64 bit integer
sint64(name)
- signed, little endian 64 bit integer
sint64le(name)
- signed, little endian 64 bit integer
sint64be(name)
- signed, big endian 64 bit integer
uint64(name)
- unsigned, little endian 64 bit integer
uint64le(name)
- unsigned, little endian 64 bit integer
uint64be(name)
- unsigned, big endian 64 bit integer
floatbe(data)
- big endian 32 bit float
floatle(data)
- little endian 32 bit float
doublebe(data)
- big endian 64 bit double
doublele(data)
- little endian 64 bit double
License
3-clause BSD. A copy is included with the source.
Contact