Skip to content

ZstrLen

Description

Get length of a null-terminated string.

Parameters

Name Direction Description
str in Null-terminated string.

Success

Returns number of characters before null terminator.

Failure

Aborts via LOG_FATAL when str is NULL.

Usage example (Cross-references)

Usage examples (Cross-references)
    /// TAGS: StrIter, Construct, Zstr
    ///
    #define StrIterFromZstr(s) ((StrIter) {.data = (s), .length = ZstrLen((s)), .pos = 0, .alignment = 1, .dir = 1})
    
    ///
    ///
    #define StrInitFromZstr(...)       OVERLOAD(StrInitFromZstr, __VA_ARGS__)
    #define StrInitFromZstr_1(zstr)    StrInitFromCstr_2((zstr), ZstrLen(zstr))
    #define StrInitFromZstr_2(zstr, a) StrInitFromCstr_3((zstr), ZstrLen(zstr), (a))
    #define StrInitFromZstr(...)       OVERLOAD(StrInitFromZstr, __VA_ARGS__)
    #define StrInitFromZstr_1(zstr)    StrInitFromCstr_2((zstr), ZstrLen(zstr))
    #define StrInitFromZstr_2(zstr, a) StrInitFromCstr_3((zstr), ZstrLen(zstr), (a))
    
    ///
    #define StrInsertMany(...) OVERLOAD(StrInsertMany, __VA_ARGS__)
    #define StrInsertMany_3(str, zstr, idx)                                                                            \
        _Generic((zstr), Zstr: VecInsertRangeR((str), (Zstr)(zstr), (idx), ZstrLen((Zstr)(zstr))), char *: VecInsertRangeR((str), (Zstr)(zstr), (idx), ZstrLen((Zstr)(zstr))))
    #define StrInsertMany_4(str, cstr, cstr_len, idx)                                                                  \
        _Generic((cstr), Zstr: VecInsertRangeR((str), (Zstr)(cstr), (idx), (cstr_len)), char *: VecInsertRangeR((str), (Zstr)(cstr), (idx), (cstr_len)))
    #define StrMustInsertMany(...) OVERLOAD(StrMustInsertMany, __VA_ARGS__)
    #define StrMustInsertMany_3(str, zstr, idx)                                                                        \
        _Generic((zstr), Zstr: VecMustInsertRangeR((str), (Zstr)(zstr), (idx), ZstrLen((Zstr)(zstr))), char *: VecMustInsertRangeR((str), (Zstr)(zstr), (idx), ZstrLen((Zstr)(zstr))))
    #define StrMustInsertMany_4(str, cstr, cstr_len, idx)                                                              \
        _Generic((cstr), Zstr: VecMustInsertRangeR((str), (Zstr)(cstr), (idx), (cstr_len)), char *: VecMustInsertRangeR((str), (Zstr)(cstr), (idx), (cstr_len)))
    #define StrInsertManyFast(...) OVERLOAD(StrInsertManyFast, __VA_ARGS__)
    #define StrInsertManyFast_3(str, zstr, idx)                                                                        \
        _Generic((zstr), Zstr: VecInsertRangeFastR((str), (Zstr)(zstr), (idx), ZstrLen((Zstr)(zstr))), char *: VecInsertRangeFastR((str), (Zstr)(zstr), (idx), ZstrLen((Zstr)(zstr))))
    #define StrInsertManyFast_4(str, cstr, cstr_len, idx)                                                              \
        _Generic((cstr), Zstr: VecInsertRangeFastR((str), (Zstr)(cstr), (idx), (cstr_len)), char *: VecInsertRangeFastR((str), (Zstr)(cstr), (idx), (cstr_len)))
    #define StrMustInsertManyFast(...) OVERLOAD(StrMustInsertManyFast, __VA_ARGS__)
    #define StrMustInsertManyFast_3(str, zstr, idx)                                                                    \
        _Generic((zstr), Zstr: VecMustInsertRangeFastR((str), (Zstr)(zstr), (idx), ZstrLen((Zstr)(zstr))), char *: VecMustInsertRangeFastR((str), (Zstr)(zstr), (idx), ZstrLen((Zstr)(zstr))))
    #define StrMustInsertManyFast_4(str, cstr, cstr_len, idx)                                                          \
        _Generic((cstr), Zstr: VecMustInsertRangeFastR((str), (Zstr)(cstr), (idx), (cstr_len)), char *: VecMustInsertRangeFastR((str), (Zstr)(cstr), (idx), (cstr_len)))
    #define StrPushBackMany(...) OVERLOAD(StrPushBackMany, __VA_ARGS__)
    #define StrPushBackMany_2(str, zstr)                                                                               \
        _Generic((zstr), Zstr: VecPushBackArrR((str), (Zstr)(zstr), ZstrLen((Zstr)(zstr))), char *: VecPushBackArrR((str), (Zstr)(zstr), ZstrLen((Zstr)(zstr))))
    #define StrPushBackMany_3(str, cstr, cstr_len)                                                                     \
        _Generic((cstr), Zstr: VecPushBackArrR((str), (Zstr)(cstr), (cstr_len)), char *: VecPushBackArrR((str), (Zstr)(cstr), (cstr_len)))
    #define StrMustPushBackMany(...) OVERLOAD(StrMustPushBackMany, __VA_ARGS__)
    #define StrMustPushBackMany_2(str, zstr)                                                                           \
        _Generic((zstr), Zstr: VecMustPushBackArrR((str), (Zstr)(zstr), ZstrLen((Zstr)(zstr))), char *: VecMustPushBackArrR((str), (Zstr)(zstr), ZstrLen((Zstr)(zstr))))
    #define StrMustPushBackMany_3(str, cstr, cstr_len)                                                                 \
        _Generic((cstr), Zstr: VecMustPushBackArrR((str), (Zstr)(cstr), (cstr_len)), char *: VecMustPushBackArrR((str), (Zstr)(cstr), (cstr_len)))
    #define StrPushFrontMany(...) OVERLOAD(StrPushFrontMany, __VA_ARGS__)
    #define StrPushFrontMany_2(str, zstr)                                                                              \
        _Generic((zstr), Zstr: VecPushFrontArrR((str), (Zstr)(zstr), ZstrLen((Zstr)(zstr))), char *: VecPushFrontArrR((str), (Zstr)(zstr), ZstrLen((Zstr)(zstr))))
    #define StrPushFrontMany_3(str, cstr, cstr_len)                                                                    \
        _Generic((cstr), Zstr: VecPushFrontArrR((str), (Zstr)(cstr), (cstr_len)), char *: VecPushFrontArrR((str), (Zstr)(cstr), (cstr_len)))
    #define StrMustPushFrontMany(...) OVERLOAD(StrMustPushFrontMany, __VA_ARGS__)
    #define StrMustPushFrontMany_2(str, zstr)                                                                          \
        _Generic((zstr), Zstr: VecMustPushFrontArrR((str), (Zstr)(zstr), ZstrLen((Zstr)(zstr))), char *: VecMustPushFrontArrR((str), (Zstr)(zstr), ZstrLen((Zstr)(zstr))))
    #define StrMustPushFrontMany_3(str, cstr, cstr_len)                                                                \
        _Generic((cstr), Zstr: VecMustPushFrontArrR((str), (Zstr)(cstr), (cstr_len)), char *: VecMustPushFrontArrR((str), (Zstr)(cstr), (cstr_len)))
    #define StrPushFrontManyFast(...) OVERLOAD(StrPushFrontManyFast, __VA_ARGS__)
    #define StrPushFrontManyFast_2(str, zstr)                                                                          \
        _Generic((zstr), Zstr: VecPushFrontArrFastR((str), (Zstr)(zstr), ZstrLen((Zstr)(zstr))), char *: VecPushFrontArrFastR((str), (Zstr)(zstr), ZstrLen((Zstr)(zstr))))
    #define StrPushFrontManyFast_3(str, cstr, cstr_len)                                                                \
        _Generic((cstr), Zstr: VecPushFrontArrFastR((str), (Zstr)(cstr), (cstr_len)), char *: VecPushFrontArrFastR((str), (Zstr)(cstr), (cstr_len)))
    #define StrMustPushFrontManyFast(...) OVERLOAD(StrMustPushFrontManyFast, __VA_ARGS__)
    #define StrMustPushFrontManyFast_2(str, zstr)                                                                      \
        _Generic((zstr), Zstr: VecMustPushFrontArrFastR((str), (Zstr)(zstr), ZstrLen((Zstr)(zstr))), char *: VecMustPushFrontArrFastR((str), (Zstr)(zstr), ZstrLen((Zstr)(zstr))))
    #define StrMustPushFrontManyFast_3(str, cstr, cstr_len)                                                            \
        _Generic((cstr), Zstr: VecMustPushFrontArrFastR((str), (Zstr)(cstr), (cstr_len)), char *: VecMustPushFrontArrFastR((str), (Zstr)(cstr), (cstr_len)))
                    size_t total_len = 0;
                    VecForeach(vec, str) {
                        total_len += ZstrLen(str);
                    }
                    (void)total_len; // Suppress unused variable warning
                    size_t total_len = 0;
                    VecForeachIdx(vec, str, idx) {
                        total_len += ZstrLen(str) + idx;
                    }
                    (void)total_len; // Suppress unused variable warning
                    size_t total_len = 0;
                    VecForeachPtr(vec, str_ptr) {
                        total_len += ZstrLen(*str_ptr);
                    }
                    (void)total_len; // Suppress unused variable warning
                    size_t total_len = 0;
                    VecForeachPtrIdx(vec, str_ptr, idx) {
                        total_len += ZstrLen(*str_ptr) + idx;
                    }
                    (void)total_len; // Suppress unused variable warning
                    size_t total_len = 0;
                    VecForeachReverse(vec, str) {
                        total_len += ZstrLen(str);
                    }
                    (void)total_len; // Suppress unused variable warning
                    size_t total_len = 0;
                    VecForeachReverseIdx(vec, str, idx) {
                        total_len += ZstrLen(str) + idx;
                    }
                    (void)total_len; // Suppress unused variable warning
                    size_t total_len = 0;
                    VecForeachPtrReverse(vec, str_ptr) {
                        total_len += ZstrLen(*str_ptr);
                    }
                    (void)total_len; // Suppress unused variable warning
                    size_t total_len = 0;
                    VecForeachPtrReverseIdx(vec, str_ptr, idx) {
                        total_len += ZstrLen(*str_ptr) + idx;
                    }
                    (void)total_len; // Suppress unused variable warning
                        size_t total_len = 0;
                        VecForeachInRange(vec, str, start, end) {
                            total_len += ZstrLen(str);
                        }
                        (void)total_len; // Suppress unused variable warning
                        size_t total_len = 0;
                        VecForeachInRangeIdx(vec, str, idx, start, end) {
                            total_len += ZstrLen(str) + idx;
                        }
                        (void)total_len; // Suppress unused variable warning
                        size_t total_len = 0;
                        VecForeachPtrInRange(vec, str_ptr, start, end) {
                            total_len += ZstrLen(*str_ptr);
                        }
                        (void)total_len; // Suppress unused variable warning
                        size_t total_len = 0;
                        VecForeachPtrInRangeIdx(vec, str_ptr, idx, start, end) {
                            total_len += ZstrLen(*str_ptr) + idx;
                        }
                        (void)total_len; // Suppress unused variable warning
        Zstr p         = fmtstr;
        Zstr in        = input;
        u64  rem_p     = ZstrLen(fmtstr);
        u64  rem_in    = ZstrLen(in);
        u64  arg_index = 0; // Current argument index
        Zstr in        = input;
        u64  rem_p     = ZstrLen(fmtstr);
        u64  rem_in    = ZstrLen(in);
        u64  arg_index = 0; // Current argument index
            dot = ZstrFindChar(body, '.');
            if (!dot) {
                if (!StrPushBackMany(&result, body, ZstrLen(body))) {
                    goto fail;
                }
    
            prefix = (u64)(dot - body);
            frac   = (u64)ZstrLen(dot + 1);
    
            if (!StrPushBackMany(&result, body, prefix)) {
                // Precision truncates -- see the Str path above for the
                // precision-0 carve-out.
                size len = ZstrLen(xs);
                if (fmt_info->flags & FMT_FLAG_HAS_PRECISION) {
                    if (fmt_info->precision == 0) {
                                            .precision    = 6,
                                            .flags        = FMT_FLAG_NONE,
                                            .max_read_len = (u32)ZstrLen(i),
                                 };
        default_fmt.flags &= ~FMT_FLAG_CHAR;
        default_fmt.flags &= ~FMT_FLAG_CHAR;
        if (!default_fmt.max_read_len) {
            default_fmt.max_read_len = (u32)ZstrLen(i);
        }
    #include <Misra/Std/Zstr.h>
    
    size ZstrLen(Zstr str) {
        if (!str) {
            LOG_FATAL("Invalid arguments");
            LOG_FATAL("Invalid arguments");
        }
        return zstr_dup_n(src, ZstrLen(src), alloc);
    }
        }
    
        *dst = zstr_dup_n(*src, ZstrLen(*src), (Allocator *)alloc);
        return *dst != NULL;
    }
            LOG_FATAL("Invalid arguments");
        }
        return ZstrFindSubstringN(haystack, needle, ZstrLen(needle));
    }
            return haystack;
        }
        size haystack_len = ZstrLen(haystack);
        if (needle_len > haystack_len) {
            return NULL;
    
    StrIters str_split_to_iters_zstr(Str *s, Zstr key) {
        return str_split_to_iters_impl(s, key, ZstrLen(key));
    }
    
    Strs str_split_zstr(Str *s, Zstr key) {
        return str_split_impl(s, key, ZstrLen(key));
    }
        }
    
        return str_index_of_cstr(s, key, ZstrLen(key));
    }
    bool str_starts_with_zstr(const Str *s, Zstr prefix) {
        ValidateStr(s);
        return starts_with(s->data, s->length, prefix, ZstrLen(prefix));
    }
    bool str_ends_with_zstr(const Str *s, Zstr suffix) {
        ValidateStr(s);
        return ends_with(s->data, s->length, suffix, ZstrLen(suffix));
    }
    void str_replace_zstr(Str *s, Zstr match, Zstr replacement, size count) {
        ValidateStr(s);
        str_replace_cstr(s, match, ZstrLen(match), replacement, ZstrLen(replacement), count);
    }
        }
    
        len = (u64)ZstrLen(decimal);
        if (len > 0 && decimal[0] == '+') {
            start = 1;
            LOG_FATAL("Invalid arguments");
        }
        len = (u64)ZstrLen(digits);
        if (len > 0 && digits[0] == '+') {
            start = 1;
        }
    
        len = (u64)ZstrLen(binary);
        if (len >= 2 && binary[0] == '0' && (binary[1] == 'b' || binary[1] == 'B')) {
            start = 2;
        }
    
        len = (u64)ZstrLen(octal);
        if (len >= 2 && octal[0] == '0' && (octal[1] == 'o' || octal[1] == 'O')) {
            start = 2;
        }
    
        len = (u64)ZstrLen(hex);
        return int_try_from_str_radix_impl(out, hex, len, 0, 16, false);
    }
            LOG_FATAL("Invalid arguments");
        }
        return float_try_from_str_impl(out, text, (size)ZstrLen(text));
    }
            LOG_FATAL("str is NULL");
        }
        return bitvec_try_from_str_impl(out, str, (u64)ZstrLen(str), alloc);
    }
        HANDLE hFind = INVALID_HANDLE_VALUE;
        StrInitStack(search_path, MAX_PATH) {
            size path_len = ZstrLen(path);
            if (path_len + 3 > MAX_PATH) {
                LOG_ERROR("dir_get_contents: path too long for MAX_PATH");
                }
    
                direntry.name = StrInitFromCstr(findFileData.cFileName, ZstrLen(findFileData.cFileName), alloc);
                VecPushBack(&dc, direntry);
            } while (FindNextFile(hFind, &findFileData) != 0);
    #    else
                // Linux name is null-terminated; compute length.
                size name_len = ZstrLen(nm);
    #    endif
                // Skip "." and "..".
            LOG_FATAL("DirCreateAll: NULL path");
        }
        size n = ZstrLen(path);
        if (n == 0) {
            return 1;
    
        bool ok        = true;
        size path_len  = ZstrLen(path);
        bool trail_sep = (path_len > 0 && path[path_len - 1] == '/');
        for (size i = 0; i < VecLen(&dc); ++i) {
        MemSet(&entry, 0, sizeof(entry));
    
        if (!StrTryInitFromCstr(&entry.module_path, module_path, ZstrLen(module_path), self->allocator)) {
            return NULL;
        }
            return;
        }
        u64 len = ZstrLen(name);
        while (len > 0 && name[len - 1] == '.') {
            --len;
        // `Str` below holds 256 to keep the bound check and the buffer
        // aligned with one literal.
        if (ZstrLen(hostname) >= 256) {
            LOG_ERROR("DnsResolve: hostname \"{}\" exceeds 255 bytes", hostname);
            return false;
        // never contain colons and the IPv6 case was eliminated by the
        // SocketAddrParse attempt above.
        u64 spec_len = ZstrLen(spec);
        u64 colon_at = spec_len;
        for (u64 i = spec_len; i > 0; --i) {
            // split_host_port NUL-terminates; record the textual length so the
            // Str is internally consistent before any read via StrBegin.
            StrResize(&host, ZstrLen(host_data));
    
            u16 port = 0;
                    break;
                }
                StrResize(&host, ZstrLen(host_data));
                port = FROM_BIG_ENDIAN2(sa->sin_port);
                StrAppendFmt(&out, "{}:{}", (Zstr)host_data, (u32)port);
                    break;
                }
                StrResize(&host, ZstrLen(host_data));
                port = FROM_BIG_ENDIAN2(sa->sin6_port);
                StrAppendFmt(&out, "[{}]:{}", (Zstr)host_data, (u32)port);
        MemSet(&entry, 0, sizeof(entry));
    
        if (!StrTryInitFromCstr(&entry.module_path, module_path, ZstrLen(module_path), self->allocator)) {
            return NULL;
        }
        Zstr        exe = _dyld_get_image_name(0);
        if (exe) {
            *exe_path = StrInitFromCstr(exe, ZstrLen(exe), alloc);
            return exe_path;
        }
        }
    
        lookup = StrInitFromCstr(key, ZstrLen(key), MapAllocator(cfg));
        value  = kvconfig_get_ptr_str(cfg, &lookup);
        StrDeinit(&lookup);
            return false;
        }
        size n       = ZstrLen(text);
        i64  written = FileWrite(&f, text, (u64)n);
        bool ok      = (written == (i64)n);
        FileClose(&f);
    
        bool result = (got == (i64)ZstrLen("hello from file")) && (StrLen(&body) == (size)ZstrLen("hello from file")) &&
                      ZstrCompare(StrBegin(&body), "hello from file") == 0;
    
        Zstr expected = "this is longer than the initial buffer";
        bool result   = (got == (i64)ZstrLen(expected)) && (StrLen(&body) == (size)ZstrLen(expected)) &&
                      ZstrCompare(StrBegin(&body), expected) == 0 && StrCapacity(&body) >= StrLen(&body) + 1;
    
        Zstr payload = "round-trip payload";
        u64  n       = ZstrLen(payload);
    
        i64  wrote = FileWriteAndClose(&path, payload, n);
        FileClose(&f);
    
        ok = ok && (got == (i64)ZstrLen(payload)) && (StrLen(&body) == (size)ZstrLen(payload)) &&
             ZstrCompare(StrBegin(&body), payload) == 0;
    
        Zstr payload = "payload-through-the-convenience-API";
        u64  n       = ZstrLen(payload);
    
        i64  wrote = FileWriteAndClose(&path, payload, n);
        Str              out = StrInit(&a);
        capture_help(p, &out);
        bool ok = (StrLen(&out) == ZstrLen(expected)) && (ZstrCompare(StrBegin(&out), expected) == 0);
        if (!ok) {
            WriteFmt("[a1] help mismatch:\n");
        if (!ok) {
            WriteFmt("[a1] help mismatch:\n");
            WriteFmt("---- expected ({} bytes) ----\n{}\n", ZstrLen(expected), expected);
            WriteFmt("---- actual   ({} bytes) ----\n{}\n", StrLen(&out), StrBegin(&out));
        }
        u64  hn  = StrLen(hay);
        Zstr beg = StrBegin(hay);
        u64  nn  = ZstrLen(needle);
        if (nn == 0)
            return true;
        u64  hn  = StrLen(hay);
        Zstr beg = StrBegin(hay);
        u64  nn  = ZstrLen(needle);
        if (nn == 0)
            return true;
        // Copy name
        if (src->name) {
            size name_len = ZstrLen(src->name);
            dst->name     = fixture_malloc(name_len + 1);
            if (!dst->name)
    
        if (name) {
            size name_len = ZstrLen(name);
            item.name     = (char *)fixture_malloc(name_len + 1);
            if (item.name) {
        // On success the macro advances `z` to the cursor returned by
        // str_read_fmt, which after matching the trailing ';' is end-of-string.
        success = success && (ZstrLen(z) == 0);
    
        Str text = FloatToStr(&val);
        StrReadFmt(z, "{}", val);
    
        success = success && (ZstrLen(z) == 0);
    
        Str text = FloatToStr(&val);
        StrReadFmt(z, "{e}!", val);
    
        success = success && (ZstrLen(z) == 0);
    
        // 1.5e2 == 150
        StrReadFmt(z, "{}", val);
    
        success = success && (ZstrLen(z) == 0);
    
        Str text = FloatToStr(&val);
    
        // Check that it's initialized correctly
        bool result = (StrLen(&s) == ZstrLen(test_str) && ZstrCompare(StrBegin(&s), test_str) == 0);
    
        StrDeinit(&s);
        ValidateStr(&s);
    
        bool result = (StrLen(&s) == ZstrLen(test_str) && ZstrCompare(StrBegin(&s), test_str) == 0);
    
        StrDeinit(&s);
        DefaultAllocator alloc = DefaultAllocatorInit();
    
        Str src = StrInitFromCstr("Hello, World!", ZstrLen("Hello, World!"), &alloc);
    
        Str  dup    = StrInitFromStr(&src, &alloc);
            Zstr dot_pos = ZstrFindChar(StrBegin(&s), '.');
            if (dot_pos) {
                size decimal_places = ZstrLen(dot_pos + 1);
                // Allow for trailing zeros being omitted in some cases
                result = result && (decimal_places <= precision);
        MemCopy(&cv[4], kGuid, 16);
        wr_u32(&cv[20], kAge);
        u64 path_len = ZstrLen(pdb_path);
        MemCopy(&cv[24], pdb_path, path_len + 1);
    }
    
        // Compute S_PUB32 record size based on function name length.
        u64 name_len    = ZstrLen(func_name);
        u16 rec_body    = (u16)(2 + 4 + 4 + 2 + (u16)(name_len + 1)); // kind+flags+off+seg+name
        u32 symrec_size = (u32)(2 + rec_body);                        // includes the rec_len field itself
    static void hosts_push_v4(DnsResolver *r, Zstr name, u8 b0, u8 b1, u8 b2, u8 b3) {
        HostsEntry e = {0};
        e.name       = StrInitFromCstr(name, ZstrLen(name), r->allocator);
        e.ip[0]      = b0;
        e.ip[1]      = b1;
    static void hosts_push_v6(DnsResolver *r, Zstr name, const u8 ip16[16]) {
        HostsEntry e = {0};
        e.name       = StrInitFromCstr(name, ZstrLen(name), r->allocator);
        for (u64 i = 0; i < 16; ++i) {
            e.ip[i] = ip16[i];
        File f    = file_open_temp(&path, a);
        if (FileIsOpen(&f)) {
            u64 n = ZstrLen(body);
            if (n > 0) {
                FileWrite(&f, body, n);
        Str  body   = StrInit(a);
        Zstr pad    = "# padding line to push the fixture past the four kilobyte chunk\n";
        u64  padlen = ZstrLen(pad);
        for (int i = 0; i < 300; ++i) {
            StrPushBackMany(&body, pad, padlen);
            StrPushBackMany(&body, pad, padlen);
        }
        StrPushBackMany(&body, "10.20.30.40 farhost\n", ZstrLen("10.20.30.40 farhost\n"));
    
        DnsResolver r;
    
        Zstr payload = "hello from socket test";
        size n       = (size)ZstrLen(payload);
        if (SocketSend(&client, payload, n) != (i64)n) {
            SocketClose(&server);
        // client -> server
        Zstr c2s = "client=>server:ABCDEFG";
        size n1  = (size)ZstrLen(c2s);
        ok       = ok && SocketSend(&client, c2s, n1) == (i64)n1;
        char buf1[64];
        // length mutation surfaces here too)
        Zstr s2c = "server=>client:0123456789";
        size n2  = (size)ZstrLen(s2c);
        ok       = ok && SocketSend(&server, s2c, n2) == (i64)n2;
        char buf2[64];
        // Now send data; poll(with a short timeout) must report READ ready.
        Zstr payload = "poke";
        size n       = (size)ZstrLen(payload);
        ok           = ok && SocketSend(&client, payload, n) == (i64)n;
        // -- never touching the timeout/error path.
        Zstr payload = "recv-timeout-set";
        size n       = (size)ZstrLen(payload);
        ok           = ok && SocketSend(&client, payload, n) == (i64)n;
        char buf[32];
    
        Zstr payload = "after-send-timeout";
        size n       = (size)ZstrLen(payload);
        ok           = ok && SocketSend(&client, payload, n) == (i64)n;
        char buf[32];
        // Connection still usable after toggling options.
        Zstr payload = "opts-ok";
        size n       = (size)ZstrLen(payload);
        ok           = ok && SocketSend(&client, payload, n) == (i64)n;
        char buf[16];
        u8 strbuf[256];
        MemSet(strbuf, 0, sizeof(strbuf));
        u64 name_len = ZstrLen(fn_name);
        MemCopy(&strbuf[MC_NAME_STRP], fn_name, name_len);
        strbuf[MC_NAME_STRP + name_len] = '\0';
        u8 strbuf[256];
        MemSet(strbuf, 0, sizeof(strbuf));
        u64 name_len = ZstrLen(fn_name);
        MemCopy(&strbuf[BL_NAME_STRP], fn_name, name_len);
        strbuf[BL_NAME_STRP + name_len] = '\0';
        wr_u32(&cv[20], 0x0000002a); // Age
        // PDB path, NUL-terminated.
        u64 path_len = ZstrLen(kPdbPath);
        MemCopy(&cv[24], kPdbPath, path_len + 1);
    }
        MemCopy(&cv[4], kGuid, 16);
        wr_u32(&cv[20], CV_AGE);
        u64 path_len = ZstrLen(kPdbPath);
        MemCopy(&cv[24], kPdbPath, path_len + 1);
    }
        MemCopy(&cv[4], kGuid, 16);
        wr_u32(&cv[20], CV_AGE);
        u64 path_len = ZstrLen(kPdbPath);
        MemCopy(&cv[24], kPdbPath, path_len + 1);
    }
        MemCopy(&cv[4], kGuid, 16);
        wr_u32(&cv[20], CV_AGE);
        u64 plen = ZstrLen(kPdbPath);
        MemCopy(&cv[24], kPdbPath, plen + 1);
    }
        MemCopy(&cv[4], kGuid, 16);
        wr_u32(&cv[20], CV_AGE);
        u64 plen = ZstrLen(kPdbPath);
        MemCopy(&cv[24], kPdbPath, plen + 1);
    }
        MemCopy(&cv2[4], kGuid, 16);
        wr_u32(&cv2[20], CV_AGE);
        u64 plen = ZstrLen(kPdbPath);
        MemCopy(&cv2[24], kPdbPath, plen + 1);
        Pe pe;
        const PeSection *s  = VecPtrAt(&pe.sections, 0);
        bool             ok = VecLen(&pe.sections) == 1;
        ok                  = ok && ZstrLen(s->name) == 8;
        ok                  = ok && ZstrCompare(s->name, "ABCDEFGH") == 0;
        PeDeinit(&pe);
    // `*sym` points past the record on return.
    static void write_pub32(u8 **sym, u32 offset, u16 segment, Zstr name) {
        u32 namelen = (u32)ZstrLen(name) + 1; // include NUL
        u16 rec_len = (u16)(2 + 4 + 4 + 2 + namelen);
        u8 *p       = *sym;
    // `rec_kind` overridable so a non-PUB32 record can be injected.
    static void write_pub32_kind(u8 **sym, u16 rec_kind, u32 flags, u32 offset, u16 segment, Zstr name) {
        u32 namelen = (u32)ZstrLen(name) + 1; // include NUL
        u16 rec_len = (u16)(2 + 4 + 4 + 2 + namelen);
        u8 *p       = *sym;
        u8 *sp  = sym;
        for (u32 i = 0; i < n_pubs; ++i) {
            u32 namelen = (u32)ZstrLen(pubs[i].name) + 1; // include NUL
            u16 rec_len = (u16)(2 + 4 + 4 + 2 + namelen);
            wr_u16(&sp[0], rec_len);
    
    static u32 put_pub32(u8 *dst, u32 offset, u16 segment, Zstr name) {
        u32 namelen = (u32)ZstrLen(name) + 1;
        u16 rec_len = (u16)(2 + 4 + 4 + 2 + namelen);
        wr_u16(&dst[0], rec_len);
    // line is skipped, not a hard failure).
    static bool pm_load_text(ProcMaps *m, Zstr text, DefaultAllocator *alloc) {
        return ProcMapsLoadFrom(m, text, ZstrLen(text), alloc);
    }
        bool wrote_ok = true;
        for (int i = 0; i < 200 && wrote_ok; ++i) {
            i64 w    = FileWrite(&f, filler, (u64)ZstrLen(filler));
            wrote_ok = (w == (i64)ZstrLen(filler));
        }
        for (int i = 0; i < 200 && wrote_ok; ++i) {
            i64 w    = FileWrite(&f, filler, (u64)ZstrLen(filler));
            wrote_ok = (w == (i64)ZstrLen(filler));
        }
        Zstr late = "deadbeef000-deadbeef100 r-xp 0 0:0 0 /late\n";
        Zstr late = "deadbeef000-deadbeef100 r-xp 0 0:0 0 /late\n";
        if (wrote_ok) {
            i64 w    = FileWrite(&f, late, (u64)ZstrLen(late));
            wrote_ok = (w == (i64)ZstrLen(late));
        }
        if (wrote_ok) {
            i64 w    = FileWrite(&f, late, (u64)ZstrLen(late));
            wrote_ok = (w == (i64)ZstrLen(late));
        }
        FileClose(&f);
    
        Zstr payload = "hello-body";
        ok           = ok && (FileWrite(&f, payload, ZstrLen(payload)) == (i64)ZstrLen(payload));
        FileClose(&f);
    
        ok = ok && (res != NULL) && (response.status_code == HTTP_RESPONSE_CODE_OK) &&
             (response.content_type == HTTP_CONTENT_TYPE_TEXT_PLAIN) && (StrLen(&response.body) == ZstrLen(payload)) &&
             (ZstrCompare(StrBegin(&response.body), payload) == 0);
        bool ok      = FileIsOpen(&f);
        Zstr payload = "file-payload-long-enough-to-heap-allocate-body";
        ok           = ok && (FileWrite(&f, payload, ZstrLen(payload)) == (i64)ZstrLen(payload));
        FileClose(&f);
        //   + DIE: abbrev_code(1) name(NUL-terminated) low_pc(8) high_pc(8)
        //   + end-of-children(1 byte: abbrev code 0)
        u64 namelen = ZstrLen(name) + 1; // include NUL
        u8 *p       = buf + 4;           // leave room for unit_length
        u8 *start   = p;
        Zstr msg_want  = "Hello, \"World\"!";
        Zstr data_want = "line1\nline2\ttab";
        if (StrLen(&obj.path) != ZstrLen(path_want) || MemCompare(StrBegin(&obj.path), path_want, StrLen(&obj.path)) != 0) {
            WriteFmtLn("[DEBUG] Special characters FAILED: path content wrong");
            success = false;
            success = false;
        }
        if (StrLen(&obj.message) != ZstrLen(msg_want) ||
            MemCompare(StrBegin(&obj.message), msg_want, StrLen(&obj.message)) != 0) {
            WriteFmtLn("[DEBUG] Special characters FAILED: message content wrong");
            success = false;
        }
        if (StrLen(&obj.data) != ZstrLen(data_want) || MemCompare(StrBegin(&obj.data), data_want, StrLen(&obj.data)) != 0) {
            WriteFmtLn("[DEBUG] Special characters FAILED: data content wrong");
            success = false;
Last updated on