about summary refs log tree commit diff
path: root/boot
diff options
context:
space:
mode:
Diffstat (limited to 'boot')
-rw-r--r--boot/common.c154
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.