Skip to content

BufLength

Description

Current byte count of b. Read-only (the comma-expression form rejects BufLength(b) = n at compile time – use BufResize).

Usage example (Cross-references)

Usage examples (Cross-references)
        bool             ok      = render_binary_fmt(&tmp, fmtstr, argv, argc);
        if (ok) {
            if (offset > BufLength(out) || StrLen(&tmp) > BufLength(out) - offset) {
                LOG_ERROR(
                    "BufPatchFmt: write of {} bytes at offset {} exceeds buf length {}",
                    StrLen(&tmp),
                    offset,
                    BufLength(out)
                );
                ok = false;
    static bool decode_header(MachoContext *ctx) {
        Macho *m = ctx->out;
        if (BufLength(&m->data) < MH_HEADER_64_SIZE) {
            LOG_ERROR("MachO: file too small for header");
            return false;
    
    static bool walk_load_commands(MachoContext *ctx) {
        if ((u64)MH_HEADER_64_SIZE + ctx->sizeofcmds > BufLength(&ctx->out->data)) {
            LOG_ERROR("MachO: load commands overrun file");
            return false;
        }
        u64 tab_end = (u64)ctx->symoff + (u64)ctx->nsyms * NLIST64_SIZE;
        if (tab_end > BufLength(&ctx->out->data)) {
            LOG_ERROR("MachO: symtab past EOF");
            return false;
        }
        u64 str_end = (u64)ctx->stroff + ctx->strsize;
        if (str_end > BufLength(&ctx->out->data)) {
            LOG_ERROR("MachO: symbol strtab past EOF");
            return false;
    
    static bool elf_range_ok(const Elf *self, u64 offset, u64 size) {
        if (offset > BufLength(&self->data))
            return false;
        if (size > BufLength(&self->data))
        if (offset > BufLength(&self->data))
            return false;
        if (size > BufLength(&self->data))
            return false;
        // After the two checks above both `offset` and `size` are
        // After the two checks above both `offset` and `size` are
        // bounded by `data_size`; subtracting cannot wrap.
        if (size > BufLength(&self->data) - offset)
            return false;
        return true;
    
    static bool elf_decode_header(Elf *self) {
        if (BufLength(&self->data) < EI_NIDENT + EHDR64_SIZE_AFTER_IDENT) {
            LOG_ERROR("Elf: file too small for ELF64 header ({} bytes)", (u64)BufLength(&self->data));
            return false;
    static bool elf_decode_header(Elf *self) {
        if (BufLength(&self->data) < EI_NIDENT + EHDR64_SIZE_AFTER_IDENT) {
            LOG_ERROR("Elf: file too small for ELF64 header ({} bytes)", (u64)BufLength(&self->data));
            return false;
        }
    static const u8 *block_ptr(const Pdb *self, u32 block_id) {
        u64 off = (u64)block_id * self->block_size;
        if (off + self->block_size > BufLength(&self->data))
            return NULL;
        return BufData(&self->data) + off;
    
    static bool parse_superblock(Pdb *self, u32 *out_num_dir_bytes, u32 *out_block_map_addr) {
        if (BufLength(&self->data) < SUPERBLOCK_SIZE) {
            LOG_ERROR("PDB: file too small for MSF superblock");
            return false;
            return false;
        }
        BufIter sb = BufIterFromMemory(BufData(&self->data) + 32, BufLength(&self->data) - 32);
        u32     free_blk, num_blocks, unknown;
        if (!BufReadFmt(
            return false;
        }
        if ((u64)num_blocks * self->block_size != BufLength(&self->data)) {
            // Some PDBs come with trailing padding; the spec says num_blocks
            // * block_size should equal file size. We warn but don't fail.
    // DOS header gives us e_lfanew, the offset to the NT headers.
    static bool pe_decode_dos(PeContext *ctx) {
        if (BufLength(&ctx->out->data) < 64) {
            LOG_ERROR("PE: file too small for DOS header");
            return false;
        BufIter c = BufIterFromMemory(
            BufData(&ctx->out->data) + DOS_E_LFANEW_OFFSET,
            BufLength(&ctx->out->data) - DOS_E_LFANEW_OFFSET
        );
        if (!BufReadU32LE(&c, &e_lfanew))
        if (!BufReadU32LE(&c, &e_lfanew))
            return false;
        if (e_lfanew >= BufLength(&ctx->out->data)) {
            LOG_ERROR("PE: e_lfanew past EOF");
            return false;
    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;
        if (!BufReadU32LE(&c, &sig) || sig != NT_SIGNATURE) {
    // NumberOfRvaAndSizes, and the DataDirectory[DEBUG] entry.
    static bool pe_decode_optional(PeContext *ctx, u64 opt_offset) {
        if (opt_offset > BufLength(&ctx->out->data) || ctx->opt_hdr_size > BufLength(&ctx->out->data) - opt_offset) {
            LOG_ERROR("PE: optional header overruns file");
            return false;
    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) {
        };
        u32 num_entries = ctx->debug_dir_size / DEBUG_ENTRY_SIZE;
        if (dir_offset + (u64)num_entries * DEBUG_ENTRY_SIZE > BufLength(&ctx->out->data)) {
            LOG_ERROR("PE: debug directory overruns file");
            return;
        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 (type != IMAGE_DEBUG_TYPE_CODEVIEW)
                continue;
            if (rptr + (u64)sz > BufLength(&ctx->out->data)) {
                LOG_ERROR("PE: codeview record points outside file");
                continue;
            if (rva >= vstart && (u64)rva < vend) {
                u64 off = (u64)s->raw_offset + ((u64)rva - vstart);
                if (off >= BufLength(&self->data))
                    return false;
                *out_offset = off;
        DefaultAllocator alloc = DefaultAllocatorInit();
        Buf              b     = BufInit(&alloc);
        bool             ok    = BufLength(&b) == 0;
        BufWriteU8(&b, 0x42);
        BufWriteU8(&b, 0x43);
        BufWriteU8(&b, 0x42);
        BufWriteU8(&b, 0x43);
        ok = ok && BufLength(&b) == 2 && BufData(&b)[0] == 0x42 && BufData(&b)[1] == 0x43;
        BufClear(&b);
        ok = ok && BufLength(&b) == 0;
        ok = ok && BufLength(&b) == 2 && BufData(&b)[0] == 0x42 && BufData(&b)[1] == 0x43;
        BufClear(&b);
        ok = ok && BufLength(&b) == 0;
        BufDeinit(&b);
        DefaultAllocatorDeinit(&alloc);
            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // u64 BE
        };
        bool ok = BufLength(&b) == sizeof(expect) && MemCompare(BufData(&b), expect, sizeof(expect)) == 0;
        BufDeinit(&b);
        DefaultAllocatorDeinit(&alloc);
        BufWriteULeb128(&b, 16384);
        const u8 expect_uleb[] = {0x00, 0x7F, 0x80, 0x01, 0x80, 0x80, 0x01};
        if (BufLength(&b) != sizeof(expect_uleb) || MemCompare(BufData(&b), expect_uleb, sizeof(expect_uleb)) != 0) {
            BufDeinit(&b);
            DefaultAllocatorDeinit(&alloc);
        BufWriteSLeb128(&b, -64);
        const u8 expect_sleb[] = {0x00, 0x7F, 0xC0, 0x00, 0x40};
        bool ok = BufLength(&b) == sizeof(expect_sleb) && MemCompare(BufData(&b), expect_sleb, sizeof(expect_sleb)) == 0;
    
        BufDeinit(&b);
        BufWriteZstr(&b, "hi");
        const u8 expect[] = {'h', 'i', 0};
        bool     ok       = BufLength(&b) == sizeof(expect) && MemCompare(BufData(&b), expect, sizeof(expect)) == 0;
        BufDeinit(&b);
        DefaultAllocatorDeinit(&alloc);
        bool     ok       = BufAppendFmt(&b, "{<2r}{>4r}", (u16)0xCAFE, (u32)0xDEADBEEF);
        const u8 expect[] = {0x99, 0xFE, 0xCA, 0xDE, 0xAD, 0xBE, 0xEF};
        ok                = ok && BufLength(&b) == sizeof(expect) && MemCompare(BufData(&b), expect, sizeof(expect)) == 0;
        BufDeinit(&b);
        DefaultAllocatorDeinit(&alloc);
        bool     ok       = BufWriteFmt(&b, "{<2r}", (u16)0x1234);
        const u8 expect[] = {0x34, 0x12};
        ok                = ok && BufLength(&b) == sizeof(expect) && MemCompare(BufData(&b), expect, sizeof(expect)) == 0;
        BufDeinit(&b);
        DefaultAllocatorDeinit(&alloc);
            0x11
        };
        ok = ok && BufLength(&b) == sizeof(expect) && MemCompare(BufData(&b), expect, sizeof(expect)) == 0;
    
        // Out-of-range patch must fail and leave the buf unchanged.
        // Out-of-range patch must fail and leave the buf unchanged.
        Buf snapshot = BufInit(&alloc);
        BufPushBytes(&snapshot, BufData(&b), BufLength(&b));
        ok = ok && !BufPatchFmt(&b, BufLength(&b), "{<2r}", (u16)0);
        ok = ok && BufLength(&b) == BufLength(&snapshot);
        Buf snapshot = BufInit(&alloc);
        BufPushBytes(&snapshot, BufData(&b), BufLength(&b));
        ok = ok && !BufPatchFmt(&b, BufLength(&b), "{<2r}", (u16)0);
        ok = ok && BufLength(&b) == BufLength(&snapshot);
        ok = ok && MemCompare(BufData(&b), BufData(&snapshot), BufLength(&b)) == 0;
        BufPushBytes(&snapshot, BufData(&b), BufLength(&b));
        ok = ok && !BufPatchFmt(&b, BufLength(&b), "{<2r}", (u16)0);
        ok = ok && BufLength(&b) == BufLength(&snapshot);
        ok = ok && MemCompare(BufData(&b), BufData(&snapshot), BufLength(&b)) == 0;
        BufDeinit(&snapshot);
        ok = ok && !BufPatchFmt(&b, BufLength(&b), "{<2r}", (u16)0);
        ok = ok && BufLength(&b) == BufLength(&snapshot);
        ok = ok && MemCompare(BufData(&b), BufData(&snapshot), BufLength(&b)) == 0;
        BufDeinit(&snapshot);
                                      0x00, 0x00, 0x07, 'e',  'x',  'a',  'm',  'p',  'l',  'e',
                                      0x03, 'c',  'o',  'm',  0x00, 0x00, 0x01, 0x00, 0x01};
        bool            match      = ok && BufLength(&buf) == sizeof(expected);
        for (u64 i = 0; match && i < sizeof(expected); ++i) {
            if (BufData(&buf)[i] != expected[i]) {
        bool ok = DnsBuildQuery(&no_dot, 1, "a.b.c", DNS_TYPE_AAAA) && DnsBuildQuery(&w_dot, 1, "a.b.c.", DNS_TYPE_AAAA);
    
        bool match = ok && BufLength(&no_dot) == BufLength(&w_dot);
        for (u64 i = 0; match && i < BufLength(&no_dot); ++i) {
            if (BufData(&no_dot)[i] != BufData(&w_dot)[i]) {
    
        bool match = ok && BufLength(&no_dot) == BufLength(&w_dot);
        for (u64 i = 0; match && i < BufLength(&no_dot); ++i) {
            if (BufData(&no_dot)[i] != BufData(&w_dot)[i]) {
                match = false;
Last updated on