kiba-engine
format.c
1 #include <kiba/format/format.h>
2 
4 #include <kiba/containers/hash_table.h>
5 
6 #include <kiba/format/defaults/boolean.h>
7 #include <kiba/format/defaults/floating.h>
8 #include <kiba/format/defaults/integer.h>
9 #include <kiba/format/defaults/pointer.h>
10 #include <kiba/format/defaults/string.h>
11 
12 hash_table format_read_functions;
13 hash_table format_write_functions;
14 allocator format_allocator;
15 b8 format_system_initialized = false;
16 
17 b8 format_initialize(void) {
18  return allocator_create(&format_allocator, ALLOCATOR_LINEAR, 8192)
19  && hash_table_create(&format_read_functions, 64, 0.8, &format_allocator)
20  && hash_table_create(&format_write_functions, 64, 0.8, &format_allocator)
21  && format_register_read_function("u64", &format_read_u64)
22  && format_register_write_function("u64", &format_write_u64)
23  && format_register_read_function("i64", &format_read_i64)
24  && format_register_write_function("i64", &format_write_i64)
25  && format_register_read_function("u32", &format_read_u32)
26  && format_register_write_function("u32", &format_write_u32)
27  && format_register_read_function("i32", &format_read_i32)
28  && format_register_write_function("i32", &format_write_i32)
29  && format_register_read_function("u16", &format_read_u16)
30  && format_register_write_function("u16", &format_write_u16)
31  && format_register_read_function("i16", &format_read_i16)
32  && format_register_write_function("i16", &format_write_i16)
33  && format_register_read_function("u8", &format_read_u8)
34  && format_register_write_function("u8", &format_write_u8)
35  && format_register_read_function("i8", &format_read_i8)
36  && format_register_write_function("i8", &format_write_i8)
37  && format_register_read_function("usize", &format_read_usize)
38  && format_register_write_function("usize", &format_write_usize)
39  && format_register_read_function("uptr", &format_read_uptr)
40  && format_register_write_function("uptr", &format_write_uptr)
41  && format_register_read_function("pointer", &format_read_pointer)
42  && format_register_write_function("pointer", &format_write_pointer)
43  && format_register_read_function("b8", &format_read_b8)
44  && format_register_write_function("b8", &format_write_b8)
45  && format_register_read_function("b32", &format_read_b32)
46  && format_register_write_function("b32", &format_write_b32)
47  && format_register_read_function("f32", &format_read_f32)
48  && format_register_write_function("f32", &format_write_f32)
49  && format_register_read_function("f64", &format_read_f64)
50  && format_register_write_function("f64", &format_write_f64)
51  && format_register_write_function("string_view", &format_write_string_view)
52  && format_register_write_function("string", &format_write_string)
53  && format_register_write_function("raw_string", &format_write_raw_string)
54  && format_register_write_function("char", &format_write_char) && (format_system_initialized = true);
55 }
56 
57 b8 format_register_read_function(const char *placeholder, format_function func) {
58  return hash_table_set(&format_read_functions,
59  placeholder,
60  string_from_raw(placeholder).length * sizeof(char),
61  (uptr) func);
62 }
63 
64 b8 format_register_write_function(const char *placeholder, format_function func) {
65  return hash_table_set(&format_write_functions,
66  placeholder,
67  string_from_raw(placeholder).length * sizeof(char),
68  (uptr) func);
69 }
70 
71 void format_shutdown(void) {
72  format_system_initialized = false;
73  hash_table_destroy(&format_read_functions);
74  hash_table_destroy(&format_write_functions);
75  allocator_destroy(&format_allocator);
76 }
77 
78 static b8 format_process_options(string_view view, format_options *options) {
79  KB_ASSERT(view.is_valid, "must be provided with valid options string view");
80  format_buffer buf;
81  KB_ASSERT(format_buffer_create_static(&buf, FORMAT_BUFFER_READ, (char *) string_view_data(view), view.length),
82  "static format buffer must be creatable from options string view");
83  u64 temp;
84  char c;
85  b8 pre_dot = true;
86  while (format_buffer_peak_char(&buf, &c)) {
87  if (c == '.') {
88  if (!pre_dot) {
89  KB_WARN("invalid options string (multiple dots): {string_view}", view);
90  return false;
91  }
92  pre_dot = false;
93  format_buffer_advance(&buf);
94  }
95  if (pre_dot && format_read_unsigned_decimal(&buf, format_default_format_options(), &temp)) {
96  options->width = (u32) temp;
97  if (format_buffer_peak_char(&buf, &c)) {
98  switch (c) {
99  case 'r':
100  options->alignment = FORMAT_ALIGN_RIGHT;
101  format_buffer_advance(&buf);
102  break;
103  case 'l':
104  options->alignment = FORMAT_ALIGN_LEFT;
105  format_buffer_advance(&buf);
106  break;
107  case 'c':
108  options->alignment = FORMAT_ALIGN_CENTER;
109  format_buffer_advance(&buf);
110  break;
111  case '.':
112  break;
113  default:
114  KB_WARN("invalid options string (unrecognized alignment {char}): {string_view}", c, view);
115  return false;
116  }
117  }
118  continue;
119  }
120  if (!pre_dot && format_read_unsigned_decimal(&buf, format_default_format_options(), &temp)) {
121  options->precision = (u32) temp;
122  continue;
123  }
124  KB_WARN("invalid options string (invalid format): {string_view}", view);
125  return false;
126  }
127  return true;
128 }
129 
130 static b8 format_process_placeholder(format_buffer *buf, string_view view, VA_LIST *args) {
131  format_options options = format_default_format_options();
132  string_view options_view = string_view_until_next_char(view, ':');
133  if (options_view.is_valid) {
134  if (!format_process_options(options_view, &options)) {
135  return false;
136  }
137  view = string_view_at_offset(view, options_view.length + 1);
138  }
139  hash_table *table;
140  switch (buf->mode) {
141  case FORMAT_BUFFER_READ:
142  table = &format_read_functions;
143  break;
144  case FORMAT_BUFFER_WRITE:
145  table = &format_write_functions;
146  break;
147  default:
148  KB_ERROR("invalid mode found for format_buffer: {i32}", buf->mode);
149  return false;
150  }
151  uptr raw;
152  if (hash_table_get(table, string_view_data(view), view.length * sizeof(char), &raw)) {
153  format_function f = (format_function) raw;
154  return f != KB_NULL && f(buf, options, args);
155  }
156  KB_WARN("unrecognized formatting placeholder {string_view}", view);
157  return false;
158 }
159 
160 b8 format(format_buffer *buf, const char *fmt, ...) {
161  VA_LIST args;
162  VA_START(args, fmt);
163  b8 ok = format_va_list(buf, fmt, &args);
164  VA_END(args);
165  return ok;
166 }
167 
168 b8 format_va_list(format_buffer *buf, const char *fmt, VA_LIST *args) {
169  if (!format_system_initialized) {
170  return false;
171  }
172  b8 (*string_view_funcs[2])(format_buffer *, string_view) = {&format_buffer_match_string_view,
173  &format_buffer_add_string_view};
174  b8 (*char_funcs[2])(format_buffer *, char) = {&format_buffer_match_char, &format_buffer_add_char};
175 
176  b8 ok = true;
178  while (fmt_view.is_valid && ok) {
179  string_view from_placeholder_view = string_view_after_next_char(fmt_view, '{');
180  if (!from_placeholder_view.is_valid) {
181  ok &= string_view_funcs[buf->mode](buf, fmt_view);
182  fmt_view = from_placeholder_view;
183  } else if (from_placeholder_view.offset > 1 && fmt[from_placeholder_view.offset - 2] == '\\') {
184  ok &= string_view_funcs[buf->mode](
185  buf,
186  string_view_subview(fmt_view, 0, from_placeholder_view.offset - fmt_view.offset - 2))
187  && char_funcs[buf->mode](buf, '{');
188  fmt_view = from_placeholder_view;
189  } else {
190  ok &= string_view_funcs[buf->mode](
191  buf,
192  string_view_subview(fmt_view, 0, from_placeholder_view.offset - fmt_view.offset - 1));
193  string_view placeholder_view = string_view_until_next_char(from_placeholder_view, '}');
194  ok &= placeholder_view.is_valid;
195  if (ok) {
196  ok &= format_process_placeholder(buf, placeholder_view, args);
197  fmt_view = string_view_at_offset(from_placeholder_view, placeholder_view.length + 1);
198  }
199  }
200  }
201  return ok;
202 }
b8 allocator_create(allocator *alloc, allocator_type type, usize size)
Create an allocator of a specific type.
Definition: allocator.c:66
void allocator_destroy(allocator *alloc)
Destroy an allocator.
Definition: allocator.c:84
Central header providing allocator functionality.
string_view string_view_after_next_char(const string_view view, const char c)
Create a new string_view of an existing string_view after a certain character.
Definition: string.c:201
string_view string_view_at_offset(const string_view src, usize offset)
Create a new string_view of an existing string_view.
Definition: string.c:165
const char * string_view_data(const string_view view)
Get raw pointer to beginning of the string_view data.
Definition: string.c:135
string_view string_view_subview(const string_view view, usize start, usize length)
Create a new string_view of an existing string_view.
Definition: string.c:174
string_view string_view_from_string(const string str)
Create a new string view of a whole string.
Definition: string.c:126
string_view string_view_until_next_char(const string_view view, const char c)
Create a new string_view of an existing string_view until a certain character.
Definition: string.c:211
string string_from_raw(const char *raw)
Construct a string from a raw string.
Definition: string.c:13
struct string_view string_view
Non owning views on actual strings.
#define KB_NULL
Value of an invalid ptr (nullptr).
Definition: defines.h:18
#define KB_ASSERT(expr,...)
Perform runtime assertion and log failures.
Definition: log.h:133
#define KB_WARN(...)
Log entry with warn log level.
Definition: log.h:161
#define KB_ERROR(...)
Log entry with error log level.
Definition: log.h:142
Central allocator structure.
Definition: allocator.h:87
Non owning views on actual strings.
Definition: string.h:31
b8 is_valid
Indicates if the view is valid and can be used/accessed.
Definition: string.h:39
usize length
The length of the view.
Definition: string.h:35
usize offset
The offset into the string.
Definition: string.h:37