|
58 | 58 | #include "smp.h" |
59 | 59 | #include "synclist.h" |
60 | 60 | #include "sys.h" |
| 61 | +#include "tempstack.h" |
61 | 62 | #include "term.h" |
62 | 63 | #include "term_typedef.h" |
63 | 64 | #include "unicode.h" |
@@ -213,6 +214,7 @@ static term nif_jit_backend_module(Context *ctx, int argc, term argv[]); |
213 | 214 | static term nif_jit_variant(Context *ctx, int argc, term argv[]); |
214 | 215 | #endif |
215 | 216 | static term nif_lists_reverse(Context *ctx, int argc, term argv[]); |
| 217 | +static term nif_lists_flatten(Context *ctx, int argc, term argv[]); |
216 | 218 | static term nif_lists_keyfind(Context *ctx, int argc, term argv[]); |
217 | 219 | static term nif_lists_keymember(Context *ctx, int argc, term argv[]); |
218 | 220 | static term nif_lists_member(Context *ctx, int argc, term argv[]); |
@@ -827,6 +829,10 @@ static const struct Nif erlang_lists_subtract_nif = { |
827 | 829 | .base.type = NIFFunctionType, |
828 | 830 | .nif_ptr = nif_erlang_lists_subtract |
829 | 831 | }; |
| 832 | +static const struct Nif lists_flatten_nif = { |
| 833 | + .base.type = NIFFunctionType, |
| 834 | + .nif_ptr = nif_lists_flatten |
| 835 | +}; |
830 | 836 | static const struct Nif lists_member_nif = { |
831 | 837 | .base.type = NIFFunctionType, |
832 | 838 | .nif_ptr = nif_lists_member |
@@ -6125,6 +6131,96 @@ static term nif_erlang_lists_subtract(Context *ctx, int argc, term argv[]) |
6125 | 6131 | return result; |
6126 | 6132 | } |
6127 | 6133 |
|
| 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 | + |
6128 | 6224 | static term nif_lists_member(Context *ctx, int argc, term argv[]) |
6129 | 6225 | { |
6130 | 6226 | UNUSED(argc) |
|
0 commit comments