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;
|
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