std.encoding.binary
API summary (generated from the Beans source by npm run coverage): 36 package functions · 5 types · 2 constructors · 25 instance methods · 6 public fields · 3 enum variants.
std.encoding.binary reads and writes fixed-width numbers and varints in a
Bytes buffer with an explicit byte order. It is
pure Beans over Bytes: the native Bytes word accessors stay the storage
primitives (they are little-endian and panic when out of range), and this package
adds the checked layer on top: explicit byte order, Result errors instead of
panics, bit-preserving float conversion, and cursors. Read the source at
stdlib/std/encoding/binary/binary.b.
import std.encoding.binaryReads and writes never touch memory outside the checked range. A read past the
end of the buffer is an error with kind eof, and a write that will not fit is an
error with kind range, never a panic.
ByteOrder
Section titled “ByteOrder”ByteOrder picks how multi-byte numbers are laid out.
pub enum ByteOrderlittlebignativenative is folded at compile time through std.target,
so it becomes whichever order the target machine uses. Single-byte functions
(read_u8, read_i8, write_u8, write_i8, append_u8, append_i8) take no
order, because one byte has none.
Uvarint and Varint
Section titled “Uvarint and Varint”Varint reads return a small struct that carries both the decoded value and how many bytes it took, so the caller can advance past it.
pub struct Uvarintpub value: u64pub size: int
pub struct Varintpub value: intpub size: intPositional reads
Section titled “Positional reads”Read a number at a byte position. The buffer is borrowed, not consumed, and
nothing is changed. If the buffer is too short for the width, you get an error
with kind eof.
pub fn read_u8(data: Bytes, pos: int) -> Result<u8>pub fn read_i8(data: Bytes, pos: int) -> Result<i8>pub fn read_u16(data: Bytes, pos: int, order: ByteOrder) -> Result<u16>pub fn read_i16(data: Bytes, pos: int, order: ByteOrder) -> Result<i16>pub fn read_u32(data: Bytes, pos: int, order: ByteOrder) -> Result<u32>pub fn read_i32(data: Bytes, pos: int, order: ByteOrder) -> Result<i32>pub fn read_u64(data: Bytes, pos: int, order: ByteOrder) -> Result<u64>pub fn read_i64(data: Bytes, pos: int, order: ByteOrder) -> Result<i64>pub fn read_f32(data: Bytes, pos: int, order: ByteOrder) -> Result<f32>pub fn read_f64(data: Bytes, pos: int, order: ByteOrder) -> Result<float>Float reads are bit-preserving: the exact bytes become the exact float, so infinities, quiet NaN payloads, and negative zero all survive the round trip.
Positional writes
Section titled “Positional writes”Write a number at a byte position into space that already exists. If the width
will not fit within the current length, you get an error with kind range. All
return Result<bool>.
pub fn write_u8(data: Bytes, pos: int, value: u8) -> Result<bool>pub fn write_i8(data: Bytes, pos: int, value: i8) -> Result<bool>pub fn write_u16(data: Bytes, pos: int, value: u16, order: ByteOrder) -> Result<bool>pub fn write_i16(data: Bytes, pos: int, value: i16, order: ByteOrder) -> Result<bool>pub fn write_u32(data: Bytes, pos: int, value: u32, order: ByteOrder) -> Result<bool>pub fn write_i32(data: Bytes, pos: int, value: i32, order: ByteOrder) -> Result<bool>pub fn write_u64(data: Bytes, pos: int, value: u64, order: ByteOrder) -> Result<bool>pub fn write_i64(data: Bytes, pos: int, value: i64, order: ByteOrder) -> Result<bool>pub fn write_f32(data: Bytes, pos: int, value: f32, order: ByteOrder) -> Result<bool>pub fn write_f64(data: Bytes, pos: int, value: float, order: ByteOrder) -> Result<bool>Appends
Section titled “Appends”Add a number to the end of the buffer, growing it by the width of the value. These return nothing and never fail.
pub fn append_u8(data: Bytes, value: u8)pub fn append_i8(data: Bytes, value: i8)pub fn append_u16(data: Bytes, value: u16, order: ByteOrder)pub fn append_i16(data: Bytes, value: i16, order: ByteOrder)pub fn append_u32(data: Bytes, value: u32, order: ByteOrder)pub fn append_i32(data: Bytes, value: i32, order: ByteOrder)pub fn append_u64(data: Bytes, value: u64, order: ByteOrder)pub fn append_i64(data: Bytes, value: i64, order: ByteOrder)pub fn append_f32(data: Bytes, value: f32, order: ByteOrder)pub fn append_f64(data: Bytes, value: float, order: ByteOrder)Append a fixed-width integer and read it straight back:
import std.ioimport std.encoding.binary
fn main() { var buf: Bytes = new Bytes(0) binary.append_u32(buf, 1000, binary.ByteOrder.little) let value: u32 = binary.read_u32(buf, 0, binary.ByteOrder.little).expect("read") io.println(value) // 1000}Varints
Section titled “Varints”A varint packs an integer into fewer bytes when it is small.
pub fn uvarint_size(value: u64) -> intpub fn varint_size(value: int) -> intpub fn append_uvarint(data: Bytes, value: u64)pub fn read_uvarint(data: Bytes, pos: int) -> Result<Uvarint>pub fn append_varint(data: Bytes, value: int)pub fn read_varint(data: Bytes, pos: int) -> Result<Varint>uvarint_sizeandvarint_sizereport how many bytes an encoding will take, from 1 up to 10.append_uvarint/read_uvarintare unsigned LEB128 over the 64-bit pattern: the same wire format asBytes.append_uvarintand Go’sencoding/binaryPutUvarint/Uvarint.append_varint/read_varintare the signed zigzag form matching Go’sPutVarint:-1encodes as1,1as2, and every value takes its zigzag width rather than ten bytes for all negatives.read_uvarintreports kindeofon a truncated input, and kindoverflowwhen the varint runs past ten bytes or overflows 64 bits.read_varintdecodes the unsigned form first, so it reports the same kinds.
Both reads return the value together with its byte width, so you can chain reads
by advancing pos past each one:
import std.ioimport std.encoding.binary
fn main() { var buf: Bytes = new Bytes(0) binary.append_uvarint(buf, 300) binary.append_varint(buf, -5)
let first: binary.Uvarint = binary.read_uvarint(buf, 0).expect("uvarint") io.println(first.value) // 300 io.println(binary.uvarint_size(300)) // 2
let second: binary.Varint = binary.read_varint(buf, first.size).expect("varint") io.println(second.value) // -5}class Reader
Section titled “class Reader”A Reader walks a buffer forward, tracking the read position for you. Build it
with the byte order it should use for every multi-byte read:
new Reader(order: ByteOrder)Bytes is a move-only buffer, so the reader does not own one. Every method
borrows the buffer for the call and only the position lives in the reader, so a
Reader sits beside every other use of the same buffer with no clone.
pub position: int
pub fn remaining(data: Bytes) -> intpub fn skip(data: Bytes, count: int) -> Result<bool>pub fn read_u8(data: Bytes) -> Result<u8>pub fn read_i8(data: Bytes) -> Result<i8>pub fn read_u16(data: Bytes) -> Result<u16>pub fn read_i16(data: Bytes) -> Result<i16>pub fn read_u32(data: Bytes) -> Result<u32>pub fn read_i32(data: Bytes) -> Result<i32>pub fn read_u64(data: Bytes) -> Result<u64>pub fn read_i64(data: Bytes) -> Result<i64>pub fn read_f32(data: Bytes) -> Result<f32>pub fn read_f64(data: Bytes) -> Result<float>pub fn read_uvarint(data: Bytes) -> Result<u64>pub fn read_varint(data: Bytes) -> Result<int>positionis where the next read starts; it advances by the width of each successful read.remainingis the number of bytes left afterposition, never negative.skipmoves forwardcountbytes; a negative count is kindrange, and skipping past the end is kindeof.- The
read_*methods read atpositionand advance it, failing with the same kinds as their positional counterparts.read_uvarintunwraps the value from theUvarintstruct andread_varintfrom theVarintstruct, since the reader already tracks the size.
class Writer
Section titled “class Writer”A Writer writes numbers in sequence into space that already exists, tracking a
position the same way. Build it with the byte order for its multi-byte writes:
new Writer(order: ByteOrder)pub position: int
pub fn remaining(data: Bytes) -> intpub fn write_u8(data: Bytes, value: u8) -> Result<bool>pub fn write_i8(data: Bytes, value: i8) -> Result<bool>pub fn write_u16(data: Bytes, value: u16) -> Result<bool>pub fn write_i16(data: Bytes, value: i16) -> Result<bool>pub fn write_u32(data: Bytes, value: u32) -> Result<bool>pub fn write_i32(data: Bytes, value: i32) -> Result<bool>pub fn write_u64(data: Bytes, value: u64) -> Result<bool>pub fn write_i64(data: Bytes, value: i64) -> Result<bool>pub fn write_f32(data: Bytes, value: f32) -> Result<bool>pub fn write_f64(data: Bytes, value: float) -> Result<bool>Each write advances position by the width written, and fails with kind range
if that width does not fit in the buffer. Unlike the free append_* functions,
a Writer does not grow the buffer, so size it first with new Bytes(n).
import std.ioimport std.encoding.binary
fn main() { var buf: Bytes = new Bytes(8) let w: binary.Writer = new binary.Writer(binary.ByteOrder.big) w.write_u32(buf, 7).expect("write")
let r: binary.Reader = new binary.Reader(binary.ByteOrder.big) io.println(r.read_u32(buf).expect("read")) // 7}