npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

int32array-table

v0.0.2

Published

This package provides tools to efficiently store and manipulate structured data using JavaScript's Typed Arrays (Int32Array and Uint8Array). The design emphasizes explicit control over binary layouts, making it ideal for high-performance data processing w

Downloads

6

Readme

int32array-table

Overview

This package provides tools to efficiently store and manipulate structured data using JavaScript's Typed Arrays (Int32Array and Uint8Array). The design emphasizes explicit control over binary layouts, making it ideal for high-performance data processing where every byte counts.

Data Structures

Stringtable

Purpose:
Stores unique strings in a compact binary form.

Low-Level Storage:

  • Maintains an internal array of strings (arr_strings).
  • Each string is encoded with UTF-8.
  • Serialization (toUint8Array()):
    • A 4-byte header indicates the total byte length.
    • Every string is stored with its own 4-byte length field plus the UTF‑8 encoded bytes.
    • Additional metadata and alignment (padding to 4-byte boundaries) may be applied.

Diagram:

+--------------------------------------------------+
|                    Stringtable                   |
+--------------------------------------------------+
| Header: [ Total Length (bytes) ]                 |
| Data:   [ Length, UTF-8 bytes for each string ]  |
+--------------------------------------------------+

Example Code:

// Serialize a stringtable:
const st = new Stringtable();
st.ensure('hello');  // adds the string if missing
const ta = st.toUint8Array();
console.log(ta);
// => Uint8Array [ header bytes, encoded "hello", ... ]

Name_Value_Pair_Table

Purpose:
Manages key/value pairs by utilizing a primary Stringtable for names and a container for value stringtables.

Low-Level Storage & Overhead:

  • NVPT Root Header (16 bytes):
    • Four 4-byte values (e.g., total length, record type, reserved, extra info).
  • Optional Name Property Record (20 bytes if present):
    • A 12-byte header (length, type, bitfield) plus a string field (4 bytes for its length followed by the UTF‑8 bytes).
  • Primary Stringtable (st_names):
    • Includes its own header and overhead from storing each string with a length field.
    • Overhead can be significant (e.g., around 70 bytes) due to metadata and alignment.
  • Container for Value Stringtables:
    • A fixed 16-byte container header plus headers and data for each grouped value table.
    • Each individual value stringtable also includes its own header and per-string overhead.

How Overhead Comes About:

When a record is serialized:

  • A fixed header provides a constant overhead (e.g., 16 bytes for the root record).
  • Each subrecord (like the name property) adds its own header (e.g., 12 bytes) and the cost of storing the string (a 4-byte length field plus the string’s UTF‑8 bytes).
  • Stringtables contribute extra overhead because each string is preceded by a 4-byte length, and a record header is added for the entire table.
  • Alignment padding is applied automatically to ensure the total byte length is a multiple of 4, so if the raw total isn’t divisible by 4 additional bytes will be added.

Illustrative Example (Different from Test Case):

Suppose you serialize a table with:

  • A 16-byte root header.
  • A name record for "example" that costs 20 bytes.
  • A primary stringtable that, due to several strings and its internal header, uses 90 bytes.
  • A container record for value stringtables (16 bytes), with two value stringtables consuming 50 and 70 bytes respectively.

The overall byte cost would be:

16 (root header) + 20 (name record) + 90 (primary stringtable) + 16 (container) + 50 + 70 = 262 bytes

Padding might then increase this total to meet alignment requirements.

Diagram:

+--------------------------------------------------+
|         Name_Value_Pair_Table Structure          |
+--------------------------------------------------+
| NVPT Root Header       (16 bytes)                |
| ├─ Name Property Record (optional, 20 bytes)     |
| ├─ Primary Stringtable  (≈70 bytes overhead)     |
| └─ Value Stringtables:                           |
|      ├─ Container Header (16 bytes)              |
|      ├─ Value Table 1      (e.g., ~60 bytes)      |
|      └─ Value Table 2      (e.g., ~80 bytes)      |
+--------------------------------------------------+

Example Code:

// Create and serialize a name/value pair table:
const nvpt = new Name_Value_Pair_Table({name: 'nvpt'});
nvpt.ensure('css', 'bold');
const ui8a = nvpt.toUint8Array();
console.log(ui8a);
// => Uint8Array with header, name record, st_names block, and value stringtables.

Compact_Int32Array_Table

Purpose:
Provides a dynamically growing table of records stored in a contiguous Int32Array.

Low-Level Storage:

  • Each record includes:
    1. Header (Minimum 12 bytes):
      • 4 bytes: Total record length in bytes
      • 4 bytes: Record type identifier
      • 4 bytes: Bitfield (additional flags/metadata)
    2. Body:
      • The actual record content (e.g., numeric values or nested records)
  • Records are stored sequentially and may include custom headers for nested data.

Diagram:

+--------------------------------------------------+
|             Compact_Int32Array_Table             |
+--------------------------------------------------+
| [ Record 1 ]     [ Record 2 ]    ...             |
+--------------------------------------------------+
           │                     │
           ▼                     ▼
+---------------------------+  +---------------------------+
| Header:                   |  | Header:                   |
| [ Len, Type, BF ]         |  | [ Len, Type, BF ]         |
| Data                      |  | Data                      |
+---------------------------+  +---------------------------+

Sample Header Layout:

// Example header for one record:
// [ 24 (record length in bytes), 20010 (record type: NAME_VALUE_PAIR_TABLE),
//   3 (bitfield), ...data... ]

Compact_Int32Array_Record

Purpose:
Represents a single record with a header and data section.

Low-Level Storage:

  • Header:
    • 4 bytes: Total record length in bytes
    • 4 bytes: Record type identifier
    • 4 bytes: Bitfield (flags, stored as 32-bit integer using bit masks)
  • Data:
    • Actual content, stored after the header

Diagram:

+--------------------------------------------------+
|           Compact_Int32Array_Record              |
+--------------------------------------------------+
| Header: [ Length (bytes) | Type | Bitfield ]      |
| Data:   [ Content … ]                            |
+--------------------------------------------------+

Example Code for Bitfield Operations:

const rec = new Compact_Int32Array_Record({ /* ... */ });
rec.set_bitfield_value(2, 1);  // Set flag at bit index 2
console.log(rec.get_bitfield_value(2)); // => 1

STA_Record_Reader

Purpose:
Facilitates reading nested records from a Uint8Array via a DataView.

Low-Level Storage:

  • Uses a DataView to interpret 4-byte segments:
    • Bytes 0–3: Record length (in bytes).
    • Bytes 4–7: Record type.
    • Following bytes: Bitfields and contained data.
  • Iterates recursively based on the header lengths.

Diagram:

+--------------------------------------------------+
|                STA_Record_Reader                 |
+--------------------------------------------------+
| DataView → Reads Header                          |
|    ( Length, Type, Flags )                       |
| Iterates Inner Records                           |
+--------------------------------------------------+

Example Usage:

const reader = new STA_Record_Reader({ sta: someUint8Array });
for (const child of reader.iterate_child_readers()) {
  console.log('Child record type:', child.getInt32(4));
}

Data Flow and Serialization

  1. Determine Total Byte Length:
    Each component calculates its complete size (headers + body data), taking into account per-string length fields and necessary padding.
  2. Allocate Uint8Array:
    The exact size is allocated to avoid reallocation.
  3. Write Record Headers:
    DataView is used to write each record’s fixed-size header (e.g., total length, record type, bitfield).
  4. Append Data:
    Data such as strings are appended, ensuring each string is preceded by its own length field.
  5. Automatic Padding:
    The final output is padded to a 4-byte boundary if needed.

Efficiency Considerations

  • Pre-allocation:
    Dynamically growing the underlying Typed Array minimizes costly reallocations.
  • Fixed Overhead:
    Each record has a predictable overhead (e.g., 12–16 bytes), which helps in rapid serialization and deserialization.
  • Low-Level Operations:
    Direct use of DataView for 32-bit field operations and deliberate control over alignment improves performance.

Conclusion

The int32array-table package offers an efficient way to work with binary data by breaking every component into deliberate header and data portions. Clear control over overhead ensures both consistency and performance in data processing pipelines.

Use the exported modules:

const {
  Stringtable,
  Name_Value_Pair_Table,
  Compact_Int32Array_Table,
  Compact_Int32Array_Record,
  STA_Record_Reader
} = require('int32array-table');

to build robust, high-performance applications.