Skip to content

Commit 561f330

Browse files
committed
term.c: add a generic term_funprint function
Make term printing generic by taking a printer function. Signed-off-by: Davide Bettio <[email protected]>
1 parent ab4a8d8 commit 561f330

File tree

3 files changed

+191
-38
lines changed

3 files changed

+191
-38
lines changed

src/libAtomVM/term.c

Lines changed: 162 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -28,29 +28,71 @@
2828

2929
#include <ctype.h>
3030
#include <inttypes.h>
31+
#include <stdarg.h>
32+
#include <stddef.h>
3133
#include <stdio.h>
3234

35+
//TODO use macro from utils
36+
#ifndef CONTAINER_OF
37+
#define CONTAINER_OF(ptr, type, member) \
38+
((type *) (((char *) (ptr)) - offsetof(type, member)))
39+
#endif
40+
41+
struct FprintfFun
42+
{
43+
PrinterFun base;
44+
FILE *stream;
45+
};
46+
3347
const term empty_tuple = 0;
3448

49+
int fprintf_printer(PrinterFun *fun, const char *fmt, ...)
50+
{
51+
int ret;
52+
53+
va_list args;
54+
va_start(args, fmt);
55+
56+
FILE *stream = CONTAINER_OF(fun, struct FprintfFun, base)->stream;
57+
ret = vfprintf(stream, fmt, args);
58+
59+
va_end(args);
60+
61+
return ret;
62+
}
63+
3564
void term_display(FILE *fd, term t, const Context *ctx)
3665
{
3766
term_fprint(fd, t, ctx->global);
3867
}
3968

40-
void term_fprint(FILE *fd, term t, const GlobalContext *global)
69+
int term_fprint(FILE *stream, term t, const GlobalContext *global)
70+
{
71+
struct FprintfFun fprintf_fun = {
72+
.base = {
73+
.print = fprintf_printer
74+
},
75+
.stream = stream
76+
};
77+
78+
return term_funprint(&fprintf_fun.base, t, global);
79+
}
80+
81+
int term_funprint(PrinterFun *fun, term t, const GlobalContext *global)
4182
{
4283
if (term_is_atom(t)) {
4384
int atom_index = term_to_atom_index(t);
4485
AtomString atom_string = (AtomString) valueshashtable_get_value(
4586
global->atoms_ids_table, atom_index, (unsigned long) NULL);
46-
fprintf(fd, "%.*s", (int) atom_string_len(atom_string), (char *) atom_string_data(atom_string));
87+
return fun->print(fun, "%.*s", (int) atom_string_len(atom_string),
88+
(char *) atom_string_data(atom_string));
4789

4890
} else if (term_is_integer(t)) {
4991
avm_int_t iv = term_to_int(t);
50-
fprintf(fd, AVM_INT_FMT, iv);
92+
return fun->print(fun, AVM_INT_FMT, iv);
5193

5294
} else if (term_is_nil(t)) {
53-
fprintf(fd, "[]");
95+
return fun->print(fun, "[]");
5496

5597
} else if (term_is_nonempty_list(t)) {
5698
int is_printable = 1;
@@ -69,33 +111,55 @@ void term_fprint(FILE *fd, term t, const GlobalContext *global)
69111
int ok;
70112
char *printable = interop_list_to_string(t, &ok);
71113
if (LIKELY(ok)) {
72-
fprintf(fd, "\"%s\"", printable);
114+
int ret = fun->print(fun, "\"%s\"", printable);
73115
free(printable);
116+
return ret;
74117
} else {
75-
fprintf(fd, "???");
118+
return fun->print(fun, "???");
76119
}
77120

78121
} else {
79-
fputc('[', fd);
122+
int ret = fun->print(fun, "[");
123+
if (UNLIKELY(ret < 0)) {
124+
return ret;
125+
}
80126
int display_separator = 0;
81127
while (term_is_nonempty_list(t)) {
82128
if (display_separator) {
83-
fputc(',', fd);
129+
ret += fun->print(fun, ",");
84130
} else {
85131
display_separator = 1;
86132
}
87133

88-
term_fprint(fd, term_get_list_head(t), global);
134+
int printed = term_funprint(fun, term_get_list_head(t), global);
135+
if (UNLIKELY(printed < 0)) {
136+
return printed;
137+
}
138+
ret += printed;
89139
t = term_get_list_tail(t);
90140
}
91141
if (!term_is_nil(t)) {
92-
fputc('|', fd);
93-
term_fprint(fd, t, global);
142+
int printed = fun->print(fun, "|");
143+
if (UNLIKELY(printed < 0)) {
144+
return printed;
145+
}
146+
ret += printed;
147+
148+
printed = term_funprint(fun, t, global);
149+
if (UNLIKELY(printed < 0)) {
150+
return printed;
151+
}
152+
ret += printed;
153+
}
154+
int printed = fun->print(fun, "]");
155+
if (UNLIKELY(printed < 0)) {
156+
return printed;
94157
}
95-
fputc(']', fd);
158+
ret += printed;
159+
return ret;
96160
}
97161
} else if (term_is_pid(t)) {
98-
fprintf(fd, "<0.%i.0>", term_to_local_process_id(t));
162+
return fun->print(fun, "<0.%i.0>", term_to_local_process_id(t));
99163

100164
} else if (term_is_function(t)) {
101165
const term *boxed_value = term_to_const_term_ptr(t);
@@ -107,35 +171,77 @@ void term_fprint(FILE *fd, term t, const GlobalContext *global)
107171
#else
108172
"#Fun<erl_eval.%lu.%llu>";
109173
#endif
110-
fprintf(fd, format, fun_index, (unsigned long) fun_module);
174+
return fun->print(fun, format, fun_index, (unsigned long) fun_module);
111175

112176
} else if (term_is_tuple(t)) {
113-
fputc('{', fd);
177+
int ret = fun->print(fun, "{");
178+
if (UNLIKELY(ret < 0)) {
179+
return ret;
180+
}
114181

115182
int tuple_size = term_get_tuple_arity(t);
116183
for (int i = 0; i < tuple_size; i++) {
117184
if (i != 0) {
118-
fputc(',', fd);
185+
int printed = fun->print(fun, ",");
186+
if (UNLIKELY(printed < 0)) {
187+
return printed;
188+
}
189+
ret += printed;
190+
}
191+
int printed = term_funprint(fun, term_get_tuple_element(t, i), global);
192+
if (UNLIKELY(printed < 0)) {
193+
return printed;
119194
}
120-
term_fprint(fd, term_get_tuple_element(t, i), global);
195+
ret += printed;
121196
}
122197

123-
fputc('}', fd);
198+
int printed = fun->print(fun, "}");
199+
if (UNLIKELY(printed < 0)) {
200+
return printed;
201+
}
202+
ret += printed;
203+
return ret;
124204

125205
} else if (term_is_map(t)) {
126-
fprintf(fd, "#{");
206+
int ret = fun->print(fun, "#{");
207+
if (UNLIKELY(ret < 0)) {
208+
return ret;
209+
}
127210

128211
int map_size = term_get_map_size(t);
129212
for (int i = 0; i < map_size; i++) {
130213
if (i != 0) {
131-
fputc(',', fd);
214+
int printed = fun->print(fun, ",");
215+
if (UNLIKELY(printed < 0)) {
216+
return printed;
217+
}
218+
ret += printed;
219+
}
220+
int printed = term_funprint(fun, term_get_map_key(t, i), global);
221+
if (UNLIKELY(printed < 0)) {
222+
return printed;
223+
}
224+
ret += printed;
225+
226+
printed = fun->print(fun, "=>");
227+
if (UNLIKELY(printed < 0)) {
228+
return printed;
229+
}
230+
ret += printed;
231+
232+
printed = term_funprint(fun, term_get_map_value(t, i), global);
233+
if (UNLIKELY(printed < 0)) {
234+
return printed;
132235
}
133-
term_fprint(fd, term_get_map_key(t, i), global);
134-
fprintf(fd, "=>");
135-
term_fprint(fd, term_get_map_value(t, i), global);
236+
ret += printed;
136237
}
137238

138-
fputc('}', fd);
239+
int printed = fun->print(fun, "}");
240+
if (UNLIKELY(printed < 0)) {
241+
return printed;
242+
}
243+
ret += printed;
244+
return ret;
139245

140246
} else if (term_is_binary(t)) {
141247
int len = term_binary_size(t);
@@ -149,24 +255,45 @@ void term_fprint(FILE *fd, term t, const GlobalContext *global)
149255
}
150256
}
151257

152-
fprintf(fd, "<<");
258+
int ret = fun->print(fun, "<<");
259+
if (UNLIKELY(ret < 0)) {
260+
return ret;
261+
}
262+
153263
if (is_printable) {
154-
fprintf(fd, "\"%.*s\"", len, binary_data);
264+
int printed = fun->print(fun, "\"%.*s\"", len, binary_data);
265+
if (UNLIKELY(printed < 0)) {
266+
return printed;
267+
}
268+
ret += printed;
155269

156270
} else {
157271
int display_separator = 0;
158272
for (int i = 0; i < len; i++) {
159273
if (display_separator) {
160-
fputc(',', fd);
274+
int printed = fun->print(fun, ",");
275+
if (UNLIKELY(printed < 0)) {
276+
return printed;
277+
}
278+
ret += printed;
161279
} else {
162280
display_separator = 1;
163281
}
164282

165283
uint8_t c = (uint8_t) binary_data[i];
166-
fprintf(fd, "%i", (int) c);
284+
int printed = fun->print(fun, "%i", (int) c);
285+
if (UNLIKELY(printed < 0)) {
286+
return printed;
287+
}
288+
ret += printed;
167289
}
168290
}
169-
fprintf(fd, ">>");
291+
int printed = fun->print(fun, ">>");
292+
if (UNLIKELY(printed < 0)) {
293+
return printed;
294+
}
295+
ret += printed;
296+
return ret;
170297

171298
} else if (term_is_reference(t)) {
172299
const char *format =
@@ -175,30 +302,28 @@ void term_fprint(FILE *fd, term t, const GlobalContext *global)
175302
#else
176303
"#Ref<0.0.0.%lu>";
177304
#endif
178-
fprintf(fd, format, term_to_ref_ticks(t));
305+
return fun->print(fun, format, term_to_ref_ticks(t));
179306

180307
} else if (term_is_boxed_integer(t)) {
181308
int size = term_boxed_size(t);
182309
switch (size) {
183310
case 1:
184-
fprintf(fd, AVM_INT_FMT, term_unbox_int(t));
185-
break;
311+
return fun->print(fun, AVM_INT_FMT, term_unbox_int(t));
186312

187313
#if BOXED_TERMS_REQUIRED_FOR_INT64 == 2
188314
case 2:
189-
fprintf(fd, AVM_INT64_FMT, term_unbox_int64(t));
190-
break;
315+
return fun->print(fun, AVM_INT64_FMT, term_unbox_int64(t));
191316
#endif
192-
193317
default:
194318
AVM_ABORT();
195319
}
196320

197321
} else if (term_is_float(t)) {
198322
avm_float_t f = term_to_float(t);
199-
fprintf(fd, AVM_FLOAT_FMT, f);
323+
return fun->print(fun, AVM_FLOAT_FMT, f);
324+
200325
} else {
201-
fprintf(fd, "Unknown term type: %" TERM_U_FMT, t);
326+
return fun->print(fun, "Unknown term type: %" TERM_U_FMT, t);
202327
}
203328
}
204329

src/libAtomVM/term.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,15 @@ extern "C" {
9191
typedef struct GlobalContext GlobalContext;
9292
#endif
9393

94+
typedef struct PrinterFun PrinterFun;
95+
96+
typedef int (*printer_function_t)(PrinterFun *fun, const char *fmt, ...) PRINTF_FORMAT_ARGS(2, 3);
97+
98+
struct PrinterFun
99+
{
100+
printer_function_t print;
101+
};
102+
94103
enum RefcBinaryFlags
95104
{
96105
RefcNoFlags = 0,
@@ -1404,15 +1413,27 @@ static inline int term_is_number(term t)
14041413
*/
14051414
void term_display(FILE *fd, term t, const Context *ctx);
14061415

1416+
/**
1417+
* @brief Prints a term using given printer fun
1418+
*
1419+
* @details Print any given term using a printer fun
1420+
* @param fd the file where the term will be printed.
1421+
* @param t the term that will be printed.
1422+
* @param global the \c GlobalContext.
1423+
* @returns the number of printed characters.
1424+
*/
1425+
int term_funprint(PrinterFun *pf, term t, const GlobalContext *global);
1426+
14071427
/**
14081428
* @brief Prints a term to the given file
14091429
*
14101430
* @details Print any given term to the given file.
14111431
* @param fd the file where the term will be printed.
14121432
* @param t the term that will be printed.
14131433
* @param global the \c GlobalContext.
1434+
* @returns the number of printed characters.
14141435
*/
1415-
void term_fprint(FILE *fd, term t, const GlobalContext *global);
1436+
int term_fprint(FILE *fd, term t, const GlobalContext *global);
14161437

14171438
/**
14181439
* @brief Checks if a term is a string (i.e., a list of characters)

src/libAtomVM/utils.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,4 +187,11 @@ static inline void *rand_fail_calloc(int n, unsigned long alloc_size)
187187
#define AVM_ABORT() abort()
188188
#endif
189189

190+
#ifdef __GNUC__
191+
#define PRINTF_FORMAT_ARGS(str_pos, arg_pos) \
192+
__attribute__ ((format (printf, str_pos, arg_pos)))
193+
#else
194+
#define PRINTF_FORMAT_ARGS(...)
195+
#endif
196+
190197
#endif

0 commit comments

Comments
 (0)