import "./core.zc" import "./mem.zc" import "./string.zc" include include /// Formats into a thread-local buffer (ring of buffers for nesting). fn format(fmt: char*, ...) -> char* { // A small ring of thread-local buffers so nested format() calls (e.g. // format("%s", format("%d", x))) do not clobber each other. Each returned // pointer stays valid until 4 further format() calls on the same thread. @thread_local static let buffer0: char[4096]; @thread_local static let buffer1: char[4096]; @thread_local static let buffer2: char[4096]; @thread_local static let buffer3: char[4096]; @thread_local static let slot: int = 0; let idx = slot; slot = (slot + 1) % 4; let buf: char* = NULL; if (idx == 0) { buf = (char*)buffer0; } else if (idx == 1) { buf = (char*)buffer1; } else if (idx == 2) { buf = (char*)buffer2; } else { buf = (char*)buffer3; } let ap: va_list; va_start(ap, fmt); vsnprintf(buf, 4096, fmt, ap); va_end(ap); return buf; } /// Formats into a caller-provided buffer. fn format_into(buffer: char*, size: usize, fmt: char*, ...) -> c_int { let ap: va_list; va_start(ap, fmt); let ret = vsnprintf(buffer, size, fmt, ap); va_end(ap); return ret; } /// Formats into a new heap-allocated buffer (caller frees). fn format_new(fmt: char*, ...) -> char* { let buffer: char* = alloc_n(1024); if buffer == NULL return NULL; let ap: va_list; va_start(ap, fmt); vsnprintf(buffer, 1024, fmt, ap); va_end(ap); return buffer; } /// Prints formatted output to stdout. fn print(fmt: char*, ...) -> c_int { let ap: va_list; va_start(ap, fmt); let ret = vprintf(fmt, ap); va_end(ap); return ret; } /// Prints formatted output to stdout followed by a newline. fn println(fmt: char*, ...) -> c_int { let ap: va_list; va_start(ap, fmt); let ret = vprintf(fmt, ap); va_end(ap); puts(""); return ret + 1; } /// Reads a line from stdin (caller frees). fn readln() -> char* { let cap: usize = 64; let len: usize = 0; let line: char* = alloc_n(cap); if (line == NULL) { return NULL; } let c: c_int; let std_in = stdin; while (true) { c = fgetc((FILE*)std_in); // Explicit cast to avoid -1 becoming UINT64_MAX in some contexts if treated as unsigned let eof_c: c_int = -1; if (c == eof_c) { break; } let nl_c: c_int = 10; if (c == nl_c) { break; // '\n' } if (len + 1 >= cap) { cap = cap * 2; let n = realloc_n(line, cap / 2, cap); if (n == NULL) { dealloc(line); return NULL; } line = n; } line[len] = c; len = len + 1; } let eof_final: int = -1; if (len == 0 && c == eof_final) { dealloc(line); return NULL; } line[len] = 0; return line; } fn _utf8_seq_len(first_byte: byte) -> usize { let b = (int)first_byte; if ((b & 0x80) == 0) { return 1; } // 0xxxxxxx if ((b & 0xE0) == 0xC0) { return 2; } // 110xxxxx if ((b & 0xF0) == 0xE0) { return 3; } // 1110xxxx if ((b & 0xF8) == 0xF0) { return 4; } // 11110xxx return 1; // Fallback } /// Reads a single rune from stdin. fn read_rune() -> rune { let std_in = stdin; let c1 = fgetc((FILE*)std_in); if (c1 == -1) { return (rune)0; } let first = (byte)c1; let len = _utf8_seq_len(first); if (len == 1) { return (rune)first; } let val: uint32_t = 0; if (len == 2) { val = (uint32_t)(first & 0x1F) << 6; val = val | (uint32_t)((byte)fgetc((FILE*)std_in) & 0x3F); } else if (len == 3) { val = (uint32_t)(first & 0x0F) << 12; val = val | ((uint32_t)((byte)fgetc((FILE*)std_in) & 0x3F) << 6); val = val | (uint32_t)((byte)fgetc((FILE*)std_in) & 0x3F); } else if (len == 4) { val = (uint32_t)(first & 0x07) << 18; val = val | ((uint32_t)((byte)fgetc((FILE*)std_in) & 0x3F) << 12); val = val | ((uint32_t)((byte)fgetc((FILE*)std_in) & 0x3F) << 6); val = val | (uint32_t)((byte)fgetc((FILE*)std_in) & 0x3F); } return (rune)val; } // Integer to string conversion /// Converts an int to a string in a static buffer. fn itos(n: int) -> char* { @thread_local static let buffer: char[32]; sprintf((char*)buffer, "%d", n); return (char*)buffer; } // Heap-allocated integer to string - caller must free /// Converts an int to a heap-allocated string (caller frees). fn itos_new(n: int) -> char* { let buffer: char* = alloc_n(32); if (buffer == NULL) { return NULL; } sprintf(buffer, "%d", n); return buffer; } // Unsigned integer to string /// Converts an unsigned int to a string in a static buffer. fn utos(n: u32) -> char* { @thread_local static let buffer: char[32]; sprintf((char*)buffer, "%u", n); return (char*)buffer; }