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)
- In
Buf.c:99:
BufWriteU64LE(&b, 0xFEEDFACECAFEBEEFull);
BufIter it = BufIterFromBuf(&b);
u16 v16;
u32 v32;- In
Buf.c:119:
BufWriteSLeb128(&b, -123456);
BufIter it = BufIterFromBuf(&b);
u64 uv;
i64 sv;- In
Buf.c:137:
BufWriteCstr(&b, "world");
BufIter it = BufIterFromBuf(&b);
const char *s1 = BufReadCstr(&it);
const char *s2 = BufReadCstr(&it);- In
Buf.c:220:
BufAppendFmt(&b, "{<2r}{>4r}{<8r}", (u16)0x1234, (u32)0xDEADBEEF, (u64)0x0102030405060708ull);
BufIter it = BufIterFromBuf(&b);
u16 v16;
u32 v32;- In
Buf.c:238:
BufAppendFmt(&b, "{<2r}", (u16)0xABCD); // only 2 bytes; reader wants 6
BufIter it = BufIterFromBuf(&b);
size entry = it.pos;
u16 v16 = 0;- In
Buf.c:253:
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;- In
Iter.Deadend.c:7:
#include "../Util/TestRunner.h"
typedef Iter(const u8) BufIter;
static BufIter from(const u8 *p, size n) {- In
Iter.Deadend.c:9:
typedef Iter(const u8) BufIter;
static BufIter from(const u8 *p, size n) {
BufIter it = {.data = p, .length = n, .pos = 0, .alignment = 1, .dir = 1};
return it;
static BufIter from(const u8 *p, size n) {
BufIter it = {.data = p, .length = n, .pos = 0, .alignment = 1, .dir = 1};
return it;
} bool deadend_must_read_eof(void) {
const u8 buf[1] = {1};
BufIter it = from(buf, 1);
u8 v;
IterRead(&it, &v); // consume sole element
bool deadend_must_peek_out_of_range(void) {
const u8 buf[1] = {1};
BufIter it = from(buf, 1);
u8 v;
// pos=0, length=1: peek at +1 is out of range.
bool deadend_must_move_overflow(void) {
const u8 buf[3] = {0};
BufIter it = from(buf, 3);
// length=3, dir=+1: move by 4 lands past end.
IterMustMove(&it, 4); bool deadend_must_next_eof(void) {
const u8 buf[1] = {0};
BufIter it = from(buf, 1);
u8 v;
IterRead(&it, &v); // pos=1, exhausted
bool deadend_must_prev_underflow(void) {
const u8 buf[3] = {0};
BufIter it = from(buf, 3);
// pos=0, dir=+1: prev would land at -1.
IterMustPrev(&it);- In
Iter.c:7:
#include "../Util/TestRunner.h"
typedef Iter(const u8) BufIter;
static BufIter from(const u8 *p, size n) {- In
Iter.c:9:
typedef Iter(const u8) BufIter;
static BufIter from(const u8 *p, size n) {
BufIter it = {.data = p, .length = n, .pos = 0, .alignment = 1, .dir = 1};
return it;- In
Iter.c:10:
static BufIter from(const u8 *p, size n) {
BufIter it = {.data = p, .length = n, .pos = 0, .alignment = 1, .dir = 1};
return it;
}- In
Iter.c:14:
}
static BufIter from_rev(const u8 *p, size n) {
BufIter it = {.data = p, .length = n, .pos = n - 1, .alignment = 1, .dir = -1};
return it;- In
Iter.c:15:
static BufIter from_rev(const u8 *p, size n) {
BufIter it = {.data = p, .length = n, .pos = n - 1, .alignment = 1, .dir = -1};
return it;
}- In
Iter.c:21:
bool test_iter_remaining_forward(void) {
const u8 buf[3] = {1, 2, 3};
BufIter it = from(buf, 3);
if (IterRemainingLength(&it) != 3) {
return false;- In
Iter.c:37:
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;- In
Iter.c:53:
bool test_iter_read_forward(void) {
const u8 buf[3] = {10, 20, 30};
BufIter it = from(buf, 3);
u8 v = 0;
if (!IterRead(&it, &v) || v != 10) {- In
Iter.c:69:
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) {- In
Iter.c:85:
bool test_iter_read_eof_leaves_state(void) {
const u8 buf[1] = {7};
BufIter it = from(buf, 1);
u8 v = 0;
IterRead(&it, &v); // consume sole element
- In
Iter.c:100:
bool test_iter_peek_in_range(void) {
const u8 buf[4] = {5, 6, 7, 8};
BufIter it = from(buf, 4);
IterMove(&it, 2); // pos == 2
u8 v;- In
Iter.c:117:
bool test_iter_peek_out_of_range(void) {
const u8 buf[2] = {1, 2};
BufIter it = from(buf, 2);
u8 v = 0xAA;
if (IterPeekAt(&it, 2, &v)) {- In
Iter.c:133:
bool test_iter_move_forward_basic(void) {
const u8 buf[5] = {0};
BufIter it = from(buf, 5);
if (!IterMove(&it, 3) || it.pos != 3) {
return false;- In
Iter.c:145:
bool test_iter_move_forward_to_exhausted(void) {
const u8 buf[3] = {0};
BufIter it = from(buf, 3);
if (!IterMove(&it, 3) || it.pos != 3) {
return false;- In
Iter.c:154:
bool test_iter_move_forward_overflow(void) {
const u8 buf[3] = {0};
BufIter it = from(buf, 3);
size before = it.pos;
if (IterMove(&it, 4)) {- In
Iter.c:164:
bool test_iter_move_forward_underflow(void) {
const u8 buf[3] = {0};
BufIter it = from(buf, 3);
if (IterMove(&it, -1)) {
return false;- In
Iter.c:173:
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) || it.pos != 2) {- In
Iter.c:187:
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) || it.pos != (size)-1) {- In
Iter.c:197:
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 = it.pos;- In
Iter.c:208:
bool test_iter_next_prev(void) {
const u8 buf[3] = {0};
BufIter it = from(buf, 3);
if (!IterNext(&it) || it.pos != 1) {
return false;- In
Pe.c:148:
typedef struct PeContext {
PeFile *out;
BufIter file; // bounds for the whole image
u32 nt_offset; // offset of NT signature
u16 num_sections;- In
Pe.c:169:
}
u32 e_lfanew;
BufIter c = BufIterFromMemory(ctx->out->data + DOS_E_LFANEW_OFFSET, ctx->out->data_size - DOS_E_LFANEW_OFFSET);
if (!BufReadU32LE(&c, &e_lfanew))
return false;- In
Pe.c:182:
// 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(ctx->out->data + ctx->nt_offset, ctx->out->data_size - ctx->nt_offset);
u32 sig;
if (!BufReadU32LE(&c, &sig) || sig != NT_SIGNATURE) {- In
Pe.c:212:
return false;
}
BufIter c = BufIterFromMemory(ctx->out->data + opt_offset, ctx->opt_hdr_size);
u16 magic;- In
Pe.c:362:
static bool pe_decode_sections(PeContext *ctx, u64 opt_offset) {
u64 sec_offset = opt_offset + ctx->opt_hdr_size;
BufIter c = BufIterFromMemory(ctx->out->data + sec_offset, ctx->out->data_size - sec_offset);
for (u32 i = 0; i < ctx->num_sections; ++i) {- In
Pe.c:431:
for (u32 i = 0; i < num_entries; ++i) {
u64 entry_off = dir_offset + (u64)i * DEBUG_ENTRY_SIZE;
BufIter c = BufIterFromMemory(ctx->out->data + entry_off, ctx->out->data_size - entry_off);
u32 charac, ts, type, sz, raddr, rptr;
u16 ver_maj, ver_min;- In
Pe.c:450:
if (sz < 4 + 16 + 4 + 1)
continue;
BufIter cv_cur = BufIterFromMemory(ctx->out->data + rptr, sz);
u32 cv_sig;
if (!BufReadU32LE(&cv_cur, &cv_sig))- In
Dns.c:91:
//
// 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;- In
Dns.c:161:
// 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);- In
Dns.c:214:
// 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;- In
Dns.c:235:
// ---------------------------------------------------------------------------
static bool decode_record_list(BufIter *it, u16 count, DnsRecords *out, Allocator *alloc) {
for (u16 i = 0; i < count; ++i) {
DnsRecord rec = {0};- In
Dns.c:260:
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)) {- In
Pdb.c:145:
return false;
}
BufIter sb = BufIterFromMemory(self->data + 32, self->data_size - 32);
u32 free_blk, num_blocks, unknown;
if (!BufReadFmt(- In
Pdb.c:226:
return false;
}
BufIter dir_iter = BufIterFromMemory(self->stream_dir, self->stream_dir_size);
if (!BufReadU32LE(&dir_iter, &self->num_streams))
return false;- In
Pdb.c:292:
if (!stream_read(self, 1, 0, buf, sizeof(buf)))
return false;
BufIter bi = BufIterFromMemory(buf, sizeof(buf));
if (!BufReadFmt(&bi, FMT_PDB_INFO_LE, self->info.version, self->info.signature, self->info.age)) {
LOG_ERROR("PDB: info stream prefix truncated");- In
Pdb.c:331:
return r;
BufIter bi = BufIterFromMemory(hdr, DBI_HEADER_SIZE);
u32 version_sig, version_hdr, age, mod_size, seccontrib, secmap, srcinfo, tsm, mfc_tsm_idx, optdbg_size, ec_size,
padding;- In
Pdb.c:436:
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);- In
Pdb.c:518:
// 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;- In
Dwarf.c:93:
// 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));- In
Dwarf.c:170:
// 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 (cur->pos < cur->length && cur->data[cur->pos] != 0) {
if (!BufReadCstr(cur))- In
Dwarf.c:223:
// 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 (cur.pos < cur.length && cur.data[cur.pos] != 0) {- In
Dwarf.c:327:
static bool run_line_program(
BufIter cur,
const u8 *prog_end,
const LineProgHeader *hdr,- In
Dwarf.c:520:
U64Vec pending_dir_offsets = VecInitT(pending_dir_offsets, alloc);
BufIter section_cur = BufIterFromMemory(elf->data + line_section->offset, line_section->size);
bool ok = true;- In
Dwarf.c:526:
const u8 *unit_start = section_cur.data + section_cur.pos;
u32 unit_length = 0;
BufIter peek = section_cur;
if (!BufReadU32LE(&peek, &unit_length)) {
ok = false;- In
Dwarf.c:546:
// 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)) {- In
Dwarf.c:562:
// up to the end of this CU.
size strings_start_pos = (size)(hdr.strings_start - section_cur.data);
BufIter str_cur = BufIterFromMemory(section_cur.data, unit_end_pos);
str_cur.pos = strings_start_pos;
if (!collect_cu_strings(str_cur, &out->string_pool, &cs)) {- In
Dwarf.c:571:
// Skip past the tables to find the program body start.
BufIter prog_anchor = BufIterFromMemory(section_cur.data, unit_end_pos);
prog_anchor.pos = strings_start_pos;
if (!skip_line_program_tables(&prog_anchor)) {- In
Dwarf.c:579:
}
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);- In
DwarfUnwind.c:61:
// 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, const u8 *section_data = elf->data + eh->offset;
BufIter section_cur = BufIterFromMemory(section_data, eh->size);
while (IterRemainingLength(§ion_cur) > 0) {
const u8 *rec_start = section_cur.data + section_cur.pos; // 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(section_cur.data + section_cur.pos, length32 - 4);
if (parse_cie(&body, cie_offset, &cie)) {
if (!VecPushBackR(&out->cies, cie)) { u64 cie_offset = id_field_off - id;
DwarfFde fde;
BufIter body = BufIterFromMemory(section_cur.data + section_cur.pos, length32 - 4);
if (parse_fde(&body, rec_start, cie_offset, out, section_data, eh->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) {- In
DwarfInfo.c:105:
// 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) {- In
DwarfInfo.c:195:
// 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) {- In
DwarfInfo.c:335:
static bool walk_cu_dies(
BufIter cu_cur, // positioned past CU header
const AbbrevTable *abbrevs,
u8 addr_size,- In
DwarfInfo.c:512:
}
BufIter info_cur = BufIterFromMemory(info_bytes, info_size);
PendingFns pending = VecInitT(pending, alloc);- In
DwarfInfo.c:560:
AbbrevTable abbrevs;
BufIter abbrev_cur = BufIterFromMemory(abbrev_bytes + abbrev_offset, abbrev_size - abbrev_offset);
if (!parse_abbrev_table(abbrev_cur, &abbrevs, alloc)) {
ok = false;- In
DwarfInfo.c:568:
// DIE iter spans the DIE body within this CU (from current
// info_cur position up to unit_end_pos).
BufIter die_cur = BufIterFromMemory(info_cur.data + info_cur.pos, unit_end_pos - info_cur.pos);
if (!walk_cu_dies(die_cur, &abbrevs, addr_size, str_bytes, str_size, &out->string_pool, &pending)) {
abbrev_table_deinit(&abbrevs);- In
MachO.c:130:
return false;
}
BufIter c = BufIterFromMemory(m->data, m->data_size);
u32 magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags, reserved;
if (!BufReadFmt(- In
MachO.c:182:
// cmd_p layout: cmd(4) cmdsize(4) segname[16] then FMT_MACHO_SEGMENT64_BODY_LE.
copy_fixed16(seg.name, cmd_p + 8);
BufIter c = BufIterFromMemory(cmd_p + 24, cmdsize - 24);
u32 maxprot, initprot;
if (!BufReadFmt(- In
MachO.c:216:
copy_fixed16(sec.section, s + 0);
copy_fixed16(sec.segment, s + 16);
BufIter sc = BufIterFromMemory(s + 32, SECT64_SIZE - 32);
u32 align, reloff, nreloc, reserved1, reserved2, reserved3;
if (!BufReadFmt(- In
MachO.c:253:
return false;
}
BufIter c = BufIterFromMemory(cmd_p + 8, cmdsize - 8);
if (!BufReadFmt(&c, FMT_MACHO_SYMTAB_BODY_LE, ctx->symoff, ctx->nsyms, ctx->stroff, ctx->strsize)) {
LOG_ERROR("MachO: LC_SYMTAB body truncated");- In
MachO.c:283:
return false;
const u8 *cmd_p = ctx->out->data + cur;
BufIter pc = BufIterFromMemory(cmd_p, end - cur);
u32 cmd, cmdsize;
if (!BufReadFmt(&pc, FMT_MACHO_LC_PREFIX_LE, cmd, cmdsize)) {- In
MachO.c:339:
}
const u8 *str_base = ctx->out->data + ctx->stroff;
BufIter tab = BufIterFromMemory(ctx->out->data + ctx->symoff, (u64)ctx->nsyms * NLIST64_SIZE);
for (u32 i = 0; i < ctx->nsyms; ++i) {
u32 n_strx;- In
Io.c:815:
// ---------------------------------------------------------------------------
bool buf_read_fmt(BufIter *iter, const char *fmtstr, TypeSpecificIO *argv, u64 argc) {
if (!iter || !fmtstr) {
LOG_FATAL("buf_read_fmt: NULL iter or fmtstr");- In
Buf.h:38:
/// Construct a BufIter over `[data, data + length)`.
#define BufIterFromMemory(data_, length_) \
((BufIter) {.data = (data_), .length = (length_), .pos = 0, .alignment = 1, .dir = 1})
/// Construct a BufIter over a Buf's bytes.
- In
Buf.h:64:
// ---------------------------------------------------------------------------
static inline bool BufReadU8(BufIter *c, u8 *out) {
if (c->pos >= c->length) {
return false;- In
Buf.h:72:
}
static inline bool BufReadU16LE(BufIter *c, u16 *out) {
if (c->pos + 2 > c->length) {
return false;- In
Buf.h:81:
}
static inline bool BufReadU16BE(BufIter *c, u16 *out) {
if (c->pos + 2 > c->length) {
return false;- In
Buf.h:90:
}
static inline bool BufReadU32LE(BufIter *c, u32 *out) {
if (c->pos + 4 > c->length) {
return false;- In
Buf.h:100:
}
static inline bool BufReadU32BE(BufIter *c, u32 *out) {
if (c->pos + 4 > c->length) {
return false;- In
Buf.h:110:
}
static inline bool BufReadU64LE(BufIter *c, u64 *out) {
if (c->pos + 8 > c->length) {
return false;- In
Buf.h:123:
}
static inline bool BufReadU64BE(BufIter *c, u64 *out) {
if (c->pos + 8 > c->length) {
return false;- In
Buf.h:137:
/// LEB128 unsigned. Fails on truncation or width overflow.
static inline bool BufReadULeb128(BufIter *c, u64 *out) {
u64 result = 0;
u32 shift = 0;- In
Buf.h:157:
/// LEB128 signed. Decoded in unsigned space and reinterpreted to
/// avoid signed-shift UB at high bit positions.
static inline bool BufReadSLeb128(BufIter *c, i64 *out) {
u64 uresult = 0;
u32 shift = 0;- In
Buf.h:184:
/// NUL-terminated string starting at the cursor. Returns the start;
/// advances past the terminator. NULL on truncation.
static inline const char *BufReadCstr(BufIter *c) {
const char *s = (const char *)(c->data + c->pos);
while (c->pos < c->length && c->data[c->pos] != 0) {- In
Buf.h:307:
// ---------------------------------------------------------------------------
bool buf_read_fmt(BufIter *iter, const char *fmtstr, TypeSpecificIO *argv, u64 argc);
bool buf_append_fmt(Buf *out, const char *fmtstr, TypeSpecificIO *argv, u64 argc);
bool buf_write_fmt(Buf *out, const char *fmtstr, TypeSpecificIO *argv, u64 argc);
Last updated on