Skip to content

StrInitStack

Description

Open a scope that declares a Str named name, backed by a fixed-capacity stack array of ne characters. The body that follows the macro call (NOT a macro argument) sees name as an initialised, empty Str with capacity ne. name’s lexical scope is exactly the body: outside the macro the identifier is no longer bound.

No allocator – the backing storage is the stack. The body is responsible for keeping content bounded by ne. Any operation that would grow name past ne ends up calling StrReserve, which sees the NULL allocator and aborts via LOG_FATAL("vector not growable, no allocator assigned, probably stack inited"). Use a heap-backed Str if you need spill behaviour.

The macro uses the for-chain scope idiom – the body is regular code below the macro, not a brace-delimited argument:

StrInitStack(buf, 1024) { ssize_t n = read(fd, StrBegin(&buf), 1023); StrResize(&buf, (size)n); StrMergeR(out, &buf); }

Success

Body runs once; name is valid for the body’s scope. On normal fall-through both name and the backing array are zeroed by the macro’s exit updates before name falls out of scope.

Failure

The macro itself cannot fail. Operations inside the body that try to grow name past ne abort.

updates (the same C-level limitation that applies to Scope). break exits the innermost for cleanly: the backing array is still zeroed by the outer for’s update, but the name handle’s MemSet is skipped – harmless because name’s scope is the body anyway and the identifier is no longer reachable after the macro. continue inside the body does NOT restart – it jumps to the inner for’s update clause, which zeroes name and then re-checks the (already-false) condition, exiting the scope. Treat it as a silent early-exit, not a loop control. ne is evaluated twice (the +1 for the array dimension, and the capacity assignment); sizeof(UNPL(_d)) reads the array’s type, not ne. Pass a side-effect-free expression (literal or simple variable) regardless.

Usage example (Cross-references)

Usage examples (Cross-references)
            Str           UNPL(m)         = StrInit(&UNPL(log_alloc));                                                     \
            StrAppendFmt(&UNPL(m), __VA_ARGS__);                                                                           \
            StrInitStack(UNPL(syserr), 256) {                                                                              \
                StrError(UNPL(sys_eno), &UNPL(syserr));                                                                    \
                StrAppendFmt(&UNPL(m), " : {}", UNPL(syserr));                                                             \
            Str           UNPL(m)         = StrInit(&UNPL(log_alloc));                                                     \
            StrAppendFmt(&UNPL(m), __VA_ARGS__);                                                                           \
            StrInitStack(UNPL(syserr), 256) {                                                                              \
                StrError(UNPL(sys_eno), &UNPL(syserr));                                                                    \
                StrAppendFmt(&UNPL(m), " : {}", UNPL(syserr));                                                             \
            Str           UNPL(m)         = StrInit(&UNPL(log_alloc));                                                     \
            StrAppendFmt(&UNPL(m), __VA_ARGS__);                                                                           \
            StrInitStack(UNPL(syserr), 256) {                                                                              \
                StrError(UNPL(sys_eno), &UNPL(syserr));                                                                    \
                StrAppendFmt(&UNPL(m), " : {}", UNPL(syserr));                                                             \
        // `flag` aliases the caller's `tok` directly.
        ArgRun result = ARG_RUN_ERROR;
        StrInitStack(flagbuf, 128) {
            Zstr flag = tok;
            if (eq) {
        for (Zstr p = tok + 1; *p; ++p) {
            bool iter_ok = false;
            StrInitStack(buf, 3) {
                char *data = StrBegin(&buf);
                data[0]    = '-';
                            BUNDLE_REJECT
                        } decision = BUNDLE_REJECT;
                        StrInitStack(two, 3) {
                            char *data = StrBegin(&two);
                            data[0]    = '-';
                FmtInfo fmt_info = {0};
                bool    spec_ok  = false;
                StrInitStack(spec_buf, 32) {
                    char *data = StrBegin(&spec_buf);
                    MemCopy(data, start, spec_len);
    
        bool ok = true;
        StrInitStack(digits, 32) {
            char *data        = StrBegin(&digits);
            u32   digit_count = 0;
                    // Hex of one byte is <= 2 glyphs (+ a leading '0' pad), so
                    // an 8-byte stack scratch suffices; no allocator needed.
                    StrInitStack(hex, 8) {
                        // (u8) so a high byte (>= 0x80) renders as its two-digit
                        // value, not a sign-extended 16-digit u64 (cf. site below).
                    // Hex of one byte is <= 2 glyphs (+ a leading '0' pad), so
                    // an 8-byte stack scratch suffices; no allocator needed.
                    StrInitStack(hex, 8) {
                        if (!StrFromU64(&hex, (u8)xs[i], &config)) {
                            return false;
        // allocator needed -- and `o` itself may be a stack Str (e.g. the
        // LOG_SYS_* errno scratch), which has no allocator to inherit.
        StrInitStack(temp, 80) {
            if (!StrFromU64(&temp, *v, &config)) {
                return false;
        // 80 bytes covers an i64 in any base (binary 64 + "0b" + sign). No
        // allocator needed -- `o` may itself be a stack Str.
        StrInitStack(temp, 80) {
            if (!StrFromI64(&temp, *v, &config)) {
                return false;
            // digits + '.' + precision (u8, <= 255) + sign, so 1024 bytes
            // covers it with margin. No allocator -- `o` may be a stack Str.
            StrInitStack(temp, 1024) {
                if (!StrFromF64(&temp, *v, &config)) {
                    return false;
        } else {
            bool ok = true;
            StrInitStack(buffer, 65) {
                char *data = StrBegin(&buffer);
                size  pos  = 0;
            } else {
                bool ok = true;
                StrInitStack(exp_buf, 8) {
                    char *data    = StrBegin(&exp_buf);
                    size  exp_pos = 0;
            } else {
                bool ok = true;
                StrInitStack(int_buf, 32) {
                    char *data    = StrBegin(&int_buf);
                    size  int_pos = 0;
        // `-Werror=implicit-function-declaration` here.
        HANDLE hFind = INVALID_HANDLE_VALUE;
        StrInitStack(search_path, MAX_PATH) {
            size path_len = ZstrLen(path);
            if (path_len + 3 > MAX_PATH) {
        // intermediate components. Restores the slash before continuing.
        i8 ok = 1;
        StrInitStack(buf, 4096) {
            char *data = StrBegin(&buf);
            MemCopy(data, path, n);
            // `reserve_vec`.
            bool inner_ok = false;
            StrInitStack(child, DIR_REMOVE_ALL_PATH_CAP) {
                StrAppendFmt(&child, trail_sep ? "{}{}" : "{}/{}", path, entry_nm);
                if (e->type == DIR_ENTRY_TYPE_DIRECTORY) {
        }
        bool ok = true;
        StrInitStack(chunk, 4096) {
            char *data = StrBegin(&chunk);
            for (;;) {
                // `parse_ipv4` / `parse_ipv6` can scan it. Cap matches
                // the longest reasonable IPv6-with-zone-id literal.
                StrInitStack(ip_buf, 64) {
                    StrPushBackMany(&ip_buf, (Zstr)StrIterDataAt(&si, ip_start), ip_len);
                    got_v4 = parse_ipv4(StrBegin(&ip_buf), v4);
                u64 ip_len = (u64)(StrIterIndex(&si) - ip_start);
                if (ip_len > 0 && ip_len < 64) {
                    StrInitStack(ip_buf, 64) {
                        StrPushBackMany(&ip_buf, (Zstr)StrIterDataAt(&si, ip_start), ip_len);
                        u8 v4[4]  = {0};
    
        bool found = false;
        StrInitStack(norm, 256) {
            // `normalize_hostname` strips trailing dots and lowercases;
            // `StrInitStack` zero-fills the trailing slot so `StrBegin`
        // NUL lives next to the bytes, with no parallel `char host[]`.
        bool ok = false;
        StrInitStack(host, 256) {
            StrPushBackMany(&host, spec, colon_at);
            ok = dns_resolve_5_zstr(self, StrBegin(&host), port, kind, out);
    #    if FEATURE_PARSER_PDB
            if (pdb_cache_ok) {
                StrInitStack(module_path, MAX_PATH) {
                    char *data        = StrBegin(&module_path);
                    u64   module_base = 0;
            u32  v       = octets[i];
            bool iter_ok = true;
            StrInitStack(tmp, 4) {
                char *data = StrBegin(&tmp);
                i32   n    = 0;
    static bool append_hextet(char *dst, size dst_size, size *pos, u16 v) {
        bool ok = true;
        StrInitStack(tmp, 4) {
            char *data = StrBegin(&tmp);
            i32   n    = 0;
    
        bool ok = false;
        StrInitStack(host, 256) {
            char *host_data = StrBegin(&host);
            Zstr  port_str  = NULL;
        }
    
        StrInitStack(host, 48) {
            char *host_data = StrBegin(&host);
            u16   port      = 0;
        // then `StrMergeR` copies into the caller's `buf`. The
        // `StrInitStack` scope owns `tmpbuf`'s lifetime end-to-end.
        StrInitStack(tmpbuf, 1024) {
    #if PLATFORM_UNIX
            i32 rfd = is_stdout ? proc->_stdout_fd : proc->_stderr_fd;
        // directly for `StrResize` before copying out.
        bool got = false;
        StrInitStack(buffer, MAX_PATH) {
            DWORD len = GetModuleFileNameA(NULL, StrBegin(&buffer), MAX_PATH);
            if (len == 0 || len >= MAX_PATH) {
        // we accept `>= 0` as success rather than the legacy `!= -1` shape.
        bool got = false;
        StrInitStack(buffer, 4096) {
            char   *data = StrBegin(&buffer);
            ssize_t len  = proc_readlink("/proc/self/exe", data, 4095);
    // Test StrInitStack macro
    bool test_str_init_stack(void) {
        WriteFmt("Testing StrInitStack\n");
    
        DefaultAllocator alloc = DefaultAllocatorInit();
    
        // StrInitStack declares and scopes `stack_str` itself (for-chain idiom).
        StrInitStack(stack_str, 20) {
            StrPushBackMany(&stack_str, "Hello, Stack!");
            ValidateStr(&stack_str);
    static bool test_nan_guard_no_overread(void) {
        bool ok = true;
        StrInitStack(view, 8) {
            char *d     = StrBegin(&view);
            d[0]        = ' ';
    static bool test_neg_inf_guard_no_overread(void) {
        bool ok = true;
        StrInitStack(view, 8) {
            char *d     = StrBegin(&view);
            d[0]        = '-';
            PdbCache  cache = PdbCacheInit(base);
            const u64 mbase = 0x140000000ull;
            StrInitStack(mod, 1100) {
                StrPushBackMany(&mod, (Zstr)pe_path);
                Zstr name = NULL;
Last updated on