libclod
C library for interacting with NBTs, region files, LOD data and other things.
Loading...
Searching...
No Matches
format.c
1#include <clod/string.h>
2#include "debug.h"
3#include <stdarg.h>
4
5extern bool digit_valid(char digit, int base);
6
7enum format_type {
8 type_invalid = 0,
9 type_signed_int, type_unsigned_int, // i/u
10 type_signed_long, type_unsigned_long, // l/ul
11 type_signed_long_long, type_unsigned_long_long, // ll/ull
12 type_signed_int32, type_unsigned_int32, // i32/u32
13 type_signed_int64, type_unsigned_int64, // i64/u64
14 type_signed_size, type_unsigned_size, // ptrdiff/size
15 type_ptr, // ptr
16 type_double, // d
17 type_clod_string, // str
18 type_string, // s
19};
20
22 enum format_type type : 5;
23 unsigned int max_precision: 12;
24 unsigned int pad_width: 6;
25 unsigned int base: 6;
26 unsigned int zero_pad: 1;
27 unsigned int capitalise: 1;
28 unsigned int have_precision: 1;
29};
30
31struct format_specifier parse_format(struct clod_string *fmt) {
32 struct clod_string parse = *fmt;
33
34 struct format_specifier specifier = {0};
35
36 // Flags
37 while (1) {
38 switch (clod_string_peek_char(parse)) {
39 case '0': specifier.zero_pad = true; break;
40 case 'X': specifier.base = 16; specifier.capitalise = true; break;
41 case 'x': specifier.base = 16; break;
42 case 'o': specifier.base = 8; break;
43 case 'b': specifier.base = 2; break;
44 default: goto read_width;
45 }
46
48 }
49
50read_width:
51 // Pad width
52 specifier.pad_width = clod_string_get_uint(&parse, CLOD_STRING_DIGIT_ALPHABET, 10) & 63;
53
54 // Max precision
55 if (clod_string_peek_char(parse) == '.') {
56 specifier.max_precision = clod_string_get_uint(&parse, CLOD_STRING_DIGIT_ALPHABET, 10) & 4095;
57 specifier.have_precision = 1;
58 } else {
59 specifier.max_precision = 0;
60 specifier.have_precision = false;
61 }
62
63 // length and type
64 // checks ordered from long to short type names
65 if (clod_string_remove_prefix(&parse, CLOD_STRING_C("ptrdiff"))) {
66 specifier.type = type_signed_size;
67 } else if (clod_string_remove_prefix(&parse, CLOD_STRING_C("size"))) {
68 specifier.type = type_unsigned_size;
69 } else if (clod_string_remove_prefix(&parse, CLOD_STRING_C("i32"))) {
70 specifier.type = type_signed_int32;
71 } else if (clod_string_remove_prefix(&parse, CLOD_STRING_C("u32"))) {
72 specifier.type = type_unsigned_int32;
73 } else if (clod_string_remove_prefix(&parse, CLOD_STRING_C("i64"))) {
74 specifier.type = type_signed_int64;
75 } else if (clod_string_remove_prefix(&parse, CLOD_STRING_C("u64"))) {
76 specifier.type = type_unsigned_int64;
77 } else if (clod_string_remove_prefix(&parse, CLOD_STRING_C("ptr"))) {
78 specifier.type = type_ptr;
79 } else if (clod_string_remove_prefix(&parse, CLOD_STRING_C("str"))) {
80 specifier.type = type_clod_string;
81 } else if (clod_string_remove_prefix(&parse, CLOD_STRING_C("ll"))) {
82 specifier.type = type_signed_long_long;
83 } else if (clod_string_remove_prefix(&parse, CLOD_STRING_C("ull"))) {
84 specifier.type = type_unsigned_long_long;
85 } else if (clod_string_remove_prefix(&parse, CLOD_STRING_C("l"))) {
86 specifier.type = type_signed_long;
87 } else if (clod_string_remove_prefix(&parse, CLOD_STRING_C("ul"))) {
88 specifier.type = type_unsigned_long;
89 } else if (clod_string_remove_prefix(&parse, CLOD_STRING_C("i"))) {
90 specifier.type = type_signed_int;
91 } else if (clod_string_remove_prefix(&parse, CLOD_STRING_C("u"))) {
92 specifier.type = type_unsigned_int;
93 } else if (clod_string_remove_prefix(&parse, CLOD_STRING_C("d"))) {
94 specifier.type = type_double;
95 } else if (clod_string_remove_prefix(&parse, CLOD_STRING_C("s"))) {
96 specifier.type = type_string;
97 }
98
99 *fmt = parse;
100 return specifier;
101}
102
103size_t clod_string_format(struct clod_string *dst, struct clod_string fmt, ...) {
104 va_list args;
105 va_start(args, fmt);
106 size_t size = clod_string_vformat(dst, fmt, args);
107 va_end(args);
108 return size;
109}
110
111size_t clod_string_vformat(struct clod_string *dst, struct clod_string fmt, va_list args) {
112 size_t size = 0;
113
114 while (clod_string_peek_char(fmt)) {
115 const char c = clod_string_get_char(&fmt);
116 if (c != '%') {
117 clod_string_put_char(dst, c);
118 size++;
119 continue;
120 }
121
122 if (clod_string_peek_char(fmt) == '%') {
124 size++;
125 continue;
126 }
127
128 struct format_specifier specifier = parse_format(&fmt);
129 if (specifier.base == 0) {
130 if (specifier.type == type_ptr) {
131 specifier.base = 16;
132 } else {
133 specifier.base = 10;
134 }
135 }
136
137 const struct clod_string digit_alphabet = specifier.capitalise ? CLOD_STRING_DIGIT_ALPHABET_CAPS : CLOD_STRING_DIGIT_ALPHABET;
138
139 switch (specifier.type) {
140 case type_signed_int:
141 size += clod_string_put_int(dst, va_arg(args, signed int),
142 digit_alphabet, specifier.base, specifier.pad_width, (unsigned char)specifier.max_precision);
143 break;
144 case type_unsigned_int:
145 size += clod_string_put_uint(dst, va_arg(args, unsigned int),
146 digit_alphabet, specifier.base, specifier.pad_width, (unsigned char)specifier.max_precision);
147 break;
148 case type_signed_long:
149 size += clod_string_put_int(dst, va_arg(args, signed long),
150 digit_alphabet, specifier.base, specifier.pad_width, (unsigned char)specifier.max_precision);
151 break;
152 case type_unsigned_long:
153 size += clod_string_put_uint(dst, va_arg(args, unsigned long),
154 digit_alphabet, specifier.base, specifier.pad_width, (unsigned char)specifier.max_precision);
155 break;
156 case type_signed_long_long:
157 size += clod_string_put_int(dst, va_arg(args, signed long long),
158 digit_alphabet, specifier.base, specifier.pad_width, (unsigned char)specifier.max_precision);
159 break;
160 case type_unsigned_long_long:
161 size += clod_string_put_uint(dst, va_arg(args, unsigned long long),
162 digit_alphabet, specifier.base, specifier.pad_width, (unsigned char)specifier.max_precision);
163 break;
164 case type_signed_int32:
165 size += clod_string_put_int(dst, va_arg(args, int32_t),
166 digit_alphabet, specifier.base, specifier.pad_width, (unsigned char)specifier.max_precision);
167 break;
168 case type_unsigned_int32:
169 size += clod_string_put_uint(dst, va_arg(args, uint32_t),
170 digit_alphabet, specifier.base, specifier.pad_width, (unsigned char)specifier.max_precision);
171 break;
172 case type_signed_int64:
173 size += clod_string_put_int(dst, va_arg(args, int64_t),
174 digit_alphabet, specifier.base, specifier.pad_width, (unsigned char)specifier.max_precision);
175 break;
176 case type_unsigned_int64:
177 size += clod_string_put_uint(dst, va_arg(args, uint64_t),
178 digit_alphabet, specifier.base, specifier.pad_width, (unsigned char)specifier.max_precision);
179 break;
180 case type_signed_size:
181 size += clod_string_put_int(dst, va_arg(args, ptrdiff_t),
182 digit_alphabet, specifier.base, specifier.pad_width, (unsigned char)specifier.max_precision);
183 break;
184 case type_unsigned_size:
185 size += clod_string_put_uint(dst, va_arg(args, size_t),
186 digit_alphabet, specifier.base, specifier.pad_width, (unsigned char)specifier.max_precision);
187 break;
188 case type_ptr:
189 clod_string_put_char(dst, '0');
190 clod_string_put_char(dst, 'x');
191 size += 2 + clod_string_put_uint(dst, (uintptr_t)va_arg(args, void*),
192 digit_alphabet, specifier.base, specifier.pad_width, (unsigned char)specifier.max_precision);
193 break;
194 case type_double:
195 size += clod_string_put_double(dst, va_arg(args, double),
196 digit_alphabet, specifier.base, specifier.pad_width, (unsigned char)specifier.max_precision);
197 break;
198 case type_clod_string:
199 size += clod_string_cat(dst, va_arg(args, struct clod_string));
200 break;
201 case type_string:
202 size += clod_string_cat(dst, clod_string_from_cstr(va_arg(args, const char *)));
203 break;
204 default:
206 size++;
207 }
208 }
209
210 return size;
211}
Sized string helpers.
CLOD_API bool clod_string_remove_prefix(struct clod_string *str, struct clod_string prefix)
Definition string.c:109
#define CLOD_STRING_C(cstr)
String literal constant.
Definition string.h:43
size_t clod_string_cat(struct clod_string *dst, struct clod_string src)
Definition string.c:17
CLOD_API size_t clod_string_put_int(struct clod_string *dst, intmax_t val, struct clod_string alphabet, unsigned char base, unsigned char min_digits, unsigned char max_digits)
Definition serialise.c:16
CLOD_API size_t clod_string_put_uint(struct clod_string *dst, uintmax_t val, struct clod_string alphabet, unsigned char base, unsigned char min_digits, unsigned char max_digits)
Definition serialise.c:50
CLOD_API size_t clod_string_put_double(struct clod_string *dst, double val, struct clod_string alphabet, unsigned char base, unsigned char min_digits, unsigned char max_digits)
Definition string.c:168
struct clod_string clod_string_from_cstr(const char *cstr)
Make a string from a C string.
Definition string.c:5
char clod_string_get_char(struct clod_string *str)
Remove a single char from the start of the string.
Definition string.c:93
CLOD_API uintmax_t clod_string_get_uint(struct clod_string *str, struct clod_string alphabet, unsigned char base)
Definition serialise.c:70
char clod_string_peek_char(struct clod_string str)
Get the first char in the string.
Definition string.c:103
void clod_string_put_char(struct clod_string *str, char c)
Append a single char to the end of the string.
Definition string.c:87