1 /***************************************************************************************************
2 
3   Zyan Core Library (Zycore-C)
4 
5   Original Author : Florian Bernd
6 
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24 
25 ***************************************************************************************************/
26 
27 #include <Zycore/Format.h>
28 #include <Zycore/LibC.h>
29 
30 /* ============================================================================================== */
31 /* Constants                                                                                      */
32 /* ============================================================================================== */
33 
34 /* ---------------------------------------------------------------------------------------------- */
35 /* Defines                                                                                        */
36 /* ---------------------------------------------------------------------------------------------- */
37 
38 #define ZYCORE_MAXCHARS_DEC_32 10
39 #define ZYCORE_MAXCHARS_DEC_64 20
40 #define ZYCORE_MAXCHARS_HEX_32  8
41 #define ZYCORE_MAXCHARS_HEX_64 16
42 
43 /* ---------------------------------------------------------------------------------------------- */
44 /* Lookup Tables                                                                                  */
45 /* ---------------------------------------------------------------------------------------------- */
46 
47 static const char* const DECIMAL_LOOKUP =
48     "00010203040506070809"
49     "10111213141516171819"
50     "20212223242526272829"
51     "30313233343536373839"
52     "40414243444546474849"
53     "50515253545556575859"
54     "60616263646566676869"
55     "70717273747576777879"
56     "80818283848586878889"
57     "90919293949596979899";
58 
59 /* ---------------------------------------------------------------------------------------------- */
60 /* Static strings                                                                                 */
61 /* ---------------------------------------------------------------------------------------------- */
62 
63 static const ZyanStringView STR_ADD = ZYAN_DEFINE_STRING_VIEW("+");
64 static const ZyanStringView STR_SUB = ZYAN_DEFINE_STRING_VIEW("-");
65 
66 /* ---------------------------------------------------------------------------------------------- */
67 
68 /* ============================================================================================== */
69 /* Internal macros                                                                                */
70 /* ============================================================================================== */
71 
72 /**
73  * @brief   Writes a terminating '\0' character at the end of the string data.
74  */
75 #define ZYCORE_STRING_NULLTERMINATE(string) \
76       *(char*)((ZyanU8*)(string)->vector.data + (string)->vector.size - 1) = '\0';
77 
78 /* ============================================================================================== */
79 /* Internal functions                                                                             */
80 /* ============================================================================================== */
81 
82 /* ---------------------------------------------------------------------------------------------- */
83 /* Decimal                                                                                        */
84 /* ---------------------------------------------------------------------------------------------- */
85 
86 #if defined(ZYAN_X86) || defined(ZYAN_ARM) || defined(ZYAN_EMSCRIPTEN)
ZyanStringAppendDecU32(ZyanString * string,ZyanU32 value,ZyanU8 padding_length)87 ZyanStatus ZyanStringAppendDecU32(ZyanString* string, ZyanU32 value, ZyanU8 padding_length)
88 {
89     if (!string)
90     {
91         return ZYAN_STATUS_INVALID_ARGUMENT;
92     }
93 
94     char buffer[ZYCORE_MAXCHARS_DEC_32];
95     char *buffer_end = &buffer[ZYCORE_MAXCHARS_DEC_32];
96     char *buffer_write_pointer = buffer_end;
97     while (value >= 100)
98     {
99         const ZyanU32 value_old = value;
100         buffer_write_pointer -= 2;
101         value /= 100;
102         ZYAN_MEMCPY(buffer_write_pointer, &DECIMAL_LOOKUP[(value_old - (value * 100)) * 2], 2);
103     }
104     buffer_write_pointer -= 2;
105     ZYAN_MEMCPY(buffer_write_pointer, &DECIMAL_LOOKUP[value * 2], 2);
106 
107     const ZyanUSize offset_odd    = (ZyanUSize)(value < 10);
108     const ZyanUSize length_number = buffer_end - buffer_write_pointer - offset_odd;
109     const ZyanUSize length_total  = ZYAN_MAX(length_number, padding_length);
110     const ZyanUSize length_target = string->vector.size;
111 
112     if (string->vector.size + length_total > string->vector.capacity)
113     {
114         ZYAN_CHECK(ZyanStringResize(string, string->vector.size + length_total - 1));
115     }
116 
117     ZyanUSize offset_write = 0;
118     if (padding_length > length_number)
119     {
120         offset_write = padding_length - length_number;
121         ZYAN_MEMSET((char*)string->vector.data + length_target - 1, '0', offset_write);
122     }
123 
124     ZYAN_MEMCPY((char*)string->vector.data + length_target + offset_write - 1,
125         buffer_write_pointer + offset_odd, length_number);
126     string->vector.size = length_target + length_total;
127     ZYCORE_STRING_NULLTERMINATE(string);
128 
129     return ZYAN_STATUS_SUCCESS;
130 }
131 #endif
132 
ZyanStringAppendDecU64(ZyanString * string,ZyanU64 value,ZyanU8 padding_length)133 ZyanStatus ZyanStringAppendDecU64(ZyanString* string, ZyanU64 value, ZyanU8 padding_length)
134 {
135     if (!string)
136     {
137         return ZYAN_STATUS_INVALID_ARGUMENT;
138     }
139 
140     char buffer[ZYCORE_MAXCHARS_DEC_64];
141     char *buffer_end = &buffer[ZYCORE_MAXCHARS_DEC_64];
142     char *buffer_write_pointer = buffer_end;
143     while (value >= 100)
144     {
145         const ZyanU64 value_old = value;
146         buffer_write_pointer -= 2;
147         value /= 100;
148         ZYAN_MEMCPY(buffer_write_pointer, &DECIMAL_LOOKUP[(value_old - (value * 100)) * 2], 2);
149     }
150     buffer_write_pointer -= 2;
151     ZYAN_MEMCPY(buffer_write_pointer, &DECIMAL_LOOKUP[value * 2], 2);
152 
153     const ZyanUSize offset_odd    = (ZyanUSize)(value < 10);
154     const ZyanUSize length_number = buffer_end - buffer_write_pointer - offset_odd;
155     const ZyanUSize length_total  = ZYAN_MAX(length_number, padding_length);
156     const ZyanUSize length_target = string->vector.size;
157 
158     if (string->vector.size + length_total > string->vector.capacity)
159     {
160         ZYAN_CHECK(ZyanStringResize(string, string->vector.size + length_total - 1));
161     }
162 
163     ZyanUSize offset_write = 0;
164     if (padding_length > length_number)
165     {
166         offset_write = padding_length - length_number;
167         ZYAN_MEMSET((char*)string->vector.data + length_target - 1, '0', offset_write);
168     }
169 
170     ZYAN_MEMCPY((char*)string->vector.data + length_target + offset_write - 1,
171         buffer_write_pointer + offset_odd, length_number);
172     string->vector.size = length_target + length_total;
173     ZYCORE_STRING_NULLTERMINATE(string);
174 
175     return ZYAN_STATUS_SUCCESS;
176 }
177 
178 /* ---------------------------------------------------------------------------------------------- */
179 /* Hexadecimal                                                                                    */
180 /* ---------------------------------------------------------------------------------------------- */
181 
182 #if defined(ZYAN_X86) || defined(ZYAN_ARM) || defined(ZYAN_EMSCRIPTEN)
ZyanStringAppendHexU32(ZyanString * string,ZyanU32 value,ZyanU8 padding_length,ZyanBool uppercase)183 ZyanStatus ZyanStringAppendHexU32(ZyanString* string, ZyanU32 value, ZyanU8 padding_length,
184     ZyanBool uppercase)
185 {
186     if (!string)
187     {
188         return ZYAN_STATUS_INVALID_ARGUMENT;
189     }
190 
191     const ZyanUSize len = string->vector.size;
192     ZyanUSize remaining = string->vector.capacity - string->vector.size;
193 
194     if (remaining < (ZyanUSize)padding_length)
195     {
196         ZYAN_CHECK(ZyanStringResize(string, len + padding_length - 1));
197         remaining = padding_length;
198     }
199 
200     if (!value)
201     {
202         const ZyanU8 n = (padding_length ? padding_length : 1);
203 
204         if (remaining < (ZyanUSize)n)
205         {
206             ZYAN_CHECK(ZyanStringResize(string, string->vector.size + n - 1));
207         }
208 
209         ZYAN_MEMSET((char*)string->vector.data + len - 1, '0', n);
210         string->vector.size = len + n;
211         ZYCORE_STRING_NULLTERMINATE(string);
212 
213         return ZYAN_STATUS_SUCCESS;
214     }
215 
216     ZyanU8 n = 0;
217     char* buffer = ZYAN_NULL;
218     for (ZyanI8 i = ZYCORE_MAXCHARS_HEX_32 - 1; i >= 0; --i)
219     {
220         const ZyanU8 v = (value >> i * 4) & 0x0F;
221         if (!n)
222         {
223             if (!v)
224             {
225                 continue;
226             }
227             if (remaining <= (ZyanU8)i)
228             {
229                 ZYAN_CHECK(ZyanStringResize(string, string->vector.size + i));
230             }
231             buffer = (char*)string->vector.data + len - 1;
232             if (padding_length > i)
233             {
234                 n = padding_length - i - 1;
235                 ZYAN_MEMSET(buffer, '0', n);
236             }
237         }
238         ZYAN_ASSERT(buffer);
239         if (uppercase)
240         {
241             buffer[n++] = "0123456789ABCDEF"[v];
242         } else
243         {
244             buffer[n++] = "0123456789abcdef"[v];
245         }
246     }
247     string->vector.size = len + n;
248     ZYCORE_STRING_NULLTERMINATE(string);
249 
250     return ZYAN_STATUS_SUCCESS;
251 }
252 #endif
253 
ZyanStringAppendHexU64(ZyanString * string,ZyanU64 value,ZyanU8 padding_length,ZyanBool uppercase)254 ZyanStatus ZyanStringAppendHexU64(ZyanString* string, ZyanU64 value, ZyanU8 padding_length,
255     ZyanBool uppercase)
256 {
257     if (!string)
258     {
259         return ZYAN_STATUS_INVALID_ARGUMENT;
260     }
261 
262     const ZyanUSize len = string->vector.size;
263     ZyanUSize remaining = string->vector.capacity - string->vector.size;
264 
265     if (remaining < (ZyanUSize)padding_length)
266     {
267         ZYAN_CHECK(ZyanStringResize(string, len + padding_length - 1));
268         remaining = padding_length;
269     }
270 
271     if (!value)
272     {
273         const ZyanU8 n = (padding_length ? padding_length : 1);
274 
275         if (remaining < (ZyanUSize)n)
276         {
277             ZYAN_CHECK(ZyanStringResize(string, string->vector.size + n - 1));
278         }
279 
280         ZYAN_MEMSET((char*)string->vector.data + len - 1, '0', n);
281         string->vector.size = len + n;
282         ZYCORE_STRING_NULLTERMINATE(string);
283 
284         return ZYAN_STATUS_SUCCESS;
285     }
286 
287     ZyanU8 n = 0;
288     char* buffer = ZYAN_NULL;
289     for (ZyanI8 i = ((value & 0xFFFFFFFF00000000) ?
290         ZYCORE_MAXCHARS_HEX_64 : ZYCORE_MAXCHARS_HEX_32) - 1; i >= 0; --i)
291     {
292         const ZyanU8 v = (value >> i * 4) & 0x0F;
293         if (!n)
294         {
295             if (!v)
296             {
297                 continue;
298             }
299             if (remaining <= (ZyanU8)i)
300             {
301                 ZYAN_CHECK(ZyanStringResize(string, string->vector.size + i));
302             }
303             buffer = (char*)string->vector.data + len - 1;
304             if (padding_length > i)
305             {
306                 n = padding_length - i - 1;
307                 ZYAN_MEMSET(buffer, '0', n);
308             }
309         }
310         ZYAN_ASSERT(buffer);
311         if (uppercase)
312         {
313             buffer[n++] = "0123456789ABCDEF"[v];
314         } else
315         {
316             buffer[n++] = "0123456789abcdef"[v];
317         }
318     }
319     string->vector.size = len + n;
320     ZYCORE_STRING_NULLTERMINATE(string);
321 
322     return ZYAN_STATUS_SUCCESS;
323 }
324 
325 /* ---------------------------------------------------------------------------------------------- */
326 
327 /* ============================================================================================== */
328 /* Exported functions                                                                             */
329 /* ============================================================================================== */
330 
331 /* ---------------------------------------------------------------------------------------------- */
332 /* Insertion                                                                                      */
333 /* ---------------------------------------------------------------------------------------------- */
334 
335 //ZyanStatus ZyanStringInsertFormat(ZyanString* string, ZyanUSize index, const char* format, ...)
336 //{
337 //
338 //}
339 //
340 ///* ---------------------------------------------------------------------------------------------- */
341 //
342 //ZyanStatus ZyanStringInsertDecU(ZyanString* string, ZyanUSize index, ZyanU64 value,
343 //    ZyanUSize padding_length)
344 //{
345 //
346 //}
347 //
348 //ZyanStatus ZyanStringInsertDecS(ZyanString* string, ZyanUSize index, ZyanI64 value,
349 //    ZyanUSize padding_length, ZyanBool force_sign, const ZyanString* prefix)
350 //{
351 //
352 //}
353 //
354 //ZyanStatus ZyanStringInsertHexU(ZyanString* string, ZyanUSize index, ZyanU64 value,
355 //    ZyanUSize padding_length, ZyanBool uppercase)
356 //{
357 //
358 //}
359 //
360 //ZyanStatus ZyanStringInsertHexS(ZyanString* string, ZyanUSize index, ZyanI64 value,
361 //    ZyanUSize padding_length, ZyanBool uppercase, ZyanBool force_sign, const ZyanString* prefix)
362 //{
363 //
364 //}
365 
366 /* ---------------------------------------------------------------------------------------------- */
367 /* Appending                                                                                      */
368 /* ---------------------------------------------------------------------------------------------- */
369 
370 #ifndef ZYAN_NO_LIBC
371 
ZyanStringAppendFormat(ZyanString * string,const char * format,...)372 ZyanStatus ZyanStringAppendFormat(ZyanString* string, const char* format, ...)
373 {
374     if (!string || !format)
375     {
376         return ZYAN_STATUS_INVALID_ARGUMENT;
377     }
378 
379     ZyanVAList arglist;
380     ZYAN_VA_START(arglist, format);
381 
382     const ZyanUSize len = string->vector.size;
383 
384     ZyanI32 w = ZYAN_VSNPRINTF((char*)string->vector.data + len - 1,
385         string->vector.capacity - len + 1, format, arglist);
386     if (w < 0)
387     {
388         ZYAN_VA_END(arglist);
389         return ZYAN_STATUS_FAILED;
390     }
391     if (w <= (ZyanI32)(string->vector.capacity - len))
392     {
393         string->vector.size = len + w;
394 
395         ZYAN_VA_END(arglist);
396         return ZYAN_STATUS_SUCCESS;
397     }
398 
399     // The remaining capacity was not sufficent to fit the formatted string. Trying to resize ..
400     const ZyanStatus status = ZyanStringResize(string, string->vector.size + w - 1);
401     if (!ZYAN_SUCCESS(status))
402     {
403         ZYAN_VA_END(arglist);
404         return status;
405     }
406 
407     w = ZYAN_VSNPRINTF((char*)string->vector.data + len - 1,
408         string->vector.capacity - string->vector.size + 1, format, arglist);
409     if (w < 0)
410     {
411         ZYAN_VA_END(arglist);
412         return ZYAN_STATUS_FAILED;
413     }
414     ZYAN_ASSERT(w <= (ZyanI32)(string->vector.capacity - string->vector.size));
415 
416     ZYAN_VA_END(arglist);
417     return ZYAN_STATUS_SUCCESS;
418 }
419 
420 #endif // ZYAN_NO_LIBC
421 
422 /* ---------------------------------------------------------------------------------------------- */
423 
ZyanStringAppendDecU(ZyanString * string,ZyanU64 value,ZyanU8 padding_length)424 ZyanStatus ZyanStringAppendDecU(ZyanString* string, ZyanU64 value, ZyanU8 padding_length)
425 {
426 #if defined(ZYAN_X64) || defined(ZYAN_AARCH64)
427     return ZyanStringAppendDecU64(string, value, padding_length);
428 #else
429     // Working with 64-bit values is slow on non 64-bit systems
430     if (value & 0xFFFFFFFF00000000)
431     {
432         return ZyanStringAppendDecU64(string, value, padding_length);
433     }
434     return ZyanStringAppendDecU32(string, (ZyanU32)value, padding_length);
435 #endif
436 }
437 
ZyanStringAppendDecS(ZyanString * string,ZyanI64 value,ZyanU8 padding_length,ZyanBool force_sign,const ZyanStringView * prefix)438 ZyanStatus ZyanStringAppendDecS(ZyanString* string, ZyanI64 value, ZyanU8 padding_length,
439     ZyanBool force_sign, const ZyanStringView* prefix)
440 {
441     if (value < 0)
442     {
443         ZYAN_CHECK(ZyanStringAppend(string, &STR_SUB));
444         if (prefix)
445         {
446             ZYAN_CHECK(ZyanStringAppend(string, prefix));
447         }
448         return ZyanStringAppendDecU(string, -value, padding_length);
449     }
450 
451     if (force_sign)
452     {
453         ZYAN_ASSERT(value >= 0);
454         ZYAN_CHECK(ZyanStringAppend(string, &STR_ADD));
455     }
456 
457     if (prefix)
458     {
459         ZYAN_CHECK(ZyanStringAppend(string, prefix));
460     }
461     return ZyanStringAppendDecU(string, value, padding_length);
462 }
463 
ZyanStringAppendHexU(ZyanString * string,ZyanU64 value,ZyanU8 padding_length,ZyanBool uppercase)464 ZyanStatus ZyanStringAppendHexU(ZyanString* string, ZyanU64 value, ZyanU8 padding_length,
465     ZyanBool uppercase)
466 {
467 #if defined(ZYAN_X64) || defined(ZYAN_AARCH64)
468     return ZyanStringAppendHexU64(string, value, padding_length, uppercase);
469 #else
470     // Working with 64-bit values is slow on non 64-bit systems
471     if (value & 0xFFFFFFFF00000000)
472     {
473         return ZyanStringAppendHexU64(string, value, padding_length, uppercase);
474     }
475     return ZyanStringAppendHexU32(string, (ZyanU32)value, padding_length, uppercase);
476 #endif
477 }
478 
ZyanStringAppendHexS(ZyanString * string,ZyanI64 value,ZyanU8 padding_length,ZyanBool uppercase,ZyanBool force_sign,const ZyanStringView * prefix)479 ZyanStatus ZyanStringAppendHexS(ZyanString* string, ZyanI64 value, ZyanU8 padding_length,
480     ZyanBool uppercase, ZyanBool force_sign, const ZyanStringView* prefix)
481 {
482     if (value < 0)
483     {
484         ZYAN_CHECK(ZyanStringAppend(string, &STR_SUB));
485         if (prefix)
486         {
487             ZYAN_CHECK(ZyanStringAppend(string, prefix));
488         }
489         return ZyanStringAppendHexU(string, -value, padding_length, uppercase);
490     }
491 
492     if (force_sign)
493     {
494         ZYAN_ASSERT(value >= 0);
495         ZYAN_CHECK(ZyanStringAppend(string, &STR_ADD));
496     }
497 
498     if (prefix)
499     {
500         ZYAN_CHECK(ZyanStringAppend(string, prefix));
501     }
502     return ZyanStringAppendHexU(string, value, padding_length, uppercase);
503 }
504 
505 /* ---------------------------------------------------------------------------------------------- */
506 
507 /* ============================================================================================== */
508