diff options
| author | Mel <mel@rnrd.eu> | 2025-07-10 00:17:42 +0200 |
|---|---|---|
| committer | Mel <mel@rnrd.eu> | 2025-07-10 00:17:42 +0200 |
| commit | 961ca5fb2c5aaf45b81c154874e503564afc2290 (patch) | |
| tree | d3d1dc7443ea2fe4ce39ec20b3caef71bc9529ed /boot/common.c | |
| parent | c55f2311d3c618187ad17804417cb7aec127cc6f (diff) | |
| download | catskill-961ca5fb2c5aaf45b81c154874e503564afc2290.tar.zst catskill-961ca5fb2c5aaf45b81c154874e503564afc2290.zip | |
Expand common string library with `String_View` and format macros
Signed-off-by: Mel <mel@rnrd.eu>
Diffstat (limited to 'boot/common.c')
| -rw-r--r-- | boot/common.c | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/boot/common.c b/boot/common.c index ccae087..7416696 100644 --- a/boot/common.c +++ b/boot/common.c @@ -135,6 +135,32 @@ struct String uint length; }; +// a view of a string. +// used for passing around strings without copying them, +// it is not assumed to have a permanent lifetime, +// nor is it guaranteed to be null-terminated. +struct String_View +{ + ascii* data; + uint length; +}; + +// formats and prints a string with a given format to stdout. +// to use the given string in the format, use the `%S` specifier. +// the string is required to be the first argument in the format string. +// accepts both `struct String` and `struct String_View`. +#define STRING_FORMAT(s, format, ...) \ + _internal_string_format(stdout, s.length, format, s.data, ##__VA_ARGS__) + +// formats and prints a string with a given format to `stream`. +// to use the given string in the format, use the `%S` specifier. +// the string is required to be the first argument in the format string. +// accepts both `struct String` and `struct String_View`. +#define STRING_FORMAT_TO(s, stream, format, ...) \ + _internal_string_format(stream, s.length, format, s.data, ##__VA_ARGS__) + +// iterates over each character in a string. +// accepts both `struct String` and `struct String_View`. #define STRING_ITERATE(index, c, str) \ ascii c = str.data[0]; \ for (uint index = 0; index < str.length; c = str.data[++index]) @@ -219,6 +245,134 @@ string_print(struct String s) printf("%.*s", (int32)s.length, s.data); } +void +string_format(struct String s, const ascii* format, ...) +{ + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); +} + +struct String_View +string_substring(struct String s, uint start, uint end) +{ + check(start <= end && end <= s.length, "substring out of bounds"); + return (struct String_View){ + .data = s.data + start, + .length = end - start, + }; +} + +// creates a new string view from an ascii buffer. +// the buffer is not copied, so it must have a lifetime +// spanning the at least the lifetime of the string view. +// null-termination is not required. +struct String_View +string_view_new(ascii* data, uint length) +{ + return (struct String_View){ + .data = data, + .length = length, + }; +} + +// creates a new string view from a string. +struct String_View +string_view_from_string(struct String s) +{ + return (struct String_View){ + .data = s.data, + .length = s.length, + }; +} + +// creates empty string view. +struct String_View +string_view_empty(void) +{ + return (struct String_View){ + .data = nil, + .length = 0, + }; +} + +// checks if a string view is empty. +bool +string_view_is_empty(struct String_View view) +{ + return view.data == nil || view.length == 0; +} + +uint +string_view_length(struct String_View view) +{ + return view.length; +} + +// returns the character at a given index in a string view. +ascii +string_view_at(struct String_View view, uint index) +{ + check(index < view.length, "index out of bounds"); + return view.data[index]; +} + +// prints a string view to stdout. +void +string_view_print(struct String_View view) +{ + printf("%.*s", (int32)view.length, view.data); +} + +#define MAX_FORMAT_STRING_SIZE 256 + +// creates a real c format string from our faux-format string. +// example: +// "Hello %S! You are %d years old." +// becomes: +// "Hello %.6s! You are %d years old." +// if we assume the string length is 6. +void +_internal_prepare_format_string(ascii* format_buffer, const ascii* format, uint static_length) +{ + ascii specifier[10]; + int specifier_length = snprintf(specifier, sizeof(specifier), "%%.%lus", static_length); + check(specifier_length < sizeof(specifier), "string to format is too long"); + + for (uint fi = 0, fbi = 0; fi < MAX_FORMAT_STRING_SIZE && fbi < MAX_FORMAT_STRING_SIZE; + ++fi, ++fbi) { + ascii c = format[fi]; + format_buffer[fbi] = c; + + if (c == '\0') break; + + // check if the next character is the format specifier 'S', that + // we specified as our own custom format specifier. + if (c == '%' && format[fi + 1] == 'S') { + // copy the specifier into the format buffer. + for (uint si = 0; si < specifier_length; ++si, ++fbi) + format_buffer[fbi] = specifier[si]; + fbi--, fi++; // increment `fi` to skip 'S', decrement `fbi` to not leave a gap. + } + } +} + +// formats a string using the given faux-format string, printing it to `stream`. +// the first VA arguments is expected to be the string data. +// do not use directly! use `STRING_FORMAT_TO`. +void +_internal_string_format(FILE* stream, uint string_length, const ascii* format, ...) +{ + ascii format_buffer[MAX_FORMAT_STRING_SIZE]; + _internal_prepare_format_string(format_buffer, format, string_length); + + va_list args; + va_start(args, format); + vfprintf(stream, format_buffer, args); + va_end(args); +} + #define STRING_ARRAY_MAX 8 // a string array, used for storing multiple strings. |
