feat: add strtol and atoi to klib

This commit is contained in:
dualfroz committed 2026-09-04 17:45:55 +02:00
1 parent 9732761402
commit 0cd28abb5d
1 file changed
+201
+201
View File
@@ -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;
}