Skip to content

Commit 9597fa0

Browse files
committed
utils: add int*_write_to_ascii_buf functions
Add functions for converting integers to a string, that are better suited for our usage. These new functions they likely perform better than lltoa, since they don't rely on helpers for 64 bit division, and also compiler optimization friendly functions for base 10 and 16 are provided: Compiler is able to optimize n / k, when k is a known constant, by replacing it with a multiplication. Note that these new functions will write characters without C string terminator. Signed-off-by: Davide Bettio <davide@uninstall.it>
1 parent 38b38eb commit 9597fa0

File tree

3 files changed

+221
-0
lines changed

3 files changed

+221
-0
lines changed

src/libAtomVM/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ set(SOURCE_FILES
102102
term.c
103103
timer_list.c
104104
unicode.c
105+
utils.c
105106
valueshashtable.c
106107
)
107108

src/libAtomVM/utils.c

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/*
2+
* This file is part of AtomVM.
3+
*
4+
* Copyright 2025 Davide Bettio <davide@uninstall.it>
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*
18+
* SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
19+
*/
20+
21+
#include "utils.h"
22+
23+
#include <inttypes.h>
24+
#include <stdbool.h>
25+
#include <stdint.h>
26+
#include <string.h>
27+
28+
static char *uintptr_to_a_n(uintptr_t n, unsigned int base, char *out_end)
29+
{
30+
ASSUME((base >= 2) && (base <= 36));
31+
32+
char *c = out_end;
33+
uintptr_t q = n;
34+
do {
35+
c--;
36+
uintptr_t r = (q % base);
37+
*c = (r <= 9) ? '0' + r : 'A' + r - 10;
38+
q /= base;
39+
} while (q);
40+
41+
return c;
42+
}
43+
44+
static char *uintptr_to_a_10(uintptr_t n, char *out_end)
45+
{
46+
char *c = out_end;
47+
uintptr_t q = n;
48+
do {
49+
c--;
50+
*c = '0' + (q % 10);
51+
q /= 10;
52+
} while (q);
53+
54+
return c;
55+
}
56+
57+
static char *uintptr_to_a_16(uintptr_t n, char *out_end)
58+
{
59+
char *c = out_end;
60+
uintptr_t q = n;
61+
do {
62+
c--;
63+
uintptr_t r = (q & 0xF);
64+
*c = (r <= 9) ? '0' + r : 'A' + r - 10;
65+
q >>= 4;
66+
} while (q);
67+
68+
return c;
69+
}
70+
71+
size_t intptr_write_to_ascii_buf(intptr_t n, unsigned int base, char *out_end)
72+
{
73+
ASSUME((base >= 2) && (base <= 36));
74+
75+
// let's avoid undefined behaviors
76+
// -INTPTR_MIN is INTPTR_MAX + 1
77+
uintptr_t pos_n;
78+
if (n >= 0) {
79+
pos_n = n;
80+
} else if (n == INTPTR_MIN) {
81+
pos_n = ((uintptr_t) INTPTR_MAX) + 1;
82+
} else {
83+
pos_n = -n;
84+
}
85+
86+
char *c;
87+
88+
// use optimized versions for 10 and 16
89+
switch (base) {
90+
case 10:
91+
c = uintptr_to_a_10(pos_n, out_end);
92+
break;
93+
case 16:
94+
c = uintptr_to_a_16(pos_n, out_end);
95+
break;
96+
default:
97+
c = uintptr_to_a_n(pos_n, base, out_end);
98+
break;
99+
}
100+
101+
if (n < 0) {
102+
c--;
103+
*c = '-';
104+
}
105+
106+
return out_end - c;
107+
}
108+
109+
#if INT64_MAX > INTPTR_MAX
110+
111+
static char *uint64_to_a_n(uint64_t n, unsigned int base, char *out_end)
112+
{
113+
ASSUME((base >= 2) && (base <= 36));
114+
115+
char *c = out_end;
116+
uint64_t q = n;
117+
do {
118+
c--;
119+
uint64_t r = (q % base);
120+
*c = (r <= 9) ? '0' + r : 'A' + r - 10;
121+
q /= base;
122+
} while (q);
123+
124+
return c;
125+
}
126+
127+
static char *uint64_to_a_10(uint64_t n, char *out_end)
128+
{
129+
char *c = out_end;
130+
uint64_t q = n;
131+
do {
132+
c--;
133+
*c = '0' + (q % 10);
134+
q /= 10;
135+
} while (q);
136+
137+
return c;
138+
}
139+
140+
static char *uint64_to_a_16(uint64_t n, char *out_end)
141+
{
142+
char *c = out_end;
143+
uint64_t q = n;
144+
do {
145+
c--;
146+
uint64_t r = (q & 0xF);
147+
*c = (r <= 9) ? '0' + r : 'A' + r - 10;
148+
q >>= 4;
149+
} while (q);
150+
151+
return c;
152+
}
153+
154+
size_t int64_write_to_ascii_buf(int64_t n, unsigned int base, char *out_end)
155+
{
156+
ASSUME((base >= 2) && (base <= 36));
157+
158+
// let's avoid undefined behaviors
159+
// -INT64_MIN is INT64_MAX + 1
160+
uint64_t pos_n;
161+
if (n >= 0) {
162+
pos_n = n;
163+
} else if (n == INT64_MIN) {
164+
pos_n = ((uint64_t) INT64_MAX) + 1;
165+
} else {
166+
pos_n = -n;
167+
}
168+
169+
char *c;
170+
171+
// use optimized versions for 10 and 16
172+
switch (base) {
173+
case 10:
174+
c = uint64_to_a_10(pos_n, out_end);
175+
break;
176+
case 16:
177+
c = uint64_to_a_16(pos_n, out_end);
178+
break;
179+
default:
180+
c = uint64_to_a_n(pos_n, base, out_end);
181+
break;
182+
}
183+
184+
if (n < 0) {
185+
c--;
186+
*c = '-';
187+
}
188+
189+
return out_end - c;
190+
}
191+
192+
#endif

src/libAtomVM/utils.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#ifndef _UTILS_H_
2929
#define _UTILS_H_
3030

31+
#include <inttypes.h>
3132
#include <stddef.h>
3233
#include <stdio.h>
3334
#include <stdlib.h>
@@ -347,6 +348,33 @@ static inline __attribute__((always_inline)) func_ptr_t cast_void_to_func_ptr(vo
347348
#define ASSUME(...)
348349
#endif
349350

351+
#if INTPTR_MAX <= INT32_MAX
352+
#define INTPTR_WRITE_TO_ASCII_BUF_LEN (32 + 1)
353+
#elif INTPTR_MAX <= INT64_MAX
354+
#define INTPTR_WRITE_TO_ASCII_BUF_LEN (64 + 1)
355+
#endif
356+
357+
#define INT32_WRITE_TO_ASCII_BUF_LEN (32 + 1)
358+
#define INT64_WRITE_TO_ASCII_BUF_LEN (64 + 1)
359+
360+
size_t intptr_write_to_ascii_buf(intptr_t n, unsigned int base, char *out_end);
361+
362+
#if INTPTR_MAX >= INT32_MAX
363+
static inline size_t int32_write_to_ascii_buf(int32_t n, unsigned int base, char *out_end)
364+
{
365+
return intptr_write_to_ascii_buf(n, base, out_end);
366+
}
367+
#endif
368+
369+
#if INT64_MAX > INTPTR_MAX
370+
size_t int64_write_to_ascii_buf(int64_t n, unsigned int base, char *out_end);
371+
#else
372+
static inline size_t int64_write_to_ascii_buf(int64_t n, unsigned int base, char *out_end)
373+
{
374+
return intptr_write_to_ascii_buf(n, base, out_end);
375+
}
376+
#endif
377+
350378
#ifdef __cplusplus
351379
}
352380
#endif

0 commit comments

Comments
 (0)