Grace Hopper taught the world that languages should serve people, not the other way around. Hopper is a small homage to that idea: structured records you can trust, sitting on top of a fixed arena you can reason about.
Current release: 1.0.0 (ABI version 1).
Hopper is an arena-backed record system with explicit layouts (offsets, sizes, encodings) and safe access (bounds checks, typed reads/writes). It’s designed for the part of systems programming where you want byte-for-byte control without falling back to “raw pointer soup”.
Think: mainframe-style record discipline, but as a small, embeddable library.
Most environments give you two extremes:
- Raw bytes: fast and flexible, but easy to mis-index, mis-size, or accidentally reinterpret.
- Heap objects: ergonomic, but with hidden allocation, fragmentation, GC, and weak guarantees about wire layout.
Hopper lives in the middle:
- You allocate from a fixed arena (predictable memory and no fragmentation surprises).
- You work with records defined by layouts (predictable offsets, encodings, and sizes).
- Every read/write is checked (bounds, encoding validity) and deterministic.
This is useful in places where correctness and predictability beat cleverness:
- file formats and binary protocols
- financial records and fixed-point decimals
- structured telemetry/log envelopes
- language runtimes (safe “scratch space” without leaking raw pointers)
- tools that need stable, inspectable memory representations
- A safe arena: allocate records, keep them until arena reset/free.
- A layout engine: define record fields with explicit offsets and encodings.
- A codec layer:
DISPLAYdigits, binary (COMP), packed decimal (COMP-3), raw bytes. - A portable runtime primitive: usable from C, Zing, or any language that can call a C ABI.
- A general-purpose heap / GC replacement.
- A dynamic object system.
- A serialization framework that guesses formats for you.
Hopper is intentionally boring: everything is explicit.
- Deterministic by default: no locale, no pointer/address observability, no hidden time/rand.
- Data-oriented: layouts describe bytes; APIs manipulate bytes through that description.
- No ambient magic: no implicit conversions; encodings are named and enforced.
- Total failure modes: operations return status/results; no “mystery half-writes”.
- Embeddable: small surface area, C ABI, easy to vendor.
An arena is a fixed block of memory. Allocations advance a cursor.
- allocations are fast (bump pointer)
- memory is predictable (bounded)
- individual frees are not supported (by design)
Allocations return a small integer handle:
- not a pointer
- validated on access
- maps to an internal table entry:
(base_offset, size, layout_id)
This indirection is the safety line: you can’t do pointer arithmetic and wander off.
A layout names a record shape, including byte size and field definitions:
- explicit
at:offsets - explicit sizes / picture strings
- explicit storage mode (
DISPLAY,COMP,COMP3, raw)
Layouts are how Hopper stays honest: if the layout says “salary at offset 33”, that is what it means, everywhere.
A Hopper record is a view over bytes that is checked and typed.
- Reading a numeric field means decode bytes → integer (with validation).
- Writing a numeric field means integer → encode bytes (with overflow/format checks).
- Reading/writing bytes means copy with bounds checks.
Hopper does not hide that these are bytes. It just makes them hard to misuse.
Hopper borrows COBOL’s picture strings because they’re a compact way to describe record fields.
Common forms:
X(n)– n raw bytes9(n)– n digitsS9(n)– signed digitsV– implied decimal point (fixed-point scale)
Storage modes:
DISPLAY– ASCII digits (human-readable)COMP– binary integer (little-endian)COMP3– packed decimal (BCD, sign nibble)
Important: fields with V are scaled integers. A 9(5)V99 field stores 123.45 as the integer 12345.
Sometimes you need multiple interpretations of the same bytes.
redefines: lets a field alias an existing region. It’s explicit and intentional — a safe version of a union.
Rules of thumb:
- Use overlays for headers/bodies, union-like variants, and “raw + structured view” patterns.
- Keep overlays obvious: prefer one
rawfield and a small set of named views.
This repo is early. The README explains the shape and goals first; the API is still stabilizing.
Expected project structure (target):
include/hopper.h– public C APIsrc/– arena + table + codec implementationsexamples/– small demostests/– conformance tests (layouts, bounds, numeric codecs)
(This is illustrative. The library is language-agnostic; Zing bindings can map 1:1.)
layout Person bytes: 50 {
name: at: 0 as: pic "X(30)".
age: at: 30 as: pic "9(3)" usage: #DISPLAY.
salary: at: 33 as: pic "S9(9)V99" usage: #COMP3.
}.
- allocate record → get
HopperRef - set fields (validated)
- read fields (validated)
- drop everything by resetting/freeing the arena
This is the “batch processing” happy path: fast, predictable, no cleanup per object.
Zing wants a runtime story where:
- effects are explicit,
- memory is predictable,
- and raw pointers don’t leak into user programs.
Hopper is a natural fit as the structured scratch space for:
- numeric work (including big integer users without Hopper knowing anything about big integers)
- parsing and formatting pipelines
- deterministic tool output
The key point: Hopper stays agnostic. BigInt (or anything else) can use Hopper without Hopper embedding BigInt rules.
Near-term:
- stabilize the core C API (arena init/reset, record alloc, field read/write)
- stabilize PIC parsing and numeric codecs (DISPLAY/COMP/COMP3)
- define clear, testable error codes
Later:
- richer tooling around layouts (layout compiler, schema dump)
- bindings for Zing and other languages
- performance passes (while preserving semantics)
This project is meant to be small and sharp.
If you contribute:
- prefer explicit invariants over clever abstractions
- add tests for every new codec rule
- keep determinism guarantees intact
See repository license files.