New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

construct-js

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

construct-js - npm Package Compare versions

Comparing version 0.7.0 to 1.0.0

dist/index.d.ts

15

package.json
{
"name": "construct-js",
"version": "0.7.0",
"version": "1.0.0",
"description": "",
"main": "src/index.js",
"main": "dist/index.js",
"scripts": {
"test": "mocha index.test.js",
"test:coverage": "nyc --reporter=text --reporter=lcov --reporter=clover mocha index.test.js"
"test": "mocha -r ts-node/register index.test.ts",
"test:coverage": "nyc --reporter=text --reporter=lcov --reporter=clover npm run test",
"build": "tsc"
},

@@ -13,7 +14,11 @@ "author": "Francis Stokes",

"devDependencies": {
"@types/chai": "^4.2.22",
"@types/mocha": "^9.0.0",
"@types/node": "^14.0.27",
"chai": "^4.2.0",
"mocha": "^8.1.0",
"nyc": "^15.1.0"
"nyc": "^15.1.0",
"ts-node": "^10.2.1",
"typescript": "^4.4.3"
}
}
# construct-js
`construct-js` is a library for creating byte level data structures. It focuses on a declarative API and simplicity of use.
`construct-js` is a library for creating byte level data structures.

@@ -9,58 +9,85 @@ ```bash

## Features
- Signed and unsigned fields, up to 64-bit
- Nested structs
- Pointer and SizeOf fields
- Different struct alignments, up to 64-bit, including packed structs. Padding can be added before or after the data
- Ability to specify endianness per field
- String support - both raw and null-terminated
- Outputs to the standard Uint8Array type, which can be used in the browser and node
- Getting and setting data in fields
- Fast computation for the size of a field or complete struct
- Written in TypeScript - providing static typing in both JS and TS (dependant on editor support)
- Less than 3.5KiB after minification and gzip
## Table of contents
- [0. High Level Overview](#high-level-overview)
- [1. Examples](#examples)
- [2. Changelog](#changelog)
- [3. API](#api)
<details>
<summary>Click to expand</summary>
- [3.1 Struct](#struct)
- [3.1.1 field](#field)
- [3.1.2 get](#get)
- [3.1.3 getOffset](#getOffset)
- [3.1.4 getDeep](#getDeep)
- [3.1.5 getDeepOffset](#getDeepOffset)
- [3.1.6 computeBufferSize](#computeBufferSize)
- [3.1.7 toArrayBuffer](#toArrayBuffer)
- [3.1.8 toBuffer](#toBuffer)
- [3.1.9 toBytes](#toBytes)
- [3.2 BitStruct](#bitstruct)
- [3.2.1 flag](#flag)
- [3.2.2 multiBit](#multiBit)
- [3.2.3 getOffset](#getOffset-1)
- [3.2.4 computeBufferSize](#computeBufferSize-1)
- [3.2.5 toBuffer](#toBuffer-1)
- [3.2.6 toArrayBuffer](#toArrayBuffer-1)
- [3.2.7 toArray](#toArray-1)
- [3.3 Fields](#fields)
- [3.3.0 Common Methods](#Common-Methods)
- [3.3.0 Field Interfaces](#Field-Interfaces)
- [3.3.1 U8](#U8)
- [3.3.2 U16](#U16)
- [3.3.3 U32](#U32)
- [3.3.4 S8](#S8)
- [3.3.5 S16](#S16)
- [3.3.6 S32](#S32)
- [3.3.7 RawString](#RawString)
- [3.3.8 NullTerminatedString](#NullTerminatedString)
- [3.3.9 U8s](#U8s)
- [3.3.10 U16s](#U16s)
- [3.3.11 U32s](#U32s)
- [3.3.12 S8s](#S8s)
- [3.3.13 S16s](#S16s)
- [3.3.14 S32s](#S32s)
- [3.3.15 Pointer8](#Pointer8)
- [3.3.16 Pointer16](#Pointer16)
- [3.3.17 Pointer32](#Pointer32)
- [3.3.18 SizeOf8](#SizeOf8)
- [3.3.19 SizeOf16](#SizeOf16)
- [3.3.20 SizeOf32](#SizeOf32)
- [3.3.4 U64](#U64)
- [3.3.5 I8](#I8)
- [3.3.6 I16](#I16)
- [3.3.7 I32](#I32)
- [3.3.8 I64](#I64)
- [3.3.9 RawString](#RawString)
- [3.3.10 NullTerminatedString](#NullTerminatedString)
- [3.3.11 U8s](#U8s)
- [3.3.12 U16s](#U16s)
- [3.3.13 U32s](#U32s)
- [3.3.14 U64s](#U64s)
- [3.3.15 I8s](#I8s)
- [3.3.16 I16s](#I16s)
- [3.3.17 I32s](#I32s)
- [3.3.18 Pointer8](#Pointer8)
- [3.3.19 Pointer16](#Pointer16)
- [3.3.20 Pointer32](#Pointer32)
- [3.3.21 Pointer64](#Pointer64)
- [3.3.22 SizeOf8](#SizeOf8)
- [3.3.23 SizeOf16](#SizeOf16)
- [3.3.24 SizeOf32](#SizeOf32)
- [3.3.25 SizeOf64](#SizeOf64)
</details>
## High Level Overview
`construct-js` is all about creating a low-cost and performant abstraction for working with structured, binary data. If you've ever found yourself trying to manually assemble binary data in an ArrayBuffer - stuffing data, calculating sizes, scratching your head over endianness and offsets, then this is likely the tool for you.
`construct-js` allows you to specify and manipulate binary data using an expressive API made up of standard primitives that you may be used to in lower level languages - such as structs, pointers, sizeof operators, and standard sized signed and unsigned integer types.
### Why?
Why not? I mean - I think there is genuine utility, but even if there wasn't, it would simply be an interesting project to undertake.
In terms of actual utility, as the web and distributed services evolve, web pages and JavaScript are taking on increasing more diverse and complex task, as well as connecting to more elaborate and varied services. Typically communication channels between different services use simple interchange formats like JSON over HTTP or WebSockets, but for a variety of reasons this is not always ideal. A large part of the reason this is so widespread is that JavaScript traditionally hasn't had good facilities or abstractions for creating byte-level data structures. Now, however, with the advent and standardisation of Typed Arrays and BigInt, this is no longer the case. `construct-js` allows developers to write expressive descriptions using standard native types like numbers, strings, and regular arrays - and outputs to an efficient Uint8Array format for interchange with the network, filesystem, or even across execution environments like WebAssembly.
## Examples
[There are more examples in the examples folder.](./examples/index.md)
[There are more examples in the examples folder, showing the some more possibilities - including array fields, explicit endianness, etc.](./examples/index.md)
The following example builds a completely valid zip archive with one file inside - `helloworld.txt`.
The following example builds a (just about) valid\* zip archive with one file inside - `helloworld.txt`.
```javascript
const fs = require('fs');
const { RawString, U16LE, U32LE, Struct, Pointer32LE } = require('construct-js');
<sup>*At least when unzipped using the unzip command. Some GUI programs seem to have less success</sup>
```typescript
import * as fs from 'fs/promises';
import {RawString, U16, U32, Struct, Pointer32, Endian} from 'construct-js';
const data = RawString('helloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworld');
const dataSize = data.computeBufferSize();
const filename = RawString('helloworld.txt');
const filenameSize = filename.computeBufferSize();

@@ -71,15 +98,15 @@ // Create a stub for the top level struct that can be referenced by other structs

const sharedHeaderInfo = Struct('sharedHeaderInfo')
.field('minVersion', U16LE(10))
.field('gpFlag', U16LE(0))
.field('compressionMethod', U16LE(0))
.field('lastModifiedTime', U16LE(0))
.field('lastModifiedDate', U16LE(0))
.field('crc32', U32LE(0))
.field('compressedSized', U32LE(data.byteLength))
.field('uncompressedSized', U32LE(data.byteLength))
.field('filenameSize', U16LE(filename.byteLength))
.field('extraFieldLength', U16LE(0));
.field('minVersion', U16(10))
.field('gpFlag', U16(0))
.field('compressionMethod', U16(0))
.field('lastModifiedTime', U16(0))
.field('lastModifiedDate', U16(0))
.field('crc32', U32(0))
.field('compressedSized', U32(dataSize))
.field('uncompressedSized', U32(dataSize))
.field('filenameSize', U16(filenameSize))
.field('extraFieldLength', U16(0));
const localHeader = Struct('localHeader')
.field('header', U32LE(0x04034b50))
.field('header', U32(0x04034b50))
.field('sharedHeaderInfo', sharedHeaderInfo)

@@ -89,21 +116,21 @@ .field('filename', filename);

const centralDirectory = Struct('centralDirectory')
.field('header', U32LE(0x02014b50))
.field('madeByVersion', U16LE(10))
.field('header', U32(0x02014b50))
.field('madeByVersion', U16(10))
.field('sharedHeaderInfo', sharedHeaderInfo)
.field('fileCommentSize', U16LE(0))
.field('diskNumber', U16LE(0))
.field('internalFileAttributes', U16LE(0))
.field('externalFileAttributes', U32LE(0))
.field('relativeOffset', U32LE(0))
.field('fileCommentSize', U16(0))
.field('diskNumber', U16(0))
.field('internalFileAttributes', U16(0))
.field('externalFileAttributes', U32(0))
.field('relativeOffset', U32(0))
.field('filename', filename);
const endOfCentralDirectory = Struct('endOfCentralDirectory')
.field('header', U32LE(0x06054b50))
.field('diskNumber', U16LE(0))
.field('centralDirDiskStart', U16LE(0))
.field('numberOfCentralDirsOnDisk', U16LE(1))
.field('totalNumberOfCentralDirs', U16LE(1))
.field('centralDirSize', U32LE(0))
.field('offsetToStart', Pointer32LE(zipFile, 'centralDirectory'))
.field('commentLength', U16LE(0));
.field('header', U32(0x06054b50))
.field('diskNumber', U16(0))
.field('centralDirDiskStart', U16(0))
.field('numberOfCentralDirsOnDisk', U16(1))
.field('totalNumberOfCentralDirs', U16(1))
.field('centralDirSize', U32(0))
.field('offsetToStart', Pointer32(zipFile, 'centralDirectory'))
.field('commentLength', U16(0));

@@ -117,5 +144,7 @@ // Finalise the top level struct

const fileBuffer = zipFile.toBuffer();
const fileBuffer = zipFile.toUint8Array();
fs.writeFileSync('./test.zip', fileBuffer);
fs.writeFile('./test.zip', fileBuffer).then(() => {
console.log('Done writing zip file.');
});
```

@@ -125,2 +154,9 @@

### 1.0.0
- Full rewrite in TypeScript
- Added 64-Bit support
- Added alignment support
- Breaking changes with the pre 1.0.0 releases
### 0.7.0

@@ -161,21 +197,27 @@

`Struct(name)`
`Struct(name: string, alignment = StructAlignment.Packed, paddingDirection = AlignmentPadding.AfterData)`
Creates a `Struct` object.
Creates a `Struct` object. `alignment` specifies how much (if any) padding should be applied to the fields in order for them to align to a fixed byte boundary. `paddingDirection` specifies where the extra bytes should be added (before or after the data).
#### field
`.field(name, value)`
`.field(name: string, value: ConstructDataType)`
Adds a field to the struct. *name* is used to lookup the field using `struct.get(name)`. *value* must be either a `Struct` or one of the other data types provided by construct-js.
Adds a field to the struct. *name* is used to lookup the field using methods like `struct.get(name)`. *value* must be either a `Struct` or one of the other data types provided by construct-js.
#### get
`.get(name)`
`.get<T extends ConstructDataType>(name: string)`
Returns the [field](#field) with that *name*.
Returns the [field](#field) with that *name*. Note: When using TypeScript, this value must be *cast* to the correct type, either using the generic or with the `as` keyword:
```typescript
const s = Struct('example').field('first', U8(0));
s.get<DataType<typeof U8>>('first');
```
#### getOffset
`.getOffset(name)`
`.getOffset(name: string)`

@@ -186,7 +228,7 @@ Returns the byte offset within the struct of the [field](#field) with that *name*.

`.getDeep(path)`
`.getDeep(path: string)`
Returns the [field](#field) within multiple structs, where *path* is a `.` separated string. For example:
Returns the [field](#field) within multiple structs, where *path* is a `.` separated string. Note: When using TypeScript, this value must be *cast* to the correct type, either using the generic or with the `as` keyword:
```javascript
```typescript

@@ -199,3 +241,3 @@ const struct = Struct('firstStruct')

struct2.getDeep('deeperStruct.aRawString');
struct2.getDeep<DataType<RawString>>('deeperStruct.aRawString');
```

@@ -205,3 +247,3 @@

`.getDeepOffset(path)`
`.getDeepOffset(path: string)`

@@ -216,93 +258,35 @@ Returns the byte offset within multiple structs, where *path* is a `.` separated string.

#### toArrayBuffer
#### toUint8Array
`.toArrayBuffer()`
`.toUint8Array()`
Returns an `ArrayBuffer` representation of the Struct. You should use this if working in the browser.
Returns a `Uint8Array` representation of the Struct.
#### toBuffer
`.toBuffer()`
Returns a `Buffer` representation of the Struct.
#### toBytes
`.toBytes()`
Returns a regular `Array` representation of the Struct.
### BitStruct
`BitStructLSB(name)` and `BitStructMSB(name)`
Creates a BitStruct object, for storing and addressing data on the sub-byte level. If using `BitStructLSB`, the resulting buffer will consider the fields to be ordered from the 0th bit i.e. the first field in the BitStruct will be the least significant bit in the Buffer. If using `BitStructMSB`, the Buffer will write the bits in the opposite order
**Note**: When [bitStruct.toBuffer()](#tobuffer-1) is used, the resulting buffer will be byte aligned. This means if the size of the BitStruct is 12-bits, the resulting buffer will be 16-bits (2 bytes). When using `BitStructLSB`, the most significant bits will be padded.
#### flag
`.flag(name, value)`
Sets a 1-bit flag in the structure.
#### multiBit
`.multiBit(name, size, value)`
Sets a multi-bit flag of *size*.
#### getOffset
`.getOffset(name)`
Gets the **bit** offset of the data with *name*.
#### computeBufferSize
`.computeBufferSize()`
Returns the size of the BitStruct in **bytes**.
#### toBuffer
`.toBuffer()`
Returns a byte-aligned `Buffer` representing the BitStruct.
#### toArrayBuffer
`.toArrayBuffer()`
Returns a byte-aligned `ArrayBuffer` representing the BitStruct.
#### toArray
`.toArray()`
Returns a byte-aligned `Array` representing the BitStruct.
### Fields
#### Common Methods
#### Field Interfaces
All fields contain some common properties and methods. These are:
Fields implement the `IField` interface, and optionally the `IValue` interface:
`.set(value | values)`
##### IField
Which sets either the value or values of the field.
```typescript
interface IField {
computeBufferSize(): number;
toUint8Array(): Uint8Array;
}
```
`.setIsLittleEndian(trueOrFalse)`
##### IValue
Manually sets this field to little or big endian.
```typescript
interface IValue<T> {
set(value: T): void;
get(): T;
}
```
`.value()`
Returns the value of this field, as originally specified.
**The rest of the properties should be considered private and not modified directly.**
#### U8
`U8(value)`
`U8(value: number) implements IField, IValue<number>`

@@ -313,3 +297,3 @@ A single 8-bit unsigned value.

`U16LE(value)` or `U16BE(value)`
`U16(value: number, endian = Endian.Little) implements IField, IValue<number>`

@@ -320,95 +304,100 @@ A single 16-bit unsigned value, in either big or little endian byte order.

`U32LE(value)` or `U32BE(value)`
`U32(value: number, endian = Endian.Little) implements IField, IValue<number>`
A single 32-bit unsigned value, in either big or little endian byte order.
#### S8
#### U64
`S8(value)`
`U64(value: bigint, endian = Endian.Little) implements IField, IValue<bigint>`
A single 64-bit unsigned value, in either big or little endian byte order. Note: Values for 64-bit fields must be specified as `bigint`.
#### I8
`I8(value: number) implements IField, IValue<number>`
A single 8-bit signed value.
#### S16
#### I16
`S16LE(value)` or `S16BE(value)`
`I16(value: number, endian = Endian.Little) implements IField, IValue<number>`
A single 16-bit signed value, in either big or little endian byte order.
#### S32
#### I32
`S32LE(value)` or `S32BE(value)`
`I32(value: number, endian = Endian.Little) implements IField, IValue<number>`
A single 32-bit signed value, in either big or little endian byte order.
#### RawString
#### I64
`RawString(string)`
`I64(value: bigint, endian = Endian.Little) implements IField, IValue<bigint>`
A collection of 8-bit unsigned values, interpreted directly from the string provided. The size of the field is the **byte length** of the string (which is not always the `string.length` when considering unicode).
A single 64-bit signed value, in either big or little endian byte order. Note: Values for 64-bit fields must be specified as `bigint`.
##### RawString.set(string)
#### RawString
(Re)sets the value using the provided string.
`RawString(string) implements IField, IValue<string>`
#### NullTerminatedString
A collection of 8-bit unsigned values, interpreted directly from the string provided.
`NullTerminatedString(string)`
A collection of 8-bit unsigned values, interpreted directly from the string provided. The size of the field is the **byte length** of the string (which is not always the `string.length` when considering unicode). This field appends a single `0x00` byte to the end of the data.
#### NullTerminatedString
##### NullTerminatedString.set(string)
`NullTerminatedString(string) implements IField, IValue<string>`
(Re)sets the value using the provided string. Automatically adds a new null termination byte.
A collection of 8-bit unsigned values, interpreted directly from the string provided. This field appends a single `0x00` byte to the end of the data.
#### U8s
`U8s(array | number)`
`U8s(values: number[]) implements IField, IValue<number[]>`
A collection of 8-bit unsigned values.
If the argument provided is an array, then the size of the field is `array.length` bytes, with each value corresponding to an 8-bit interpretation of that value.
#### U16s
`U16LEs(array | number)` or `U16BEs(array | number)`
`U16s(values: number[], endian = Endian.Little) implements IField, IValue<number[]>`
A collection of 16-bit unsigned values, in either big or little endian byte order.
If the argument provided is an array, then the size of the field is `array.length * 2` bytes, with each value corresponding to an 16-bit interpretation of that value.
#### U32s
`U32LEs(array | number)` or `U32BEs(array | number)`
`U32s(values: number[], endian = Endian.Little) implements IField, IValue<number[]>`
A collection of 32-bit unsigned values, in either big or little endian byte order.
If the argument provided is an array, then the size of the field is `array.length * 4` bytes, with each value corresponding to an 32-bit interpretation of that value.
#### U64s
#### S8s
`U64s(values: bigint[], endian = Endian.Little) implements IField, IValue<bigint[]>`
`S8s(array | number)`
A collection of 64-bit unsigned values, in either big or little endian byte order. Note: Values for 64-bit fields must be specified as `bigint`.
#### I8s
`I8s(values: number[]) implements IField, IValue<number[]>`
A collection of 8-bit signed values.
If the argument provided is an array, then the size of the field is `array.length` bytes, with each value corresponding to an 8-bit interpretation of that value.
#### I16s
#### S16s
`I16s(values: number[], endian = Endian.Little) implements IField, IValue<number[]>`
`S16LEs(array | number)` or `S16BEs(array | number)`
A collection of 16-bit signed values, in either big or little endian byte order.
If the argument provided is an array, then the size of the field is `array.length * 2` bytes, with each value corresponding to an 16-bit interpretation of that value.
#### I32s
#### S32s
`I32s(values: number[], endian = Endian.Little) implements IField, IValue<number[]>`
`S32LEs(array | number)` or `S32BEs(array | number)`
A collection of 32-bit signed values, in either big or little endian byte order.
If the argument provided is an array, then the size of the field is `array.length * 4` bytes, with each value corresponding to an 32-bit interpretation of that value.
#### I64s
`I64s(values: bigint[], endian = Endian.Little) implements IField, IValue<bigint[]>`
A collection of 64-bit signed values, in either big or little endian byte order. Note: Values for 64-bit fields must be specified as `bigint`.
#### Pointer8
`Pointer8(struct, path)`
`Pointer8(struct: Struct, path: string) implements IField`

@@ -419,15 +408,21 @@ `Pointer8` takes a [Struct](#Struct) and a path, and represents an 8-bit pointer (offset) to the field specified by the path in the provided struct.

`Pointer16(struct, path)`
`Pointer16(struct: Struct, path: string, endian = Endian.Little) implements IField`
`Pointer16` take a [Struct](#Struct) and a path, and represents a 16-bit pointer (offset) to the field specified by the path in the provided struct - in either little endian or big endian format.
`Pointer16` takes a [Struct](#Struct) and a path, and represents an 16-bit pointer (offset) to the field specified by the path in the provided struct.
#### Pointer32
`Pointer32(struct, path)`
`Pointer32(struct: Struct, path: string, endian = Endian.Little) implements IField`
`Pointer32` take a [Struct](#Struct) and a path, and represents a 32-bit pointer (offset) to the field specified by the path in the provided struct - in either little endian or big endian format.
`Pointer32` takes a [Struct](#Struct) and a path, and represents an 32-bit pointer (offset) to the field specified by the path in the provided struct.
#### Pointer64
`Pointer64(struct: Struct, path: string, endian = Endian.Little) implements IField`
`Pointer64` takes a [Struct](#Struct) and a path, and represents an 64-bit pointer (offset) to the field specified by the path in the provided struct.
#### SizeOf8
`SizeOf8(structOrField)`
`SizeOf8(target: ConstructDataType) implements IField`

@@ -438,10 +433,16 @@ `SizeOf8` takes a [Struct](#Struct) or a [Field](#Field), and represents the size of the Struct or the Field as an 8-bit unsigned integer.

`SizeOf16LE(structOrField)`
`SizeOf16(target: ConstructDataType, endian = Endian.Little) implements IField`
`SizeOf16` take a [Struct](#Struct) or a [Field](#Field), and represents the size of the Struct or the Field as a 16-bit unsigned integer - in either little endian or big endian format.
`SizeOf16` takes a [Struct](#Struct) or a [Field](#Field), and represents the size of the Struct or the Field as an 16-bit unsigned integer.
#### SizeOf32
`SizeOf32LE(structOrField)`
`SizeOf32(target: ConstructDataType, endian = Endian.Little) implements IField`
`SizeOf32` take a [Struct](#Struct) or a [Field](#Field), and represents the size of the Struct or the Field as a 32-bit unsigned integer - in either little endian or big endian format.
`SizeOf32` takes a [Struct](#Struct) or a [Field](#Field), and represents the size of the Struct or the Field as an 32-bit unsigned integer.
#### SizeOf64
`SizeOf64(target: ConstructDataType, endian = Endian.Little) implements IField`
`SizeOf64` takes a [Struct](#Struct) or a [Field](#Field), and represents the size of the Struct or the Field as an 64-bit unsigned integer.

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc