Files
frostbyte/src/klib.c
T

363 lines
9.7 KiB
C

#include "klib.h"
#include "string.h"
/* ---- character classification -------------------------------------- */
bool isDigit(int c) { return c >= '0' && c <= '9'; }
bool isUpper(int c) { return c >= 'A' && c <= 'Z'; }
bool isLower(int c) { return c >= 'a' && c <= 'z'; }
bool isAlpha(int c) { return isUpper(c) || isLower(c); }
bool isAlnum(int c) { return isAlpha(c) || isDigit(c); }
bool isSpace(int c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f'; }
bool isHexDigit(int c) { return isDigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); }
int toUpper(int c) { return isLower(c) ? c - 'a' + 'A' : c; }
int toLower(int c) { return isUpper(c) ? c - 'A' + 'a' : c; }
/* ---- string manipulation --------------------------------------------- */
char* strcpy(char* dest, const char* src)
{
char* out = dest;
while ((*out++ = *src++) != 0)
;
return dest;
}
char* strncpy(char* dest, const char* src, usize count)
{
usize i = 0;
for (; i < count && src[i]; i++)
dest[i] = src[i];
for (; i < count; i++)
dest[i] = 0;
return dest;
}
char* strcat(char* dest, const char* src)
{
strcpy(dest + strlen(dest), src);
return dest;
}
char* strncat(char* dest, const char* src, usize count)
{
char* end = dest + strlen(dest);
usize i = 0;
for (; i < count && src[i]; i++)
end[i] = src[i];
end[i] = 0;
return dest;
}
char* strchr(const char* str, int c)
{
for (;; str++) {
if (*str == (char)c)
return (char*)str;
if (*str == 0)
return nullptr;
}
}
char* strrchr(const char* str, int c)
{
const char* last = nullptr;
for (;; str++) {
if (*str == (char)c)
last = str;
if (*str == 0)
return (char*)last;
}
}
char* strstr(const char* haystack, const char* needle)
{
if (!*needle)
return (char*)haystack;
usize needleLen = strlen(needle);
for (; *haystack; haystack++) {
if (memcmp(haystack, needle, needleLen) == 0)
return (char*)haystack;
}
return nullptr;
}
usize strspn(const char* str, const char* accept)
{
usize count = 0;
for (; str[count]; count++) {
if (!strchr(accept, str[count]))
break;
}
return count;
}
usize strcspn(const char* str, const char* reject)
{
usize count = 0;
for (; str[count]; count++) {
if (strchr(reject, str[count]))
break;
}
return count;
}
char* strTokR(char* str, const char* delim, char** savePtr)
{
char* cursor = str ? str : *savePtr;
if (cursor == nullptr)
return nullptr;
cursor += strspn(cursor, delim);
if (*cursor == 0) {
*savePtr = nullptr;
return nullptr;
}
char* tokenEnd = cursor + strcspn(cursor, delim);
if (*tokenEnd) {
*tokenEnd = 0;
*savePtr = tokenEnd + 1;
} else {
*savePtr = nullptr;
}
return cursor;
}
/* ---- number parsing ---------------------------------------------------- */
static int digitValue(int c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'z')
return c - 'a' + 10;
if (c >= 'A' && c <= 'Z')
return c - 'A' + 10;
return -1;
}
uint64 strtoul(const char* str, char** endPtr, int base)
{
const char* cursor = str;
while (isSpace((unsigned char)*cursor))
cursor++;
bool negative = false;
if (*cursor == '+' || *cursor == '-') {
negative = (*cursor == '-');
cursor++;
}
if ((base == 0 || base == 16) && cursor[0] == '0' && (cursor[1] == 'x' || cursor[1] == 'X')) {
cursor += 2;
base = 16;
} else if (base == 0 && cursor[0] == '0') {
base = 8;
} else if (base == 0) {
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;
}