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