aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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.