Skip to content

Commit 3c3efb6

Browse files
committed
Implement lists:flatten/1 as a NIF
Signed-off-by: Paul Guyot <[email protected]>
1 parent 58768ab commit 3c3efb6

File tree

4 files changed

+101
-16
lines changed

4 files changed

+101
-16
lines changed

libs/estdlib/src/lists.erl

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -452,25 +452,12 @@ any(Fun, L) ->
452452
%%-----------------------------------------------------------------------------
453453
%% @param L the list to flatten
454454
%% @returns flattened list
455-
%% @doc recursively flattens elements of L into a single list
455+
%% @doc Flattens elements of L into a single list
456456
%% @end
457457
%%-----------------------------------------------------------------------------
458458
-spec flatten(L :: list()) -> list().
459-
flatten(L) when is_list(L) ->
460-
flatten(L, []).
461-
462-
%% @private
463-
%% pre: Accum is flattened
464-
flatten([], Accum) ->
465-
Accum;
466-
flatten([H | T], Accum) when is_list(H) ->
467-
FlattenedT = flatten(T, Accum),
468-
flatten(H, FlattenedT);
469-
flatten([H | T], Accum) ->
470-
FlattenedT = flatten(T, Accum),
471-
[H | FlattenedT].
472-
473-
%% post: return is flattened
459+
flatten(_L) ->
460+
erlang:nif_error(undefined).
474461

475462
%%-----------------------------------------------------------------------------
476463
%% @param F the function to apply to elements of L

src/libAtomVM/nifs.c

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#include "smp.h"
5959
#include "synclist.h"
6060
#include "sys.h"
61+
#include "tempstack.h"
6162
#include "term.h"
6263
#include "term_typedef.h"
6364
#include "unicode.h"
@@ -213,6 +214,7 @@ static term nif_jit_backend_module(Context *ctx, int argc, term argv[]);
213214
static term nif_jit_variant(Context *ctx, int argc, term argv[]);
214215
#endif
215216
static term nif_lists_reverse(Context *ctx, int argc, term argv[]);
217+
static term nif_lists_flatten(Context *ctx, int argc, term argv[]);
216218
static term nif_lists_keyfind(Context *ctx, int argc, term argv[]);
217219
static term nif_lists_keymember(Context *ctx, int argc, term argv[]);
218220
static term nif_lists_member(Context *ctx, int argc, term argv[]);
@@ -827,6 +829,10 @@ static const struct Nif erlang_lists_subtract_nif = {
827829
.base.type = NIFFunctionType,
828830
.nif_ptr = nif_erlang_lists_subtract
829831
};
832+
static const struct Nif lists_flatten_nif = {
833+
.base.type = NIFFunctionType,
834+
.nif_ptr = nif_lists_flatten
835+
};
830836
static const struct Nif lists_member_nif = {
831837
.base.type = NIFFunctionType,
832838
.nif_ptr = nif_lists_member
@@ -6125,6 +6131,96 @@ static term nif_erlang_lists_subtract(Context *ctx, int argc, term argv[])
61256131
return result;
61266132
}
61276133

6134+
static term nif_lists_flatten(Context *ctx, int argc, term argv[])
6135+
{
6136+
UNUSED(argc)
6137+
6138+
// Compute resulting list length
6139+
size_t result_len = 0;
6140+
term list = argv[0];
6141+
6142+
if (term_is_nil(list)) {
6143+
return list;
6144+
}
6145+
6146+
VALIDATE_VALUE(list, term_is_nonempty_list);
6147+
6148+
struct TempStack temp_stack;
6149+
if (UNLIKELY(temp_stack_init(&temp_stack) != TempStackOk)) {
6150+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
6151+
}
6152+
if (UNLIKELY(temp_stack_push(&temp_stack, list) != TempStackOk)) {
6153+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
6154+
}
6155+
while (!temp_stack_is_empty(&temp_stack)) {
6156+
term t = temp_stack_pop(&temp_stack);
6157+
6158+
if (term_is_nonempty_list(t)) {
6159+
term t_head = term_get_list_head(t);
6160+
term t_tail = term_get_list_tail(t);
6161+
if (term_is_nonempty_list(t_tail)) {
6162+
if (UNLIKELY(temp_stack_push(&temp_stack, t_tail) != TempStackOk)) {
6163+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
6164+
}
6165+
} else if (!term_is_nil(t_tail)) {
6166+
RAISE_ERROR(BADARG_ATOM);
6167+
}
6168+
if (!term_is_nil(t_head)) {
6169+
if (UNLIKELY(temp_stack_push(&temp_stack, t_head) != TempStackOk)) {
6170+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
6171+
}
6172+
}
6173+
} else {
6174+
result_len++;
6175+
}
6176+
}
6177+
6178+
// Allocate flattened list and build it.
6179+
if (UNLIKELY(memory_ensure_free_with_roots(ctx, CONS_SIZE * result_len, 1, &list, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
6180+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
6181+
}
6182+
6183+
term result = term_nil();
6184+
term *prev_term = NULL;
6185+
6186+
if (UNLIKELY(temp_stack_push(&temp_stack, list) != TempStackOk)) {
6187+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
6188+
}
6189+
while (!temp_stack_is_empty(&temp_stack)) {
6190+
term t = temp_stack_pop(&temp_stack);
6191+
6192+
if (term_is_nonempty_list(t)) {
6193+
term t_head = term_get_list_head(t);
6194+
term t_tail = term_get_list_tail(t);
6195+
if (term_is_nonempty_list(t_tail)) {
6196+
if (UNLIKELY(temp_stack_push(&temp_stack, t_tail) != TempStackOk)) {
6197+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
6198+
}
6199+
}
6200+
if (!term_is_nil(t_head)) {
6201+
if (UNLIKELY(temp_stack_push(&temp_stack, t_head) != TempStackOk)) {
6202+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
6203+
}
6204+
}
6205+
} else {
6206+
term *new_list_item = term_list_alloc(&ctx->heap);
6207+
if (prev_term) {
6208+
prev_term[0] = term_list_from_list_ptr(new_list_item);
6209+
} else {
6210+
result = term_list_from_list_ptr(new_list_item);
6211+
}
6212+
prev_term = new_list_item;
6213+
new_list_item[1] = t;
6214+
}
6215+
}
6216+
6217+
if (prev_term) {
6218+
prev_term[0] = term_nil();
6219+
}
6220+
6221+
return result;
6222+
}
6223+
61286224
static term nif_lists_member(Context *ctx, int argc, term argv[])
61296225
{
61306226
UNUSED(argc)

src/libAtomVM/nifs.gperf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ base64:encode/1, &base64_encode_nif
189189
base64:decode/1, &base64_decode_nif
190190
base64:encode_to_string/1, &base64_encode_to_string_nif
191191
base64:decode_to_string/1, &base64_decode_to_string_nif
192+
lists:flatten/1, &lists_flatten_nif
192193
lists:keyfind/3, &lists_keyfind_nif
193194
lists:keymember/3, &lists_keymember_nif
194195
lists:member/2, &lists_member_nif

tests/libs/estdlib/test_lists.erl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ test_list_match() ->
236236

237237
test_flatten() ->
238238
?ASSERT_MATCH(lists:flatten([]), []),
239+
?ASSERT_MATCH(lists:flatten([[]]), []),
239240
?ASSERT_MATCH(lists:flatten([a]), [a]),
240241
?ASSERT_MATCH(lists:flatten([a, []]), [a]),
241242
?ASSERT_MATCH(lists:flatten([[[[[[[[a]]]]]]]]), [a]),

0 commit comments

Comments
 (0)