An open API service indexing awesome lists of open source software.

https://github.com/wiiseguy/node-structor

Convert binary file data to JavaScript objects
https://github.com/wiiseguy/node-structor

Last synced: 5 months ago
JSON representation

Convert binary file data to JavaScript objects

Awesome Lists containing this project

README

          

# node-structor

> Convert binary file data to JavaScript objects

## Installation

`npm i node-structor`

## Usage

In the example below we will be reading a list of people from a binary source.

```js
const fs = require('fs')
const Structor = require('node-structor')

const structDef = {
numPersons: {
$format: 'byte',
$ignore: true
},
persons: {
$repeat: 'numPersons',
$format: {
firstName: 'string7',
lastName: 'string7',
address: {
city: 'string7',
street: 'string7',
number: 'uint16',
zipCode: 'string7'
},
numHobbies: {
$ignore: true,
$format: 'byte'
},
hobbies: {
$format: 'string7',
$repeat: 'numHobbies'
}
}
}
}

let result = Structor.readStruct(structDef, fs.readFileSync('./examples/people.dat'))

console.log(result)
```

> Note that `'string7'` is used - this denotes a string that is prepended by the length of that string. [Reference]().

Running this will log the following:

```js
{
persons: [
{
firstName: 'John',
lastName: 'A',
address: {
city: 'New York',
street: '1st Ave.',
number: 1165,
zipCode: '10065'
},
hobbies: ['eating', 'coding', 'walking']
},
{
firstName: 'Betty',
lastName: 'B',
address: {
city: 'York',
street: 'Bridge St.',
number: 1,
zipCode: 'YO1 6DD'
},
hobbies: []
}
]
}
```

## API

### `readStruct(structDef, buffer, [options])`

Reads the binary data from the buffer according to the structure definition. Returns a JavaScript object.

### `writeStruct(obj, structDef, data, [options])`

Writes the an object to a binary buffer according to the structure definition. Returns the number of bytes written.

### `sizeOf(structDef, [size = 4096])`

Calculates the size of the binary data that would be written according to the structure definition. The size parameter is optional and defaults to 4096; it references the size of the buffer that will be allocated to be able to calculate the size of the data.

## Types

| Type | Description |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `byte` | Unsigned byte (0 to 255) |
| `uint8` | Unsigned byte (0 to 255) |
| `sbyte` | Signed byte (-128 to 127) |
| `int8` | Signed byte (-128 to 127) |
| `uint16` | 16-bit unsigned integer (0 to 65,535) |
| `int16` | 16-bit signed integer (-32,768 to 32,767) |
| `uint32` | 32-bit unsigned integer (0 to 4,294,967,295) |
| `int32` | 32-bit signed integer (-2,147,483,648 to 2,147,483,647) |
| `uint64` | 64-bit unsigned integer (read as `BigInt`) |
| `int64` | 64-bit signed integer (read as `BigInt`) |
| `char_*` | A string of characters with its length defined by the `*`. e.g. `char_28`. When reading, the null (0) bytes are removed from the end. When writing, null bytes are added to the end if the string is shorter than the specified length. |
| `string0` | A string of characters terminated by a null (0) byte. When used with writeStruct, it will write the string with a null byte at the end. |
| `string7` | A string of characters prepended by its [7-bit encoded]() length |
| `string` | Can only be used in conjunction with `$format`. Read `$length` amount of bytes as a new string. Can also be used with `$encoding` to specify the encoding. Default is `utf8`. |
| `buffer` | Can only be used in conjunction with `$format`. Read `$length` amount of bytes as a new `Buffer`. |

> Note: By default the endianness is little-endian (LE) - But you can explicitly define the endianness e.g. `int16be`, `uint64le`, etc.

## Directives

### `$format`

Define the format. This can be any of the types mentioned above, or another structure definition.

Examples:

```js
{
someNumber: {
$format: 'uint16' // Results in a single number
},
anotherNumber: 'uint16' // Short-hand for the above
}
```

Some types only work in conjunction with `$format`, as they require extra information to be on the same level. These are: `string` and `buffer`.

Examples:

```js
{
name: {
$format: 'string',
$length: 32,
$encoding: 'ascii' // $encoding is optional, default is 'utf8'
}
}
```

```js
{
blobData: {
$format: 'buffer',
$length: 64000
}
}
```

### `$repeat`

Repeats the specified `$format`. Can be a number or the name of a property containing the value.

Examples:

```js
{
$format: 'byte',
$repeat: 2
}
```

```js
{
numObjects: 'byte',
objects: {
$format: {
...
},
$repeat: 'numObjects'
}
}
```

### `$foreach`

A special form of `$repeat`. Must be a referenced value pointing to a previously read `array` combined with an alias.

Examples:

```js
{
numFiles: 'uint16',
fileTable: {
$repeat: 'numFiles',
$format: {
name: 'char_24',
address: 'uint32',
length: 'uint32'
}
},
files: {
// Iterate over each item in fileTable as 'file'
$foreach: 'fileTable file',
$format: {
fileName: {
$value: 'file.name',
$format: 'char_24'
},
fileContent: {
$goto: 'file.address',
$format: 'buffer',
$length: 'file.length'
}
}
}
}
```

### `$switch`

Read the next data differently based on a previously read value. A `default` case can optionally be defined.

Examples:

```js
const struct = {
type: 'byte',
shape: {
$switch: 'type',
$cases: {
1: { // when type is 1, assume circle data follows
$format: {
radius: 'uint32'
}
},
2: { // when type is 2, assume rectangle data follows
$format: {
width: 'uint16',
height: 'uint16'
}
},
3: { // when type is 3, assume polygon data follows
$format: {
numPoints: 'byte',
points: {
$repeat: 'numPoints',
$format: {
x: 'byte',
y: 'byte'
}
}
}
},
default: {
$format: {
unknown: 'byte'
}
}
}
}
}
// Which could result in:
{
type: 1,
shape: {
radius: 38892
}
}
// or:
{
type: 2,
shape: {
width: 96,
height: 128
}
}
// or:
{
type: 3,
shape: {
points: [
{ x: 0, y: 2 },
{ x: 128, y: 24 },
{ x: 255, y: 8 }
]
}
}

```

### `$ignore`

Read the data, but don't put the property in the eventual JS object. Its value can still be used as a referenced value in other directives.

Examples:

```js
numObjects: {
$format: 'byte',
$ignore: true
}
```

### `$goto`

Jumps to the specified byte location before reading the value.

Examples:

```js
signature: {
$goto: 0xf0,
$format: 'char_2'
}
```

### `$skip`

Skips the specified number of bytes before reading the value.

Examples:

```js
startOfHeader: {
$skip: 255,
$format: 'uint16'
}
```

### `$length`

Can be used in conjunction with `$format: 'string'` and **must** be used when `$format: 'buffer'`.

Examples:

```js
firstName: {
$format: 'string',
$length: 32
}
```

> Note: when `$format` is `'string'`, `$length` is optional. If not present, characters will be read until a null byte (0) is encountered.

```js
blobData: {
$format: 'buffer',
$length: 64000
}
```

### `$encoding`

Can only be used in conjunction with `$format: 'string'`.

Examples:

```js
firstName: {
$format: 'string',
$encoding: 'ascii'
}
```

> Note: the default value for `$encoding` is `'utf8'`

### `$value`

A special directive that doesn't read anything from the buffer and thus doesn't move the internal cursor. Used to copy a value from another source.

```js
{
name: 'string',
nameCopy: {
$value: 'name',
$format: 'uint32'
}
}
```

### `$tell`

A special directive that reads the current position of the internal cursor. Must be used in conjunction with `$format: '$tell'`.

```js
{
name: 'string',
currentAddress: {
$format: '$tell',
$tell: 'uint16'
}
}
```

## Referenced values

Every numeric directive supports passing a reference value string instead of a hard-coded integer. This can be a simple name pointing to a sibling value, or a more complex path.

Examples:

```js
{
nameLength: 'byte',
myName: {
$format: 'string',
$length: 'nameLength'
}
}
```

```js
{
header: {
config: {
nameLength: 'byte'
}
},
myName: {
$format: 'string',
$length: 'header.config.nameLength'
}
}
```

Directives that support this:

- `$repeat`
- `$switch`
- `$length` (see above examples)
- `$goto`
- `$skip`
- `$value`