From 0cd28abb5de13c8c8c8bad1c7991c6e4c14e39cb Mon Sep 17 00:00:00 2001 From: DualFroz Date: Fri, 4 Sep 2026 17:45:55 +0200 Subject: [PATCH] feat: add strtol and atoi to klib --- src/klib.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) diff --git a/src/klib.c b/src/klib.c index e91b81d..f840c1d 100644 --- a/src/klib.c +++ b/src/klib.c @@ -159,3 +159,204 @@ uint64 strtoul(const char* str, char** endPtr, int base) base = 10; } + uint64 value = 0; + const char* firstDigit = cursor; + for (;; cursor++) { + int digit = digitValue((unsigned char)*cursor); + if (digit < 0 || digit >= base) + break; + value = value * (uint64)base + (uint64)digit; + } + + if (endPtr) + *endPtr = (char*)(cursor == firstDigit ? str : cursor); + return negative ? (uint64)(-(int64)value) : value; +} + +int64 strtol(const char* str, char** endPtr, int base) +{ + /* strtoul already skips whitespace, consumes an optional sign and + applies it (via unsigned two's-complement negation) before we ever + see the result, so reinterpreting its bits as signed is correct + on its own -- negating again here would flip the sign twice. */ + return (int64)strtoul(str, endPtr, base); +} + +int32 atoi(const char* str) { return (int32)strtol(str, nullptr, 10); } +int64 atol(const char* str) { return strtol(str, nullptr, 10); } + +/* ---- formatted output --------------------------------------------------- */ + +typedef struct SnprintfSink { + char* buf; + usize size; /* capacity including the room for a nul terminator */ + usize written; /* total chars that would be written, unbounded */ +} SnprintfSink; + +static void sinkPutChar(SnprintfSink* sink, char c) +{ + if (sink->written + 1 < sink->size) + sink->buf[sink->written] = c; + sink->written++; +} + +static void sinkPutString(SnprintfSink* sink, const char* str) +{ + while (*str) + sinkPutChar(sink, *str++); +} + +static void sinkPutUnsigned(SnprintfSink* sink, uint64 value, uint32 base, bool upper, int minWidth, char pad) +{ + static const char* lower = "0123456789abcdef"; + static const char* higher = "0123456789ABCDEF"; + const char* digits = upper ? higher : lower; + char buffer[32]; + int length = 0; + + if (value == 0) + buffer[length++] = '0'; + while (value > 0) { + buffer[length++] = digits[value % base]; + value /= base; + } + while (length < minWidth && length < (int)sizeof(buffer)) + buffer[length++] = pad; + + while (length > 0) + sinkPutChar(sink, buffer[--length]); +} + +static void sinkPutSigned(SnprintfSink* sink, int64 value, int minWidth, char pad) +{ + if (value < 0) { + sinkPutChar(sink, '-'); + sinkPutUnsigned(sink, (uint64)(-value), 10, false, minWidth > 0 ? minWidth - 1 : 0, pad); + } else { + sinkPutUnsigned(sink, (uint64)value, 10, false, minWidth, pad); + } +} + +int vsnprintf(char* buf, usize size, const char* format, va_list args) +{ + SnprintfSink sink = { buf, size, 0 }; + + for (usize i = 0; format[i]; i++) { + if (format[i] != '%') { + sinkPutChar(&sink, format[i]); + continue; + } + + i++; + int width = 0; + char pad = ' '; + if (format[i] == '0') { + pad = '0'; + i++; + } + while (format[i] >= '0' && format[i] <= '9') { + width = width * 10 + (format[i] - '0'); + i++; + } + + int longCount = 0; + while (format[i] == 'l') { + longCount++; + i++; + } + + switch (format[i]) { + case 'c': + sinkPutChar(&sink, (char)va_arg(args, int)); + break; + case 's': + sinkPutString(&sink, va_arg(args, const char*)); + break; + case 'd': + case 'i': + sinkPutSigned(&sink, longCount ? va_arg(args, int64) : (int64)va_arg(args, int), width, pad); + break; + case 'u': + sinkPutUnsigned(&sink, longCount ? va_arg(args, uint64) : (uint64)va_arg(args, uint32), 10, false, width, pad); + break; + case 'x': + sinkPutUnsigned(&sink, longCount ? va_arg(args, uint64) : (uint64)va_arg(args, uint32), 16, false, width, pad); + break; + case 'X': + sinkPutUnsigned(&sink, longCount ? va_arg(args, uint64) : (uint64)va_arg(args, uint32), 16, true, width, pad); + break; + case 'p': + sinkPutString(&sink, "0x"); + sinkPutUnsigned(&sink, (uint64)va_arg(args, void*), 16, false, 16, '0'); + break; + case '%': + sinkPutChar(&sink, '%'); + break; + default: + sinkPutChar(&sink, '%'); + if (format[i]) + sinkPutChar(&sink, format[i]); + break; + } + } + + if (size > 0) + buf[sink.written < size ? sink.written : size - 1] = 0; + return (int)sink.written; +} + +int snprintf(char* buf, usize size, const char* format, ...) +{ + va_list args; + va_start(args, format); + int result = vsnprintf(buf, size, format, args); + va_end(args); + return result; +} + +/* ---- generic sorting / searching ---------------------------------------- */ + +static void rawSwap(uint8* a, uint8* b, usize size) +{ + for (usize i = 0; i < size; i++) { + uint8 tmp = a[i]; + a[i] = b[i]; + b[i] = tmp; + } +} + +/* Insertion sort: O(n^2) but allocation-free and simple to prove correct; + fine for the small, bounded arrays a freestanding kernel actually sorts + (dirents in a directory, a handful of scheduler entries, ...). */ +void qsort(void* base, usize count, usize size, int (*cmp)(const void*, const void*)) +{ + uint8* items = (uint8*)base; + for (usize i = 1; i < count; i++) { + usize j = i; + while (j > 0 && cmp(items + j * size, items + (j - 1) * size) < 0) { + rawSwap(items + j * size, items + (j - 1) * size, size); + j--; + } + } +} + +void* bsearch(const void* key, const void* base, usize count, usize size, + int (*cmp)(const void*, const void*)) +{ + const uint8* items = (const uint8*)base; + usize low = 0; + usize high = count; + + while (low < high) { + usize mid = low + (high - low) / 2; + const uint8* candidate = items + mid * size; + int result = cmp(key, candidate); + if (result == 0) + return (void*)candidate; + if (result < 0) + high = mid; + else + low = mid + 1; + } + return nullptr; +}