feat: add strtol and atoi to klib
This commit is contained in:
1 parent
9732761402
commit
0cd28abb5d
1 file changed
+201
+201
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user