structron
Version:
A reader, writer and validator for binary data structures
222 lines (172 loc) • 7.28 kB
Markdown
is a reader and writer for binary data. It parses buffers from in code defined models and returns the result as object, or creates buffers from objects.
Install using [npm](https://www.npmjs.com/)
```
npm i structron
```
Example of a custom image format with the following structure:
```
header:
int magicNumber (Magic number of the file)
Dimensions size (Custom Data-Type of 2 ints)
int pixelOffset (Start address of the pixel array)
int pixelNumber (Pixel array size)
int namePosition (Pointer to a null-terminated string)
int unused[2] (2 unused integer reserved for later use)
pixel:
rgb565 color (Color values)
byte alpha (Opacity)
```
Preparing the structure:
```js
const Struct = require('structron');
// --- First we need to define the custom datatypes ---
// Define via another struct
const Dimensions = new Struct()
.addMember(Struct.TYPES.INT, "width")
.addMember(Struct.TYPES.INT, "height");
// Define via custom function
const rgb565 = {
read(buffer, offset) {
let short = buffer.readUInt16LE(offset);
return {
r: (short & 0b1111100000000000) >> 11,
g: (short & 0b0000011111100000) >> 5,
b: (short & 0b0000000000011111)
}
},
SIZE: 2 // Size in bytes
};
const Pixel = new Struct()
.addMember(rgb565, "color")
.addMember(Struct.TYPES.BYTE, "alpha");
// --- Then we define our file header ---
const Image = new Struct()
.addMember(Struct.TYPES.INT, "magicNumber")
.addMember(Dimensions, "size")
.addMember(Struct.TYPES.INT, "pixelOffset")
.addMember(Struct.TYPES.INT, "pixelNumber")
.addMember(Struct.TYPES.INT, "nameIndex")
.addMember(Struct.TYPES.SKIP(8), "unused")
.addReference(Struct.TYPES.NULL_TERMINATED_STRING('ASCII'), "name", "nameIndex")
.addArray(Pixel, "pixels", "pixelOffset", "pixelNumber")
.addRule(Struct.RULES.EQUAL("magicNumber", 604051865));
```
Reading with structron:
```js
const data = Buffer.from("mRkBJAQAAAAEAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAhKjM8RU5XYGlye4SNlp+osbrDzNXe5/D5AwwVHicwOUJLVF1mb3iBipOcpa63wMlUaW5hAA==", "base64");
let ctx = Image.readContext(data);
// Check for errors in the input data
if (!ctx.hasErrors()) {
// Log the result
console.log(ctx.data);
}
```
`ctx.data` will then look like this:
```json
{
"magicNumber": 604051865,
"size": { "width": 4, "height": 4 },
"pixelOffset": 32,
"pixelNumber": 16,
"nameIndex": 80,
"unused": null,
"pixels": [
{ "color": { "r": 5, "g": 17, "b": 1 }, "alpha": 51 },
{ "color": { "r": 8, "g": 41, "b": 28 }, "alpha": 78 },
{ "color": { "r": 12, "g": 2, "b": 23 }, "alpha": 105 },
{ "color": { "r": 15, "g": 27, "b": 18 }, "alpha": 132 },
{ "color": { "r": 18, "g": 52, "b": 13 }, "alpha": 159 },
{ "color": { "r": 22, "g": 13, "b": 8 }, "alpha": 186 },
{ "color": { "r": 25, "g": 38, "b": 3 }, "alpha": 213 },
{ "color": { "r": 28, "g": 62, "b": 30 }, "alpha": 240 },
{ "color": { "r": 0, "g": 31, "b": 25 }, "alpha": 12 },
{ "color": { "r": 3, "g": 48, "b": 21 }, "alpha": 39 },
{ "color": { "r": 7, "g": 9, "b": 16 }, "alpha": 66 },
{ "color": { "r": 10, "g": 34, "b": 11 }, "alpha": 93 },
{ "color": { "r": 13, "g": 59, "b": 6 }, "alpha": 120 },
{ "color": { "r": 17, "g": 20, "b": 1 }, "alpha": 147 },
{ "color": { "r": 20, "g": 44, "b": 28 }, "alpha": 174 },
{ "color": { "r": 24, "g": 5, "b": 23 }, "alpha": 201 }
],
"name": "Tina"
}
```
Writing with structron:
```js
let object = {
magicNumber: 604051865,
size: { width: 1, height: 1},
pixels: [{color: {r: 2, g: 3, b: 2}, alpha: 255}],
name: "One Pixel"
};
let ctx = Image.write(object);
// ctx.buffer contains the exported data!
ctx.buffer().toString("base64"); // => "mRkBJAEAAAABAAAAIAAAAAEAAAAjAAAAAAAAAAAAAABiEP9PbmUgUGl4ZWwA"
```
Reads data from a buffer from a specific address on. Returns the data as object.
Reads data from a buffer from a specific address on. Returns an object containing additional data about the import like how many bytes were actually read. The returned object also holds the imported data.
`options.monitorUsage`: Keep track of what bytes were read.
`options.hideReferenceValues`: Remove reference fields (array offset, array length and reference offset fields) from output data.
### `struct.validate(buffer, offset)`
Returns an boolean if the data matches the struct.
### `struct.addMember(type, name)`
Adds an member to the struct definition.
### `struct.addArray(type, name, index, length, relative)`
`type` is the type of the elements in the array. To work, the size of the elements need to be correct.
`name` is the name of the member that will be added to the output object containing the array.
`index` is the start address of the array. If a string is given, it will read the value from another member with that name.
`length` is the number of elements in the array. (Not the size in bytes!). If a string is given, it will read the value from another member with that name.
When `relative` is set to true, the array will be read from the index + the structs address.
### `struct.addReference(type, name, index, relative)`
Circular references are possible.
`type` is the type of the object to reference.
`name` is the name of the member that will be added to the output object.
`index` is the start address of the element to load. If a string is given, it will read the value from another member with that name.
When `relative` is set to true, the index is relative to the start of the parent struct.
### `struct.addRule(rule)`
Adds a rule. A rule is like a test. If it is not successful, an error will be added to the report.
### `struct.addStatic(name, value)`
Adds a static value. These will show in the output data, but will not be written to buffers when writing.
### `struct.write(object, context, offset)`
Writes a struct to a buffer. Returns a new context if none is given.
## Inbuilt Types
These inbuilt types are accessible with `Structron.TYPES.`
### `INT_LE`
4 byte signed little-endian Integer
### `INT_BE`
4 byte signed big-endian Integer
### `UINT_LE`
4 byte unsigned little-endian Integer
### `UINT_BE`
2 byte unsigned big-endian Integer
### `SHORT_LE`
2 byte signed little-endian Integer
### `SHORT_BE`
2 byte signed big-endian Integer
### `USHORT_LE`
2 byte signed little-endian Integer
### `USHORT_BE`
2 byte signed big-endian Integer
### `BYTE`
Unsigned 1 byte
### `FLOAT_LE`
4 byte little-endian Float
### `FLOAT_BE`
4 byte big-endian Float
### `CHAR`
1 ASCII character
### `STRING(length, encoding)`
String with variable length.
Supports all encodings from `buffer.toString()`.
### `NULL_TERMINATED_STRING(encoding)`
Null terminated string. Reads a string from an address until a null byte is hit.
Can't be used inside a Struct. Only as reference.
Supports all encodings from `buffer.toString()`.
### `SKIP(length)`
Skips a given amount of bytes.
Structron