@@ -213,6 +213,8 @@ void dbuf_free(DynBuf *s)
213213 memset (s , 0 , sizeof (* s ));
214214}
215215
216+ /*--- Unicode / UTF-8 utility functions --*/
217+
216218/* Note: at most 31 bits are encoded. At most UTF8_CHAR_LEN_MAX bytes
217219 are output. */
218220int unicode_to_utf8 (uint8_t * buf , unsigned int c )
@@ -315,6 +317,231 @@ int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp)
315317 return c ;
316318}
317319
320+ /*--- integer to string conversions --*/
321+
322+ /* All conversion functions:
323+ - require a destination array `buf` of sufficient length
324+ - write the string representation at the beginning of `buf`
325+ - null terminate the string
326+ - return the string length
327+ */
328+
329+ /* 2 <= base <= 36 */
330+ char const digits36 [36 ] = "0123456789abcdefghijklmnopqrstuvwxyz" ;
331+
332+ /* using u32toa_shift variant */
333+
334+ #define gen_digit (buf , c ) if (is_be()) \
335+ buf = (buf >> 8) | ((uint64_t)(c) << ((sizeof(buf) - 1) * 8)); \
336+ else \
337+ buf = (buf << 8) | (c)
338+
339+ size_t u7toa_shift (char dest [minimum_length (8 )], uint32_t n )
340+ {
341+ size_t len = 1 ;
342+ uint64_t buf = 0 ;
343+ while (n >= 10 ) {
344+ uint32_t quo = n % 10 ;
345+ n /= 10 ;
346+ gen_digit (buf , '0' + quo );
347+ len ++ ;
348+ }
349+ gen_digit (buf , '0' + n );
350+ memcpy (dest , & buf , sizeof buf );
351+ return len ;
352+ }
353+
354+ size_t u07toa_shift (char dest [minimum_length (8 )], uint32_t n , size_t len )
355+ {
356+ size_t i ;
357+ dest += len ;
358+ dest [7 ] = '\0' ;
359+ for (i = 7 ; i -- > 1 ;) {
360+ uint32_t quo = n % 10 ;
361+ n /= 10 ;
362+ dest [i ] = (char )('0' + quo );
363+ }
364+ dest [i ] = (char )('0' + n );
365+ return len + 7 ;
366+ }
367+
368+ size_t u32toa (char buf [minimum_length (11 )], uint32_t n )
369+ {
370+ if (n < 10 ) {
371+ buf [0 ] = (char )('0' + n );
372+ buf [1 ] = '\0' ;
373+ return 1 ;
374+ }
375+ #define TEN_POW_7 10000000
376+ if (n >= TEN_POW_7 ) {
377+ uint32_t quo = n / TEN_POW_7 ;
378+ n %= TEN_POW_7 ;
379+ size_t len = u7toa_shift (buf , quo );
380+ return u07toa_shift (buf , n , len );
381+ }
382+ return u7toa_shift (buf , n );
383+ }
384+
385+ size_t u64toa (char buf [minimum_length (21 )], uint64_t n )
386+ {
387+ if (likely (n < 0x100000000 ))
388+ return u32toa (buf , n );
389+
390+ size_t len ;
391+ if (n >= TEN_POW_7 ) {
392+ uint64_t n1 = n / TEN_POW_7 ;
393+ n %= TEN_POW_7 ;
394+ if (n1 >= TEN_POW_7 ) {
395+ uint32_t quo = n1 / TEN_POW_7 ;
396+ n1 %= TEN_POW_7 ;
397+ len = u7toa_shift (buf , quo );
398+ len = u07toa_shift (buf , n1 , len );
399+ } else {
400+ len = u7toa_shift (buf , n1 );
401+ }
402+ return u07toa_shift (buf , n , len );
403+ }
404+ return u7toa_shift (buf , n );
405+ }
406+
407+ size_t i32toa (char buf [minimum_length (12 )], int32_t n )
408+ {
409+ if (likely (n >= 0 ))
410+ return u32toa (buf , n );
411+
412+ buf [0 ] = '-' ;
413+ return 1 + u32toa (buf + 1 , - (uint32_t )n );
414+ }
415+
416+ size_t i64toa (char buf [minimum_length (22 )], int64_t n )
417+ {
418+ if (likely (n >= 0 ))
419+ return u64toa (buf , n );
420+
421+ buf [0 ] = '-' ;
422+ return 1 + u64toa (buf + 1 , - (uint64_t )n );
423+ }
424+
425+ /* using u32toa_radix_length variant */
426+
427+ static uint8_t const radix_shift [64 ] = {
428+ 0 , 0 , 1 , 0 , 2 , 0 , 0 , 0 , 3 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
429+ 4 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
430+ 5 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
431+ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
432+ };
433+
434+ size_t u32toa_radix (char buf [minimum_length (33 )], uint32_t n , unsigned base )
435+ {
436+ #ifdef USE_SPECIAL_RADIX_10
437+ if (likely (base == 10 ))
438+ return u32toa (buf , n );
439+ #endif
440+ if (n < base ) {
441+ buf [0 ] = digits36 [n ];
442+ buf [1 ] = '\0' ;
443+ return 1 ;
444+ }
445+ int shift = radix_shift [base & 63 ];
446+ if (shift ) {
447+ uint32_t mask = (1 << shift ) - 1 ;
448+ size_t len = (32 - clz32 (n ) + shift - 1 ) / shift ;
449+ size_t last = n & mask ;
450+ n /= base ;
451+ char * end = buf + len ;
452+ * end -- = '\0' ;
453+ * end -- = digits36 [last ];
454+ while (n >= base ) {
455+ size_t quo = n & mask ;
456+ n >>= shift ;
457+ * end -- = digits36 [quo ];
458+ }
459+ * end = digits36 [n ];
460+ return len ;
461+ } else {
462+ size_t len = 2 ;
463+ size_t last = n % base ;
464+ n /= base ;
465+ uint32_t nbase = base ;
466+ while (n >= nbase ) {
467+ nbase *= base ;
468+ len ++ ;
469+ }
470+ char * end = buf + len ;
471+ * end -- = '\0' ;
472+ * end -- = digits36 [last ];
473+ while (n >= base ) {
474+ size_t quo = n % base ;
475+ n /= base ;
476+ * end -- = digits36 [quo ];
477+ }
478+ * end = digits36 [n ];
479+ return len ;
480+ }
481+ }
482+
483+ size_t u64toa_radix (char buf [minimum_length (65 )], uint64_t n , unsigned base )
484+ {
485+ #ifdef USE_SPECIAL_RADIX_10
486+ if (likely (base == 10 ))
487+ return u64toa (buf , n );
488+ #endif
489+ int shift = radix_shift [base & 63 ];
490+ if (shift ) {
491+ if (n < base ) {
492+ buf [0 ] = digits36 [n ];
493+ buf [1 ] = '\0' ;
494+ return 1 ;
495+ }
496+ uint64_t mask = (1 << shift ) - 1 ;
497+ size_t len = (64 - clz64 (n ) + shift - 1 ) / shift ;
498+ size_t last = n & mask ;
499+ n /= base ;
500+ char * end = buf + len ;
501+ * end -- = '\0' ;
502+ * end -- = digits36 [last ];
503+ while (n >= base ) {
504+ size_t quo = n & mask ;
505+ n >>= shift ;
506+ * end -- = digits36 [quo ];
507+ }
508+ * end = digits36 [n ];
509+ return len ;
510+ } else {
511+ if (likely (n < 0x100000000 ))
512+ return u32toa_radix (buf , n , base );
513+ size_t last = n % base ;
514+ n /= base ;
515+ uint64_t nbase = base ;
516+ size_t len = 2 ;
517+ while (n >= nbase ) {
518+ nbase *= base ;
519+ len ++ ;
520+ }
521+ char * end = buf + len ;
522+ * end -- = '\0' ;
523+ * end -- = digits36 [last ];
524+ while (n >= base ) {
525+ size_t quo = n % base ;
526+ n /= base ;
527+ * end -- = digits36 [quo ];
528+ }
529+ * end = digits36 [n ];
530+ return len ;
531+ }
532+ }
533+
534+ size_t i64toa_radix (char buf [minimum_length (66 )], int64_t n , unsigned int base )
535+ {
536+ if (likely (n >= 0 ))
537+ return u64toa_radix (buf , n , base );
538+
539+ buf [0 ] = '-' ;
540+ return 1 + u64toa_radix (buf + 1 , - (uint64_t )n , base );
541+ }
542+
543+ /*---- sorting with opaque argument ----*/
544+
318545typedef void (* exchange_f )(void * a , void * b , size_t size );
319546typedef int (* cmp_f )(const void * , const void * , void * opaque );
320547
@@ -614,6 +841,8 @@ void rqsort(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque)
614841 }
615842}
616843
844+ /*---- Portable time functions ----*/
845+
617846#if defined(_MSC_VER )
618847 // From: https://stackoverflow.com/a/26085827
619848static int gettimeofday_msvc (struct timeval * tp , struct timezone * tzp )
@@ -677,7 +906,7 @@ int64_t js__gettimeofday_us(void) {
677906 return ((int64_t )tv .tv_sec * 1000000 ) + tv .tv_usec ;
678907}
679908
680- /* Cross-platform threading APIs. */
909+ /*--- Cross-platform threading APIs. ---- */
681910
682911#if !defined(EMSCRIPTEN ) && !defined(__wasi__ )
683912
0 commit comments