Skip to content

frankkulak/binary-coding

Repository files navigation

Binary Coding

Zero-dependency utilities for reading/writing binary data in JavaScript.

View full documentation at frankkulak.com/binary-coding.

Notes

  • This package is for Node, but is compatible with browserify.
  • This package is a replacement for @s4tk/encoding, and is completely backwards compatible with it (some methods are deprecated, but still work, so you just have to swap out imports).

Overview

Instead of this:

// Encoding
let offset = 0;
const buffer = Buffer.alloc(size);
buffer.write(header, offset, 4);
offset += 4;
buffer.writeUInt16LE(version, offset);
offset += 2;

// Decoding
let offset = 0;
const header = buffer.toString("utf-8", offset, 4);
offset += 4;
const version = buffer.readUInt16LE(offset);
offset += 2;

Do this:

// Encoding
const encoder = BinaryEncoder.alloc(size);
encoder.chars(header);
encoder.uint16(version);

// Decoding
const decoder = new BinaryDecoder(buffer);
const header = decoder.chars(4);
const version = decoder.uint16();

Quick Facts

  • All the primitives you'd expect are supported: bytes, bools, chars, signed/unsigned 8/16/32/64-bit ints, floats, and doubles.
  • There are options for easily reading/writing more complex types, like null-terminated strings and bit-masked uints.
  • Both little and big endianness are supported, and you can switch between them.
  • The encoder/decoder automatically track the offset, but it can be manually set if needed.
  • The encoder supports dynamically growing as values are written, if needed.

Usage

Installation

This package is available on npm:

npm i binary-coding

Importing

Both ES modules and CommonJS are supported:

import { BinaryEncoder, BinaryDecoder } from "binary-coding";
const { BinaryEncoder, BinaryDecoder } = require("binary-coding");

Decoding Data

To decode data in a buffer, create a BinaryDecoder:

// by default, it uses little endian - but this can be configured
const decoder = new BinaryDecoder(buffer);

Once you have the decoder, simply call its methods:

const header = decoder.chars(4);
const version = decoder.uint16();
decoder.skip(2); // skip over some unneeded metadata
const numEntries = decoder.uint32();
const entries = decoder.iterate(numEntries, () => {
  const hash = decoder.uint64();
  const value = decoder.terminatedString();
  return { hash, value };
});

Encoding Data

There are multiple ways to create a BinaryEncoder:

  • Its constructor.
  • The static alloc() method.
  • The static dynamicallySized() method.

When performance is critical, it is highly recommended to pre-calculate the total length of the buffer and create the encoder using that size, such as:

const encoder = new BinaryEncoder(Buffer.alloc(size));

Or, for short:

const encoder = BinaryEncoder.alloc(size);

When performance isn't as important as ease of writing code, the encoder can figure its size out as you write to it:

const encoder = BinaryEncoder.dynamicallySized(encoder => {
  // write your data here
});

However you choose to create your encoder, you can write data just as simply as reading it:

encoder.chars(header);
encoder.uint16(version);
encoder.null(2); // make the metadata blank
encoder.uint32(entries.length);
entries.forEach(({ hash, value }) => {
  encoder.uint64(hash);
  encoder.terminatedString(value);
});

Finally, when it's time to get your encoded buffer, just access the buffer property:

const buffer = encoder.buffer;