UNPKG

structron

Version:

A reader, writer and validator for binary data structures

222 lines (172 loc) 7.28 kB
# Structron Structron 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. ## Installation Install using [npm](https://www.npmjs.com/) ``` npm i structron ``` ## Example 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" ``` ## Methods ### `struct.read(buffer, offset)` Reads data from a buffer from a specific address on. Returns the data as object. ### `struct.readContext(buffer, offset, options)` 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.