fennec
Loading...
Searching...
No Matches
formatter.h
Go to the documentation of this file.
1// =====================================================================================================================
2// fennec, a free and open source game engine
3// Copyright © 2025 Medusa Slockbower
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <https://www.gnu.org/licenses/>.
17// =====================================================================================================================
18
30
31#ifndef FENNEC_FORMAT_FORMATTER_H
32#define FENNEC_FORMAT_FORMATTER_H
33
35#include <fennec/string/string.h>
37
38namespace fennec
39{
40
41// base template =======================================================================================================
42
46template<typename T>
47struct formatter {
48 string operator()(const format_arg&, const T&) {
49 static_assert(false, "Formatter not implemented for the provided type.");
50 return string("");
51 }
52};
53
54
55// strings =============================================================================================================
56
57template<size_t N>
58struct formatter<char[N]> {
59 string operator()(const format_arg&, const char (&str)[N]) {
60 return string(str);
61 }
62};
63
64template<size_t N>
65struct formatter<const char[N]> {
66 string operator()(const format_arg&, const char (&str)[N]) {
67 return string(str);
68 }
69};
70
71template<>
72struct formatter<cstring> {
73 string operator()(const format_arg&, const cstring& str) {
74 return str;
75 }
76};
77
78template<>
79struct formatter<string> {
80 string operator()(const format_arg&, const string& str) {
81 return str;
82 }
83};
84
85
86// decimal types =======================================================================================================
87
88template<typename IntT> requires(is_integral_v<IntT> and not is_bool_v<IntT>)
89struct formatter<IntT> {
90 string operator()(const format_arg& fmt, IntT x) {
91 char digits[128] = {};
92 auto chk = fennec::to_chars(digits, digits + sizeof(digits), fennec::abs(x), fmt.base);
93 assertf(chk != nullptr, "fennec::format error, to_chars error");
94 size_t len = chk - digits;
95
96 // handle uppercase
97 if (fmt.upper) {
98 for (auto& digit : digits) {
99 if (digit == 0) {
100 break;
101 }
102 digit = toupper(digit);
103 }
104 }
105
106 const bool has_sign = (x < 0 or fmt.sign != '-');
107 const bool zero = fmt.fill == '0';
108 const size_t prefix = fmt.alt ? (fmt.type == 'd' ? 0 : 2 - (fmt.type == 'o')) : 0;
109 const size_t sgnlen = len + (zero ? has_sign + prefix : 0);
110 const size_t explen = fennec::max(sgnlen, fmt.width) + (zero ? 0 : has_sign + prefix);
111 const size_t fill = fmt.width > sgnlen ? fmt.width - sgnlen : 0;
112 size_t sign = 0;
113
114 string res = string(explen);
115
116 if (fill > 0) {
117 switch (fmt.align) {
118 case '<':
119 memcpy(res.data() + has_sign + prefix, digits, len);
120 memset(res.data() + has_sign + prefix + len, fmt.fill == '0' ? ' ' : fmt.fill, fill);
121 break;
122 case '>': case '\0':
123 memcpy(res.data() + explen - len, digits, len);
124 sign = fmt.fill == '0' ? 0 : explen - len - 1 - prefix;
125 memset(res.data(), fmt.fill, explen - len);
126 break;
127 case '^':
128 size_t bef = fill / 2 + has_sign + prefix;
129 size_t aft = explen - bef;
130 memcpy(res.data() + bef, digits, len);
131 sign = fmt.fill == '0' ? 0 : bef - 1 - prefix;
132 memset(res.data(), fmt.fill, bef);
133 memset(res.data() + bef + len, fmt.fill == '0' ? ' ' : fmt.fill, aft);
134 break;
135
136
137 }
138 } else {
139 memcpy(res.data() + has_sign + prefix, digits, len);
140 }
141
142 if (has_sign) {
143 res[sign] = (x < 0) ? '-' : fmt.sign;
144 }
145
146 if (prefix) {
147 res[sign + has_sign] = '0';
148 if (fmt.type != 'o') {
149 res[sign + has_sign + 1] = fmt.type;
150 }
151 }
152
153 return res;
154 }
155};
156
157template<typename BoolT> requires(is_bool_v<BoolT>)
158struct formatter<BoolT> {
159 string operator()(const format_arg& fmt, BoolT x) {
160 if (fmt.type == 's' or fmt.type == '\0') {
161 return x ? string("true") : string("false");
162 }
163
164 return formatter<uint8_t>{}(fmt, static_cast<uint8_t>(x));
165 }
166};
167
168template<typename FloatT> requires(is_floating_point_v<FloatT>)
169struct formatter<FloatT> {
170 string operator()(const format_arg& fmt, FloatT x) {
171
172 // nan & inf cases
173 if (fennec::isnan(x)) {
174 return string("nan");
175 }
176 if (fennec::isinf(x)) {
177 return string("inf");
178 }
179
180
181 char digits[128] = {};
182 auto chk = fennec::to_chars(digits, digits + sizeof(digits), fennec::abs(x), fmt.type, fmt.precision);
183 assertf(chk != nullptr, "fennec::format error, to_chars error");
184 size_t len = chk - digits;
185
186 // handle uppercase
187 if (fmt.upper) {
188 for (auto& digit : digits) {
189 if (digit == 0) {
190 break;
191 }
192 digit = toupper(digit);
193 }
194 }
195
196 const bool has_sign = (x < 0 or fmt.sign != '-');
197 const bool zero = fmt.fill == '0';
198 const size_t prefix = fmt.alt ? 2 : 0;
199 const size_t sgnlen = len + (zero ? has_sign + prefix : 0);
200 const size_t explen = fennec::max(sgnlen, fmt.width) + (zero ? 0 : has_sign + prefix);
201 const size_t fill = fmt.width > sgnlen ? fmt.width - sgnlen : 0;
202 size_t sign = 0;
203
204 string res = string(explen);
205
206 if (fill > 0) {
207 switch (fmt.align) {
208 case '<':
209 memcpy(res.data() + has_sign + prefix, digits, len);
210 memset(res.data() + has_sign + prefix + len, fmt.fill == '0' ? ' ' : fmt.fill, fill);
211 break;
212 case '>': case '\0':
213 memcpy(res.data() + explen - len, digits, len);
214 sign = fmt.fill == '0' ? 0 : explen - len - 1 - prefix;
215 memset(res.data(), fmt.fill, explen - len);
216 break;
217 case '^':
218 size_t bef = fill / 2 + has_sign + prefix;
219 size_t aft = explen - bef;
220 memcpy(res.data() + bef, digits, len);
221 sign = fmt.fill == '0' ? 0 : bef - 1 - prefix;
222 memset(res.data(), fmt.fill, bef);
223 memset(res.data() + bef + len, fmt.fill == '0' ? ' ' : fmt.fill, aft);
224 break;
225
226
227 }
228 } else {
229 memcpy(res.data() + has_sign + prefix, digits, len);
230 }
231
232 if (has_sign) {
233 res[sign] = (x < 0) ? '-' : fmt.sign;
234 }
235
236 if (prefix) {
237 res[sign + has_sign] = '0';
238 res[sign + has_sign + 1] = fmt.type + ('x' - 'a');
239 }
240
241 return res;
242 }
243
244
245};
246
247}
248
249#endif // FENNEC_FORMAT_FORMATTER_H
constexpr genType max(genType x, genType y)
Returns if , otherwise it returns .
Definition common.h:705
constexpr genType sign(genType x)
Returns if , if , or if .
Definition common.h:306
constexpr genType zero()
Definition constants.h:549
Formatter struct, used to turn values into formatted strings.
Definition formatter.h:47
::uint8_t uint8_t
Unsigned 8-bit integer.
Definition types.h:272