• Stars
    star
    1,349
  • Rank 34,821 (Top 0.7 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created over 5 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

๐Ÿ› ๏ธA library for creating byte level data structures.

construct-js

construct-js is a library for creating byte level data structures.

npm i construct-js

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

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, showing the some more possibilities - including array fields, explicit endianness, etc.

The following example builds a (just about) valid* zip archive with one file inside - helloworld.txt.

*At least when unzipped using the unzip command. Some GUI programs seem to have less success

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();

// Create a stub for the top level struct that can be referenced by other structs
const zipFile = Struct('ZipFile');

const sharedHeaderInfo = Struct('sharedHeaderInfo')
  .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', U32(0x04034b50))
  .field('sharedHeaderInfo', sharedHeaderInfo)
  .field('filename', filename);

const centralDirectory = Struct('centralDirectory')
  .field('header', U32(0x02014b50))
  .field('madeByVersion', U16(10))
  .field('sharedHeaderInfo', sharedHeaderInfo)
  .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', 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));

// Finalise the top level struct
zipFile
  .field('localHeader', localHeader)
  .field('data', data)
  .field('centralDirectory', centralDirectory)
  .field('endOfCentralDirectory', endOfCentralDirectory);

const fileBuffer = zipFile.toUint8Array();

fs.writeFile('./test.zip', fileBuffer).then(() => {
  console.log('Done writing zip file.');
});

Changelog

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

  • Add TypeScript definition file

0.6.1

  • Add .value() method to fields.

0.6.0

  • Refactor the user-facing API to remove endianness flags in fields and instead create a field for little endian and big endian variations.

0.5.0

  • Added NullTerminatedString field
  • Fixed a bug in getDeep that allowed requesting nonsense values in the path

0.4.2

  • Added Pointer8, Pointer16, Pointer32, SizeOf8, SizeOf16 and SizeOf32 fields

0.4.0

  • Removed concept of endianness from Structs. All endianness information comes directly from the Fields themselves
  • Removed deprecated Fields
  • Renamed I8, I16, I32, I8s, I16s, I32s -> S8, S16, S32, S8s, S16s, S32s

0.3.0

  • Allow the bit ordering to be specified for BitStructs

API

Struct

Struct(name: string, alignment = StructAlignment.Packed, paddingDirection = AlignmentPadding.AfterData)

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: string, value: ConstructDataType)

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<T extends ConstructDataType>(name: string)

Returns the 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:

const s = Struct('example').field('first', U8(0));

s.get<DataType<typeof U8>>('first');

getOffset

.getOffset(name: string)

Returns the byte offset within the struct of the field with that name.

getDeep

.getDeep(path: string)

Returns the 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:

const struct = Struct('firstStruct')
  .field('aRawString', RawString('ABC'));

const struct2 = Struct('secondStruct')
  .field('deeperStruct', struct);

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

getDeepOffset

.getDeepOffset(path: string)

Returns the byte offset within multiple structs, where path is a . separated string.

computeBufferSize

.computeBufferSize()

Returns the size of the struct in bytes.

toUint8Array

.toUint8Array()

Returns a Uint8Array representation of the Struct.

Fields

Field Interfaces

Fields implement the IField interface, and optionally the IValue interface:

IField
interface IField {
  computeBufferSize(): number;
  toUint8Array(): Uint8Array;
}
IValue
interface IValue<T> {
  set(value: T): void;
  get(): T;
}

U8

U8(value: number) implements IField, IValue<number>

A single 8-bit unsigned value.

U16

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

A single 16-bit unsigned value, in either big or little endian byte order.

U32

U32(value: number, endian = Endian.Little) implements IField, IValue<number>

A single 32-bit unsigned value, in either big or little endian byte order.

U64

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.

I16

I16(value: number, endian = Endian.Little) implements IField, IValue<number>

A single 16-bit signed value, in either big or little endian byte order.

I32

I32(value: number, endian = Endian.Little) implements IField, IValue<number>

A single 32-bit signed value, in either big or little endian byte order.

I64

I64(value: bigint, endian = Endian.Little) implements IField, IValue<bigint>

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

RawString(string) implements IField, IValue<string>

A collection of 8-bit unsigned values, interpreted directly from the string provided.

NullTerminatedString

NullTerminatedString(string) implements IField, IValue<string>

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(values: number[]) implements IField, IValue<number[]>

A collection of 8-bit unsigned values.

U16s

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.

U32s

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.

U64s

U64s(values: bigint[], endian = Endian.Little) implements IField, IValue<bigint[]>

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.

I16s

I16s(values: number[], endian = Endian.Little) implements IField, IValue<number[]>

A collection of 16-bit signed values, in either big or little endian byte order.

I32s

I32s(values: number[], endian = Endian.Little) implements IField, IValue<number[]>

A collection of 32-bit signed values, in either big or little endian byte order.

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: Struct, path: string) implements IField

Pointer8 takes a Struct and a path, and represents an 8-bit pointer (offset) to the field specified by the path in the provided struct.

Pointer16

Pointer16(struct: Struct, path: string, endian = Endian.Little) implements IField

Pointer16 takes a 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: Struct, path: string, endian = Endian.Little) implements IField

Pointer32 takes a 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 and a path, and represents an 64-bit pointer (offset) to the field specified by the path in the provided struct.

SizeOf8

SizeOf8(target: ConstructDataType) implements IField

SizeOf8 takes a Struct or a Field, and represents the size of the Struct or the Field as an 8-bit unsigned integer.

SizeOf16

SizeOf16(target: ConstructDataType, endian = Endian.Little) implements IField

SizeOf16 takes a Struct or a Field, and represents the size of the Struct or the Field as an 16-bit unsigned integer.

SizeOf32

SizeOf32(target: ConstructDataType, endian = Endian.Little) implements IField

SizeOf32 takes a Struct or a 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 or a Field, and represents the size of the Struct or the Field as an 64-bit unsigned integer.

More Repositories

1

super-expressive

๐Ÿฆœ Super Expressive is a zero-dependency JavaScript library for building regular expressions in (almost) natural language
JavaScript
4,464
star
2

arcsecond

โœจZero Dependency Parser Combinator Library for JS Based on Haskell's Parsec
TypeScript
500
star
3

16bitjs

๐Ÿ’ป A 16-bit virtual machine, including assembly language with 37 instructions, binary assembler, and a step through debugger
JavaScript
470
star
4

githublog

I'm sick of complex blogging solutions, so markdown files in a git repo it is
338
star
5

tega

๐Ÿ•น TypeScript Embedded GameBoy Macro Assembler
TypeScript
227
star
6

hexnut

๐Ÿ”ฉ Hexnut is a middleware based, express/koa like framework for web sockets
JavaScript
208
star
7

React-Machinery

๐Ÿ”ฅ React Machinery provides a simple to use, component based approach to state machines in react.
JavaScript
105
star
8

vec-la

Tiny linear algebra library specifically for 2d
JavaScript
41
star
9

bewitched

๐Ÿง™๐Ÿป Command line hex editor
TypeScript
35
star
10

Lazy-Infinite-List

๐Ÿ—’๏ธ A Fantasy Land compliant Infinite List Data Structure
JavaScript
26
star
11

tiny-c-projects

A collection of small C projects - usually a minimal example of something interesting
C
25
star
12

vmfc

Stack-based VM Architecture in JavaScript. (Virtual Machine Fantasy Console)
JavaScript
23
star
13

vec-la-fp

โ†—๏ธ A tiny (functional) 2d linear algebra library
JavaScript
22
star
14

arcsecond-binary

Binary parsers for arcsecond!
JavaScript
19
star
15

GMMK-Driver

An open source, reverse engineered control driver for the GMMK mechanical keyboard
TypeScript
19
star
16

AES-C

A (non-production) implementation of AES for educational purposes
C
14
star
17

teensy-nes

NES Emulator on a Teensy 4.1
C++
8
star
18

hexnut-client

JavaScript
8
star
19

trump-chain

JavaScript
8
star
20

kandinsky-js

๐ŸŒˆA tiny colour library
JavaScript
7
star
21

Classiest

๐Ÿธ Create classier classes with overloadable methods, getters, setters, and statics!
JavaScript
7
star
22

ebpf-usb

A tool for monitoring (specific) USB devices
Python
6
star
23

super-expressive-fp

SuperExpressive, but with a wrapped API for functional programming
JavaScript
6
star
24

Image-Glitcher

๐Ÿ’ข Generates glitchy GIFs from JPEGs
JavaScript
5
star
25

rustack-machine

A simple stack machine in rust
Rust
5
star
26

bito

B.I.T.O - Programatic Beats Code Golfed In Your Browser
JavaScript
5
star
27

Steganography-C

Steganographic encoding implementation for hiding data in images
C
4
star
28

ATmega328P-Bare-Metal-Task-Switching

๐ŸŽ– A minimal implementation of a task-switching kernel in C for the ATmega328P chip
C
4
star
29

hexnut-sequence

Sequencing middleware for the HexNut framework
JavaScript
3
star
30

hexnut-handle

Simple hexnut middleware for dealing with connections and messages
JavaScript
3
star
31

algebraic-types

JavaScript
3
star
32

SpelBoy

A GameBoy (DMG) emulator written in TypeScript
TypeScript
3
star
33

modular-animation-synthesizer

https://francisrstokes.github.io/modular-animation-synthesizer/
JavaScript
3
star
34

frame-http

๐Ÿ–ผ๏ธLaughably minimal http framework combining the best parts of express and koa
JavaScript
3
star
35

ElessarOS

risc-v OS inspired by xv6
C
2
star
36

Brainfuck-Interpreter

Brainfuck interpreter using Jison
JavaScript
2
star
37

SNES-Controller-Arduino-Leonardo

Turn the SNES controller into a USB controller for use with an emulator
C++
2
star
38

zig-expressions

A regular expression engine written in Zig
Zig
2
star
39

lazy-do

Fantasy Land compliant do notation for lazy structures ๐Ÿฆ„
JavaScript
2
star
40

readme-cli

๐Ÿ“– A CLI tool for rendering npm/github README files in the terminal
JavaScript
2
star
41

microcan

JavaScript
2
star
42

simple-transduce

A really simple transducer module to easily convert map-filter-reduce chains to single pass transducers.
JavaScript
2
star
43

creative-code-toolkit-fp

JavaScript
2
star
44

aoc-2023

Zig
1
star
45

WaveStrider

CMake
1
star
46

lambda-lang

JavaScript
1
star
47

hexnut-bodyparser

JavaScript
1
star
48

hexnut-restore-connection

HexNut middleware to restore a lost connection
JavaScript
1
star
49

app-and-bootloader

Simple app and bootloader implementation for STM32 using libopencm3
Makefile
1
star
50

salsa20-on-rp2040

My entry for LLJam0001: Salsa20 hardware encryption device using a Raspberry Pi Pico
C
1
star
51

microcan-fp

JavaScript
1
star
52

riscv-gateware-ts

A RISC-V processor with gateware-ts
TypeScript
1
star
53

bus-pirate

Bus Pirate integration for TS and JS
TypeScript
1
star
54

primer-js

๐Ÿ•ฐ A tiny (474 bytes minified + gzipped) library for creating normalised, unit independent timelines
JavaScript
1
star
55

zig-stm32-bare-metal

Minimal zig code to blink a LED for the STM32F401RE chip
Zig
1
star
56

Jazz-Chordr

Memorise common jazz chords https://francisrstokes.github.io/Jazz-Chordr/
JavaScript
1
star
57

Hindley-Milner-Parser

A Hindley-Milner type signature parser in haskell
Haskell
1
star
58

hexnut-with-observable

A Hexnut middleware for integrating with rxjs
JavaScript
1
star
59

Redux-State-Resolver

๐Ÿ’กCleanly resolve a sequence of dependencies - write component logic that can assume the state has what it needs.
JavaScript
1
star
60

x86_64-Userspace-Emulator

C
1
star
61

hexnut-router

Routing middleware for HexNut
JavaScript
1
star
62

c-proj-init

A script to generate a skeleton C project, with a minimal Makefile and vscode debugging
JavaScript
1
star
63

autonotyper

An automatic typing engine tht can be plugged into anything
JavaScript
1
star
64

4FVM

Spiritual successor to 16bitJS
JavaScript
1
star
65

gibson-engine

A minimalist text game (interactive fiction) engine
JavaScript
1
star