Skip to content

BufIter

Description

Iterator over an immutable byte buffer. Layout matches Iter(const u8) so the generic Iter macros work on it.

Usage example (Cross-references)

Usage examples (Cross-references)
    /// TAGS: Buf, Read, Format, I/O
    ///
    bool buf_read_fmt(BufIter *iter, Zstr fmtstr, TypeSpecificIO *argv, u64 argc);
    
    ///
    ///
    #define BufIterFromMemory(data_, length_)                                                                              \
        ((BufIter) {.data = (data_), .length = (length_), .pos = 0, .alignment = 1, .dir = 1})
    
    ///
    /// TAGS: Buf, Read, U8
    ///
    static inline bool BufReadU8(BufIter *c, u8 *out) {
        if (c->pos >= c->length) {
            return false;
    /// TAGS: Buf, Read, U16, LittleEndian
    ///
    static inline bool BufReadU16LE(BufIter *c, u16 *out) {
        if (c->pos + 2 > c->length) {
            return false;
    /// TAGS: Buf, Read, U16, BigEndian
    ///
    static inline bool BufReadU16BE(BufIter *c, u16 *out) {
        if (c->pos + 2 > c->length) {
            return false;
    /// TAGS: Buf, Read, U32, LittleEndian
    ///
    static inline bool BufReadU32LE(BufIter *c, u32 *out) {
        if (c->pos + 4 > c->length) {
            return false;
    /// TAGS: Buf, Read, U32, BigEndian
    ///
    static inline bool BufReadU32BE(BufIter *c, u32 *out) {
        if (c->pos + 4 > c->length) {
            return false;
    /// TAGS: Buf, Read, U64, LittleEndian
    ///
    static inline bool BufReadU64LE(BufIter *c, u64 *out) {
        if (c->pos + 8 > c->length) {
            return false;
    /// TAGS: Buf, Read, U64, BigEndian
    ///
    static inline bool BufReadU64BE(BufIter *c, u64 *out) {
        if (c->pos + 8 > c->length) {
            return false;
    /// TAGS: Buf, Read, LEB128, Unsigned
    ///
    static inline bool BufReadULeb128(BufIter *c, u64 *out) {
        u64 result = 0;
        u32 shift  = 0;
    /// TAGS: Buf, Read, LEB128, Signed
    ///
    static inline bool BufReadSLeb128(BufIter *c, i64 *out) {
        u64 uresult = 0;
        u32 shift   = 0;
    /// TAGS: Buf, Read, Zstr, String
    ///
    static inline Zstr BufReadZstr(BufIter *c) {
        Zstr s = (Zstr)(c->data + c->pos);
        while (c->pos < c->length && c->data[c->pos] != 0) {
    // ---------------------------------------------------------------------------
    
    bool buf_read_fmt(BufIter *iter, Zstr fmtstr, TypeSpecificIO *argv, u64 argc) {
        if (!iter || !fmtstr) {
            LOG_FATAL("buf_read_fmt: NULL iter or fmtstr");
        }
    
        BufIter start     = *iter;
        u64     arg_index = 0;
        StrIter fsi       = StrIterFromZstr(fmtstr);
    typedef struct PeContext {
        Pe     *out;
        BufIter file;      // bounds for the whole image
        u32     nt_offset; // offset of NT signature
        u16     num_sections;
            return false;
        }
        BufIter mz_iter = BufIterFromMemory(BufData(&ctx->out->data), 2);
        u16     mz;
        if (!BufReadU16LE(&mz_iter, &mz)) {
        }
        u32     e_lfanew;
        BufIter c = BufIterFromMemory(
            BufData(&ctx->out->data) + DOS_E_LFANEW_OFFSET,
            BufLength(&ctx->out->data) - DOS_E_LFANEW_OFFSET
    // NT signature + File Header. Returns the offset of the Optional Header.
    static bool pe_decode_nt(PeContext *ctx, u64 *out_opt_offset) {
        BufIter c =
            BufIterFromMemory(BufData(&ctx->out->data) + ctx->nt_offset, BufLength(&ctx->out->data) - ctx->nt_offset);
        u32 sig;
            return false;
        }
        BufIter c = BufIterFromMemory(BufData(&ctx->out->data) + opt_offset, ctx->opt_hdr_size);
    
        u16 magic;
    static bool pe_decode_sections(PeContext *ctx, u64 opt_offset) {
        u64     sec_offset = opt_offset + ctx->opt_hdr_size;
        BufIter c = BufIterFromMemory(BufData(&ctx->out->data) + sec_offset, BufLength(&ctx->out->data) - sec_offset);
    
        for (u32 i = 0; i < ctx->num_sections; ++i) {
        for (u32 i = 0; i < num_entries; ++i) {
            u64     entry_off = dir_offset + (u64)i * DEBUG_ENTRY_SIZE;
            BufIter c = BufIterFromMemory(BufData(&ctx->out->data) + entry_off, BufLength(&ctx->out->data) - entry_off);
            u32     charac, ts, type, sz, raddr, rptr;
            u16     ver_maj, ver_min;
            if (sz < 4 + 16 + 4 + 1)
                continue;
            BufIter cv_cur = BufIterFromMemory(BufData(&ctx->out->data) + rptr, sz);
            u32     cv_sig;
            if (!BufReadU32LE(&cv_cur, &cv_sig))
    // Parse the abbrev table starting at `start`. The table is terminated
    // by an entry with code 0. Returns false on malformed input.
    static bool parse_abbrev_table(BufIter cur, AbbrevTable *out, Allocator *alloc) {
        *out = VecInitT(*out, alloc);
        while (IterRemainingLength(&cur) > 0) {
    // Returns false only on truncated / malformed bytes; unknown forms
    // fail-closed so the caller can stop cleanly.
    static bool read_form(BufIter *cur, u32 form, u8 addr_size, AttrVal *out) {
        *out = (AttrVal) {.kind = ATTR_VAL_NONE};
        switch (form) {
    
    static bool walk_cu_dies(
        BufIter            cu_cur, // positioned past CU header
        const AbbrevTable *abbrevs,
        u8                 addr_size,
        }
    
        BufIter info_cur = BufIterFromMemory(info_bytes, info_size);
    
        PendingFns pending = VecInitT(pending, alloc);
    
            AbbrevTable abbrevs;
            BufIter     abbrev_cur = BufIterFromMemory(abbrev_bytes + abbrev_offset, abbrev_size - abbrev_offset);
            if (!parse_abbrev_table(abbrev_cur, &abbrevs, alloc)) {
                ok = false;
            // DIE iter spans the DIE body within this CU (from current
            // info_cur position up to unit_end_pos).
            BufIter die_cur =
                BufIterFromMemory(IterDataAt(&info_cur, IterIndex(&info_cur)), unit_end_pos - IterIndex(&info_cur));
            if (!walk_cu_dies(die_cur, &abbrevs, addr_size, str_bytes, str_size, &out->string_pool, &pending)) {
    //
    // Returns false on cycles, oversized names, or out-of-bounds.
    static bool decode_name(BufIter *it, Str *out_name) {
        const u32 MAX_HOPS  = 64;
        u32       hops      = 0;
    
    // Decode one resource record. Advances the iter past the record.
    static bool decode_record(BufIter *it, DnsRecord *rec, Allocator *alloc) {
        rec->name   = StrInit(alloc);
        rec->target = StrInit(alloc);
                // follow compression pointers back into the whole message,
                // so we use a sub-iter cloned from the main one.
                BufIter sub = *it;
                if (!decode_name(&sub, &rec->target)) {
                    return false;
    // ---------------------------------------------------------------------------
    
    static bool decode_record_list(BufIter *it, u16 count, DnsRecords *out, Allocator *alloc) {
        for (u16 i = 0; i < count; ++i) {
            DnsRecord rec = {0};
            return false;
        }
        BufIter it = BufIterFromMemory(buf, len);
        u16     flags, qd, an, ns, ar;
        if (!BufReadFmt(&it, "{>2r}{>2r}{>2r}{>2r}{>2r}{>2r}", out->id, flags, qd, an, ns, ar)) {
            return false;
        }
        BufIter sb = BufIterFromMemory(BufData(&self->data) + 32, BufLength(&self->data) - 32);
        u32     free_blk, num_blocks, unknown;
        if (!BufReadFmt(
    
        for (u32 i = 0; i < num_dir_blocks; ++i) {
            BufIter blk_iter = BufIterFromMemory((const u8 *)&self->dir_stream_blocks[i], sizeof(u32));
            u32     block_id;
            if (!BufReadU32LE(&blk_iter, &block_id)) {
            return false;
        }
        BufIter dir_iter = BufIterFromMemory(self->stream_dir, self->stream_dir_size);
        if (!BufReadU32LE(&dir_iter, &self->num_streams))
            return false;
            if (!stream_read(self, 1, 0, VecBegin(&buf), VecCapacity(&buf)))
                break;
            BufIter bi = BufIterFromMemory(VecBegin(&buf), VecCapacity(&buf));
            if (!BufReadFmt(&bi, FMT_PDB_INFO_LE, self->info.version, self->info.signature, self->info.age)) {
                LOG_ERROR("PDB: info stream prefix truncated");
                break;
    
            BufIter bi = BufIterFromMemory(VecBegin(&hdr), VecCapacity(&hdr));
            if (!BufReadFmt(
                    &bi,
        for (u32 i = 0; i < n; ++i) {
            // IMAGE_SECTION_HEADER: name[8] + VirtualSize(4) + VirtualAddress(4) + ...
            BufIter rec = BufIterFromMemory(buf + i * 40 + 8, 40 - 8);
            (void)BufReadU32LE(&rec, &out[i].virtual_size);
            (void)BufReadU32LE(&rec, &out[i].virtual_address);
        u64 cur = 0;
        while (cur + 4 <= (u64)sz) {
            BufIter rec_iter = BufIterFromMemory(buf + cur, 4);
            u16     rec_len, rec_kind;
            if (!BufReadU16LE(&rec_iter, &rec_len) || !BufReadU16LE(&rec_iter, &rec_kind))
                // Record body starts at cur + 4 (past len + kind). 10-byte
                // prefix (Flags/Offset/Segment) then NUL-terminated Name.
                BufIter body = BufIterFromMemory(buf + cur + 4, rec_len - 2);
                u32     flags, offset;
                u16     segment;
    } TzifHeader;
    
    static bool tzif_read_header(BufIter *it, TzifHeader *h) {
        if ((u64)IterRemainingLength(it) < TZIF_HEADER_SIZE)
            return false;
    // type's utoff, with the first standard-time (isdst==0) ttinfo as the
    // pre-history / past-table fallback.
    static bool tzif_resolve(BufIter *it, u32 time_width, u32 timecnt, u32 typecnt, i64 unix_seconds, i32 *out_offset) {
        if (typecnt == 0)
            return false;
            return false;
    
        BufIter    it = BufIterFromMemory((u8 *)data, len);
        TzifHeader h1;
        if (!tzif_read_header(&it, &h1)) {
    // file-relative virtual address of `c->p` (used by PCREL). On success
    // `*out` receives the decoded absolute file-relative VA.
    static bool decode_eh_ptr(BufIter *c, u8 encoding, u64 here_vaddr, u64 *out) {
        if (encoding == DW_EH_PE_OMIT) {
            return false;
    // ---------------------------------------------------------------------------
    
    static bool parse_cie(BufIter *body, u64 cie_offset, DwarfCie *out) {
        MemSet(out, 0, sizeof(*out));
        out->offset = cie_offset;
    
    static bool parse_fde(
        BufIter        *body,
        const u8       *body_start,
        u64             cie_offset,
        cfi_parse_section(DwarfCfi *out, const u8 *section_data, u64 section_addr, u64 section_size, bool is_debug_frame) {
        const u32 cie_id      = is_debug_frame ? 0xffffffffu : 0u;
        BufIter   section_cur = BufIterFromMemory(section_data, section_size);
        while (IterRemainingLength(&section_cur) > 0) {
            const u8 *rec_start = IterDataAt(&section_cur, IterIndex(&section_cur));
                // Body iter starts just after the id field (since parse_cie
                // doesn't re-read id) and spans the remainder of the record.
                BufIter body = BufIterFromMemory(IterDataAt(&section_cur, IterIndex(&section_cur)), length32 - 4);
                if (parse_cie(&body, cie_offset, &cie)) {
                    if (!VecPushBackR(&out->cies, cie))
                }
                DwarfFde fde;
                BufIter  body = BufIterFromMemory(IterDataAt(&section_cur, IterIndex(&section_cur)), length32 - 4);
                if (parse_fde(&body, rec_start, cie_offset, out, section_data, section_addr, &fde)) {
                    if (!VecPushBackR(&out->fdes, fde))
    // One instruction. `stop_at` lets the caller bail mid-stream once an
    // advance_loc has covered the requested target PC.
    static bool cfi_vm_step(CfiVm *vm, BufIter *cur, u64 stop_at, bool *stop_now) {
        u8 op = 0;
        if (!BufReadU8(cur, &op))
    
    static bool cfi_vm_run(CfiVm *vm, const u8 *insns, u64 insns_size, u64 stop_at) {
        BufIter cur      = BufIterFromMemory(insns, insns_size);
        bool    stop_now = false;
        while (IterRemainingLength(&cur) > 0) {
            return false;
        }
        BufIter c = BufIterFromBuf(&m->data);
        u32     magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags, reserved;
        if (!BufReadFmt(
    // untouched.
    
    static bool decode_segment_64(MachoContext *ctx, BufIter *cmd) {
        u64 cmdsize = IterLength(cmd);
        if (cmdsize < SEG64_CMD_SIZE_MIN) {
            // `sec_it` is carved at exactly SECT64_SIZE bytes, so the two
            // 16-byte moves below stay inside its window.
            BufIter      sec_it = IterCarve(cmd, SECT64_SIZE);
            MachoSection sec;
            MemSet(&sec, 0, sizeof(sec));
    }
    
    static bool decode_symtab(MachoContext *ctx, BufIter *cmd) {
        if (IterLength(cmd) < SYMTAB_CMD_SIZE) {
            LOG_ERROR("MachO: LC_SYMTAB truncated");
    }
    
    static bool decode_uuid(MachoContext *ctx, BufIter *cmd) {
        if (IterLength(cmd) < UUID_CMD_SIZE) {
            LOG_ERROR("MachO: LC_UUID truncated");
            return false;
        }
        BufIter walker = BufIterFromBuf(&ctx->out->data);
        // Must-precondition: the check above proves the file is at least
        // `MH_HEADER_64_SIZE + sizeofcmds` bytes, so the header skip stays
            }
            // Peek the prefix (cmd, cmdsize) without advancing the walker.
            BufIter prefix = IterCarve(&walker, 8);
            u32     cmd, cmdsize;
            if (!BufReadFmt(&prefix, FMT_MACHO_LC_PREFIX_LE, cmd, cmdsize)) {
            // Carve a view of this entire command (including the 8-byte
            // prefix) for the sub-decoder. The walker stays put.
            BufIter cmd_view = IterCarve(&walker, cmdsize);
            u32     type     = cmd & ~LC_REQ_DYLD;
            switch (type) {
        }
        const u8 *str_base = BufData(&ctx->out->data) + ctx->stroff;
        BufIter   tab      = BufIterFromBuf(&ctx->out->data);
        // Must-precondition: `tab_end > BufLength` was checked above, so
        // `symoff <= tab_end <= BufLength` and the move stays in-bounds.
        self->header.data      = ELF_DATA_LSB;
    
        BufIter iter = BufIterFromBuf(&self->data);
        IterMustMove(&iter, EI_NIDENT);
        u64 shstr_size = 0;
        {
            BufIter iter = BufIterFromBuf(&self->data);
            IterMustMove(&iter, shoff + (u64)self->header.shstrndx * SHDR64_SIZE);
            u32 name = 0, type = 0, link = 0, info = 0;
        }
    
        BufIter iter = BufIterFromBuf(&self->data);
        IterMustMove(&iter, shoff);
        for (u16 i = 0; i < n; ++i) {
        }
    
        BufIter iter = BufIterFromBuf(&self->data);
        IterMustMove(&iter, phoff);
        for (u16 i = 0; i < n; ++i) {
        }
    
        BufIter iter = BufIterFromBuf(&self->data);
        IterMustMove(&iter, symtab->offset);
        for (u64 i = 0; i < count; ++i) {
        }
    
        BufIter it = BufIterFromMemory(BufData(&self->data) + note->offset, note->size);
    
        u32 namesz = 0, descsz = 0, type = 0;
        // CRC is in the last 4 bytes.
        const u8 *crc_bytes = BufData(&self->data) + dl->offset + dl->size - 4;
        BufIter   crc_iter  = BufIterFromMemory(crc_bytes, 4);
        u32       crc;
        if (!BufReadU32LE(&crc_iter, &crc)) {
    // cursor sits at the start of the include_directories table; the
    // directory / file tables are walked separately by collect_cu_strings.
    static bool decode_line_program_header(BufIter *cur, LineProgHeader *out) {
        MemSet(out, 0, sizeof(*out));
    // Walk past include_directories + file_names, leaving cur at the
    // start of the line number program body.
    static bool skip_line_program_tables(BufIter *cur) {
        while (IterIndex(cur) < IterLength(cur) && *IterDataAt(cur, IterIndex(cur)) != 0) {
            if (!BufReadZstr(cur))
    // to the first byte after the standard_opcode_lengths array; we
    // continue from there.
    static bool collect_cu_strings(BufIter cur, Str *pool, CuStrings *cs) {
        // include_directories
        while (IterIndex(&cur) < IterLength(&cur) && *IterDataAt(&cur, IterIndex(&cur)) != 0) {
    
    static bool run_line_program(
        BufIter               cur,
        const u8             *prog_end,
        const LineProgHeader *hdr,
        U64Vec pending_dir_offsets  = VecInitT(pending_dir_offsets, alloc);
    
        BufIter section_cur = BufIterFromMemory(BufData(ElfBuf(elf)) + line_section->offset, line_section->size);
    
        bool ok = true;
            const u8 *unit_start  = IterDataAt(&section_cur, IterIndex(&section_cur));
            u32       unit_length = 0;
            BufIter   peek        = section_cur;
            if (!BufReadU32LE(&peek, &unit_length)) {
                ok = false;
            // tables once to populate the shared string pool, then run
            // the program body.
            BufIter        hdr_cur = section_cur;
            LineProgHeader hdr;
            if (!decode_line_program_header(&hdr_cur, &hdr)) {
            // up to the end of this CU.
            size    strings_start_pos = (size)(hdr.strings_start - IterDataAt(&section_cur, 0));
            BufIter str_cur           = BufIterFromMemory(IterDataAt(&section_cur, 0), unit_end_pos);
            // strings_start_pos lies within `[0, unit_end_pos]` by construction
            // -- `hdr.strings_start` was assigned from inside this section's
    
            // Skip past the tables to find the program body start.
            BufIter prog_anchor = BufIterFromMemory(IterDataAt(&section_cur, 0), unit_end_pos);
            // Must-precondition: same bounds proof as `str_cur` above --
            // `strings_start_pos` is inside `[0, unit_end_pos]` by header
            }
    
            BufIter prog_cur = prog_anchor;
            if (!run_line_program(prog_cur, unit_end, &hdr, &cs, out, &pending_file_offsets, &pending_dir_offsets)) {
                cu_strings_deinit(&cs);
        BufWriteU64LE(&b, 0xFEEDFACECAFEBEEFull);
    
        BufIter it = BufIterFromBuf(&b);
        u16     v16;
        u32     v32;
        BufWriteSLeb128(&b, -123456);
    
        BufIter it = BufIterFromBuf(&b);
        u64     uv;
        i64     sv;
        BufWriteZstr(&b, "world");
    
        BufIter it = BufIterFromBuf(&b);
        Zstr s1 = BufReadZstr(&it);
        Zstr s2 = BufReadZstr(&it);
        BufAppendFmt(&b, "{<2r}{>4r}{<8r}", (u16)0x1234, (u32)0xDEADBEEF, (u64)0x0102030405060708ull);
    
        BufIter it = BufIterFromBuf(&b);
        u16     v16;
        u32     v32;
        BufAppendFmt(&b, "{<2r}", (u16)0xABCD); // only 2 bytes; reader wants 6
    
        BufIter it    = BufIterFromBuf(&b);
        size    entry = it.pos;
        u16     v16   = 0;
    bool test_byte_iter_from_memory(void) {
        const u8 bytes[] = {0xAA, 0xBB, 0xCC};
        BufIter  it      = BufIterFromMemory(bytes, 3);
        u8       v;
        bool     ok = BufReadU8(&it, &v) && v == 0xAA;
        ok = ok && BufWriteFmt(&b, "{<2r}{>8r}", (u16)0x1234, (u64)0x0102030405060708ULL);
        {
            BufIter it    = BufIterFromBuf(&b);
            u16     v16   = 0;
            u64     v64   = 0;
        // Read back: the literal matches and the version is extracted.
        {
            BufIter it  = BufIterFromBuf(&b);
            u8      ver = 0;
            ok          = ok && BufReadFmt(&it, "TZif{>1r}", ver) && (ver == '2');
        // Wrong magic -> soft false, no abort, cursor rewound, dest untouched.
        {
            BufIter it  = BufIterFromBuf(&b);
            u8      ver = 0xAB;
            bool    rd  = BufReadFmt(&it, "TZig{>1r}", ver);
        ok = ok && (BufLength(&b) == 2) && (BufData(&b)[0] == '{') && (BufData(&b)[1] == 0x41);
    
        BufIter it = BufIterFromBuf(&b);
        u8      v  = 0;
        ok         = ok && BufReadFmt(&it, "{​{{>1r}", v) && (v == 0x41);
        ok = ok && (BufLength(&b) == 2);
        {
            BufIter it = BufIterFromBuf(&b);
            u8      q  = 0;
            ok         = ok && BufReadFmt(&it, "{>1r}Z", q) && (q == 'Q');
        ok = ok && BufWriteFmt(&b, "{>1r}{​{", (u8)'Q');
        {
            BufIter it = BufIterFromBuf(&b);
            u8      q  = 0;
            ok         = ok && BufReadFmt(&it, "{>1r}{​{", q) && (q == 'Q');
        ok = ok && (BufLength(&b) == 1);
    
        BufIter it = BufIterFromBuf(&b);
        u8      v8 = 0;
        bool    rd = BufReadFmt(&it, "{<1r}", v8);
        ok = ok && (BufLength(&b) == 4);
    
        BufIter it  = BufIterFromBuf(&b);
        u32     v32 = 0;
        bool    rd  = BufReadFmt(&it, "{<4r}", v32);
        ok = ok && (BufLength(&b) == 8);
    
        BufIter it  = BufIterFromBuf(&b);
        u64     v64 = 0;
        bool    rd  = BufReadFmt(&it, "{>8r}", v64);
        ok = ok && (BufLength(&b) == 8);
    
        BufIter it  = BufIterFromBuf(&b);
        i64     v64 = 0;
        bool    rd  = BufReadFmt(&it, "{<8r}", v64);
        BufWriteFmt(&b, "{<2r}", (u16)0x1234);
    
        BufIter it  = BufIterFromBuf(&b);
        u16     v16 = 0;
        BufReadFmt(&it, "{2}", v16); // real: LOG_FATAL (non-raw). mutant: returns.
        ok = ok && BufWriteFmt(&b, "{>8r}", (u64)0x0102030405060708ULL);
    
        BufIter it  = BufIterFromBuf(&b);
        u64     v64 = 0;
        ok          = ok && BufReadFmt(&it, "{>8r}", v64);
        ok = ok && (BufLength(&b) == 2);
    
        BufIter it    = BufIterFromBuf(&b);
        u16     v16   = 0;
        bool    rd_ok = BufReadFmt(&it, "{^2r}", v16);
        Buf              b     = BufInit(&alloc);
        BufWriteFmt(&b, "{<1r}{<1r}", (u8)0x11, (u8)0x22); // 2 bytes available
        BufIter it = BufIterFromBuf(&b);
        u8      v8 = 0;
        BufReadFmt(&it, "{<1r}{<1r}", v8);                 // 2 fields, 1 arg -> abort
        bool             ok    = BufWriteFmt(&b, "{<2r}{>4r}", (u16)0x1234, (u32)0xAABBCCDD);
        ok                     = ok && (BufLength(&b) == 6);
        BufIter it             = BufIterFromBuf(&b);
        u16     a              = 0;
        u32     c              = 0;
        ok                = ok && BufPushBytes(&b, bytes, 4);
    
        BufIter it    = BufIterFromBuf(&b);
        u32     v32   = 0;
        bool    rd_ok = BufReadFmt(&it, "{>4r}", v32);
        ok                = ok && BufPushBytes(&b, bytes, 4);
    
        BufIter it    = BufIterFromBuf(&b);
        u32     v32   = 0;
        bool    rd_ok = BufReadFmt(&it, "{<4r}", v32);
        ok                = ok && BufPushBytes(&b, bytes, 4);
    
        BufIter it    = BufIterFromBuf(&b);
        u32     v32   = 0;
        bool    rd_ok = BufReadFmt(&it, "{<4r}", v32);
        ok = ok && (BufLength(&b) == 4);
    
        BufIter it    = BufIterFromBuf(&b);
        u32     v32   = 0;
        bool    rd_ok = BufReadFmt(&it, "{^4r}", v32);
        ok = ok && (BufLength(&b) == 8);
    
        BufIter it    = BufIterFromBuf(&b);
        u64     v64   = 0;
        bool    rd_ok = BufReadFmt(&it, "{<8r}", v64);
        ok = ok && (BufLength(&b) == 8);
    
        BufIter it    = BufIterFromBuf(&b);
        u64     v64   = 0;
        bool    rd_ok = BufReadFmt(&it, "{<8r}", v64);
        ok = ok && (BufLength(&b) == 8);
    
        BufIter it    = BufIterFromBuf(&b);
        u64     v64   = 0;
        bool    rd_ok = BufReadFmt(&it, "{^8r}", v64);
        bool ok = BufAppendFmt(&b, "{<1r}", (u8)0xAB);
    
        BufIter it  = BufIterFromBuf(&b);
        u8      out = 0;
        ok          = ok && BufReadFmt(&it, "{<1r}", out) && (out == 0xAB);
        Buf              b     = BufInit(&alloc);
        bool             ok    = BufWriteFmt(&b, "{<2r}", (u16)0xBEEF);
        BufIter          it    = BufIterFromBuf(&b);
        u16              v     = 0;
        ok                     = ok && BufReadFmt(&it, "{<2r}", v) && (v == 0xBEEF);
        } got      = {0};
        bool    ok = BufWriteFmt(&b, "{<8r}", (u64)0x0102030405060708ULL);
        BufIter it = BufIterFromBuf(&b);
        ok         = ok && BufReadFmt(&it, "{<8r}", got.f) && (got.u == 0x0102030405060708ULL);
        BufDeinit(&b);
    bool deadend_must_read_eof(void) {
        const u8 buf[1] = {1};
        BufIter  it     = BufIterFromMemory(buf, 1);
        u8       v;
        IterRead(&it, &v);
    bool deadend_must_peek_out_of_range(void) {
        const u8 buf[1] = {1};
        BufIter  it     = BufIterFromMemory(buf, 1);
        u8       v;
        IterMustPeekAt(&it, 1, &v);
    bool deadend_must_move_overflow(void) {
        const u8 buf[3] = {0};
        BufIter  it     = BufIterFromMemory(buf, 3);
        IterMustMove(&it, 4);
        return true;
    bool deadend_must_next_eof(void) {
        const u8 buf[1] = {0};
        BufIter  it     = BufIterFromMemory(buf, 1);
        u8       v;
        IterRead(&it, &v);
    bool deadend_must_prev_underflow(void) {
        const u8 buf[3] = {0};
        BufIter  it     = BufIterFromMemory(buf, 3);
        IterMustPrev(&it);
        return true;
    bool deadend_it_validate_bad_dir(void) {
        const u8 buf[2] = {1, 2};
        BufIter  it     = {.data = buf, .length = 2, .pos = 0, .alignment = 1, .dir = 0};
        ValidateIter(&it);
        return true;
    bool deadend_it_validate_zero_alignment(void) {
        const u8 buf[2] = {1, 2};
        BufIter  it     = {.data = buf, .length = 2, .pos = 0, .alignment = 0, .dir = 1};
        ValidateIter(&it);
        return true;
    bool deadend_it_remaining_bad_dir(void) {
        const u8 buf[2] = {1, 2};
        BufIter  it     = {.data = buf, .length = 2, .pos = 0, .alignment = 1, .dir = 0};
        (void)IterRemainingLength(&it);
        return true;
    bool deadend_it_move_bad_dir(void) {
        const u8 buf[2] = {1, 2};
        BufIter  it     = {.data = buf, .length = 2, .pos = 0, .alignment = 1, .dir = 0};
        (void)IterMove(&it, 1);
        return true;
    // No public reverse-iter constructor yet; open-coded until a
    // BufIterFromMemoryRev (or similar) is added.
    static BufIter from_rev(const u8 *p, size n) {
        BufIter it = {.data = p, .length = n, .pos = n - 1, .alignment = 1, .dir = -1};
        return it;
    // BufIterFromMemoryRev (or similar) is added.
    static BufIter from_rev(const u8 *p, size n) {
        BufIter it = {.data = p, .length = n, .pos = n - 1, .alignment = 1, .dir = -1};
        return it;
    }
    bool test_iter_remaining_forward(void) {
        const u8 buf[3] = {1, 2, 3};
        BufIter  it     = BufIterFromMemory(buf, 3);
        if (IterRemainingLength(&it) != 3) {
            return false;
    bool test_iter_remaining_reverse(void) {
        const u8 buf[3] = {1, 2, 3};
        BufIter  it     = from_rev(buf, 3);
        if (IterRemainingLength(&it) != 3) {
            return false;
    bool test_iter_read_forward(void) {
        const u8 buf[3] = {10, 20, 30};
        BufIter  it     = BufIterFromMemory(buf, 3);
        u8       v      = 0;
        if (!IterRead(&it, &v) || v != 10) {
    bool test_iter_read_reverse(void) {
        const u8 buf[3] = {10, 20, 30};
        BufIter  it     = from_rev(buf, 3);
        u8       v      = 0;
        if (!IterRead(&it, &v) || v != 30) {
    bool test_iter_read_eof_leaves_state(void) {
        const u8 buf[1] = {7};
        BufIter  it     = BufIterFromMemory(buf, 1);
        u8       v      = 0;
        IterRead(&it, &v);
    bool test_iter_peek_in_range(void) {
        const u8 buf[4] = {5, 6, 7, 8};
        BufIter  it     = BufIterFromMemory(buf, 4);
        IterMove(&it, 2);
        u8 v;
    bool test_iter_peek_out_of_range(void) {
        const u8 buf[2] = {1, 2};
        BufIter  it     = BufIterFromMemory(buf, 2);
        u8       v      = 0xAA;
        if (IterPeekAt(&it, 2, &v)) {
    bool test_iter_move_forward_basic(void) {
        const u8 buf[5] = {0};
        BufIter  it     = BufIterFromMemory(buf, 5);
        if (!IterMove(&it, 3) || IterIndex(&it) != 3) {
            return false;
    bool test_iter_move_forward_to_exhausted(void) {
        const u8 buf[3] = {0};
        BufIter  it     = BufIterFromMemory(buf, 3);
        if (!IterMove(&it, 3) || IterIndex(&it) != 3) {
            return false;
    bool test_iter_move_forward_overflow(void) {
        const u8 buf[3] = {0};
        BufIter  it     = BufIterFromMemory(buf, 3);
        size     before = IterIndex(&it);
        if (IterMove(&it, 4)) {
    bool test_iter_move_forward_underflow(void) {
        const u8 buf[3] = {0};
        BufIter  it     = BufIterFromMemory(buf, 3);
        if (IterMove(&it, -1)) {
            return false;
    bool test_iter_move_reverse_basic(void) {
        const u8 buf[5] = {0};
        BufIter  it     = from_rev(buf, 5);
        // start at pos=4
        if (!IterMove(&it, 2) || IterIndex(&it) != 2) {
    bool test_iter_move_reverse_to_past_start(void) {
        const u8 buf[3] = {0};
        BufIter  it     = from_rev(buf, 3);
        // pos=2, dir=-1, move by 3 lands on sentinel pos=-1
        if (!IterMove(&it, 3) || IterIndex(&it) != (size)-1) {
    bool test_iter_move_reverse_overflow(void) {
        const u8 buf[3] = {0};
        BufIter  it     = from_rev(buf, 3);
        // pos=2, dir=-1, move by 4 would land at pos=-2 — invalid
        size before = IterIndex(&it);
    bool test_iter_next_prev(void) {
        const u8 buf[3] = {0};
        BufIter  it     = BufIterFromMemory(buf, 3);
        if (!IterNext(&it) || IterIndex(&it) != 1) {
            return false;
    bool test_it_peek_from_reverse_sentinel(void) {
        const u8 buf[3] = {10, 20, 30};
        BufIter  it     = from_rev(buf, 3);
        // Step past the start so pos becomes the (size)-1 sentinel.
        IterMove(&it, 3);
    bool test_it_peek_reverse_nonsentinel_uses_pos(void) {
        const u8 buf[3] = {10, 20, 30};
        BufIter  it     = from_rev(buf, 3);
        if (IterIndex(&it) != 2) {
            return false;
    bool test_it_move_out_of_reverse_sentinel(void) {
        const u8 buf[3] = {10, 20, 30};
        BufIter  it     = from_rev(buf, 3);
        IterMove(&it, 3); // -> sentinel
        if (IterIndex(&it) != (size)-1) {
    bool test_it_move_reverse_to_max_position(void) {
        const u8 buf[3] = {10, 20, 30};
        BufIter  it     = from_rev(buf, 3);
        IterMove(&it, 3); // -> sentinel (cur == -1)
        // cur == -1, n == -3 -> delta +3 -> new_pos 2 == length-1 (top valid pos).
    bool test_it_move_reverse_rejects_length(void) {
        const u8 buf[3] = {10, 20, 30};
        BufIter  it     = from_rev(buf, 3); // pos == 2
        size     before = IterIndex(&it);
        // cur == 2, n == -1 -> delta +1 -> new_pos 3 == length: out of range.
    bool test_it_move_reverse_to_index_zero(void) {
        const u8 buf[3] = {10, 20, 30};
        BufIter  it     = from_rev(buf, 3);
        IterMove(&it, 3); // -> sentinel
        // cur == -1, n == -1 -> new_pos 0: a real index, not the sentinel.
    bool test_it_validate_accepts_forward(void) {
        const u8 buf[2] = {1, 2};
        BufIter  it     = BufIterFromMemory(buf, 2);
        ValidateIter(&it);
        return true;
    bool test_it_validate_accepts_reverse(void) {
        const u8 buf[2] = {1, 2};
        BufIter  it     = from_rev(buf, 2);
        ValidateIter(&it);
        return true;
    bool test_remaining_reverse_pos_eq_length_is_zero(void) {
        const u8 buf[3] = {10, 20, 30};
        BufIter  it     = from_rev(buf, 3); // dir == -1, pos == 2, length == 3
        // length := pos + 0 == 2, so now pos (2) == length (2).
        IterTruncate(&it, 0);
    bool test_iter_pos_current_and_exhausted(void) {
        const u8 buf[2] = {40, 50};
        BufIter  it     = BufIterFromMemory(buf, 2);
        if (IterPos(&it) != &buf[0] || *IterPos(&it) != 40) {
            return false;
    bool test_iter_data_at_index_and_end(void) {
        const u8 buf[3] = {7, 8, 9};
        BufIter  it     = BufIterFromMemory(buf, 3);
        IterMove(&it, 1); // cursor moves; DataAt must ignore it
        if (IterDataAt(&it, 0) != &buf[0] || *IterDataAt(&it, 2) != 9) {
    bool test_iter_truncate_caps_reads(void) {
        const u8 buf[5] = {1, 2, 3, 4, 5};
        BufIter  it     = BufIterFromMemory(buf, 5);
        IterMove(&it, 1);     // cursor at index 1
        IterTruncate(&it, 2); // only indices 1,2 remain reachable
    bool test_iter_carve_child_range(void) {
        const u8 buf[4] = {10, 20, 30, 40};
        BufIter  parent = BufIterFromMemory(buf, 4);
        IterMove(&parent, 1); // parent cursor at index 1
        BufIter child = IterCarve(&parent, 2);
        BufIter  parent = BufIterFromMemory(buf, 4);
        IterMove(&parent, 1); // parent cursor at index 1
        BufIter child = IterCarve(&parent, 2);
        if (IterRemainingLength(&child) != 2 || IterIndex(&child) != 0) {
            return false;
Last updated on