|
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 |
@@ -6129,6 +6135,119 @@ static term nif_erlang_lists_subtract(Context *ctx, int argc, term argv[]) |
6129 | 6135 | return result; |
6130 | 6136 | } |
6131 | 6137 |
|
| 6138 | +static term nif_lists_flatten(Context *ctx, int argc, term argv[]) |
| 6139 | +{ |
| 6140 | + UNUSED(argc) |
| 6141 | + |
| 6142 | + // Compute resulting list length, as well as the length of the reusable tail |
| 6143 | + size_t result_len = 0; |
| 6144 | + size_t tail_len = 0; |
| 6145 | + term list = argv[0]; |
| 6146 | + |
| 6147 | + if (term_is_nil(list)) { |
| 6148 | + return list; |
| 6149 | + } |
| 6150 | + |
| 6151 | + VALIDATE_VALUE(list, term_is_nonempty_list); |
| 6152 | + |
| 6153 | + struct TempStack temp_stack; |
| 6154 | + if (UNLIKELY(temp_stack_init(&temp_stack) != TempStackOk)) { |
| 6155 | + RAISE_ERROR(OUT_OF_MEMORY_ATOM); |
| 6156 | + } |
| 6157 | + if (UNLIKELY(temp_stack_push(&temp_stack, list) != TempStackOk)) { |
| 6158 | + RAISE_ERROR(OUT_OF_MEMORY_ATOM); |
| 6159 | + } |
| 6160 | + while (!temp_stack_is_empty(&temp_stack)) { |
| 6161 | + term t = temp_stack_pop(&temp_stack); |
| 6162 | + |
| 6163 | + tail_len = 0; |
| 6164 | + |
| 6165 | + do { |
| 6166 | + term t_head = term_get_list_head(t); |
| 6167 | + term t_tail = term_get_list_tail(t); |
| 6168 | + // Exit if t is not a proper list |
| 6169 | + if (!term_is_list(t_tail)) { |
| 6170 | + RAISE_ERROR(BADARG_ATOM); |
| 6171 | + } |
| 6172 | + |
| 6173 | + if (term_is_nonempty_list(t_head)) { |
| 6174 | + tail_len = 0; |
| 6175 | + if (term_is_nonempty_list(t_tail)) { |
| 6176 | + if (UNLIKELY(temp_stack_push(&temp_stack, t_tail) != TempStackOk)) { |
| 6177 | + RAISE_ERROR(OUT_OF_MEMORY_ATOM); |
| 6178 | + } |
| 6179 | + } |
| 6180 | + t = t_head; |
| 6181 | + } else { |
| 6182 | + if (term_is_nil(t_head)) { |
| 6183 | + tail_len = 0; |
| 6184 | + } else { |
| 6185 | + result_len++; |
| 6186 | + tail_len++; |
| 6187 | + } |
| 6188 | + t = t_tail; |
| 6189 | + } |
| 6190 | + } while (!term_is_nil(t)); |
| 6191 | + } |
| 6192 | + |
| 6193 | + // Allocate flattened list and build it. |
| 6194 | + if (result_len > tail_len) { |
| 6195 | + if (UNLIKELY(memory_ensure_free_with_roots(ctx, CONS_SIZE * (result_len - tail_len), 1, &list, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { |
| 6196 | + RAISE_ERROR(OUT_OF_MEMORY_ATOM); |
| 6197 | + } |
| 6198 | + } |
| 6199 | + |
| 6200 | + term result = term_nil(); |
| 6201 | + term *prev_term = NULL; |
| 6202 | + |
| 6203 | + if (UNLIKELY(temp_stack_push(&temp_stack, list) != TempStackOk)) { |
| 6204 | + RAISE_ERROR(OUT_OF_MEMORY_ATOM); |
| 6205 | + } |
| 6206 | + while (!temp_stack_is_empty(&temp_stack)) { |
| 6207 | + term t = temp_stack_pop(&temp_stack); |
| 6208 | + |
| 6209 | + do { |
| 6210 | + term t_head = term_get_list_head(t); |
| 6211 | + term t_tail = term_get_list_tail(t); |
| 6212 | + if (term_is_nonempty_list(t_head)) { |
| 6213 | + if (term_is_nonempty_list(t_tail)) { |
| 6214 | + if (UNLIKELY(temp_stack_push(&temp_stack, t_tail) != TempStackOk)) { |
| 6215 | + RAISE_ERROR(OUT_OF_MEMORY_ATOM); |
| 6216 | + } |
| 6217 | + } |
| 6218 | + t = t_head; |
| 6219 | + } else { |
| 6220 | + if (!term_is_nil(t_head)) { |
| 6221 | + // Append the tail of original list |
| 6222 | + if (result_len == tail_len) { |
| 6223 | + if (prev_term) { |
| 6224 | + prev_term[0] = t; |
| 6225 | + } else { |
| 6226 | + result = t; |
| 6227 | + } |
| 6228 | + break; |
| 6229 | + } |
| 6230 | + |
| 6231 | + term *new_list_item = term_list_alloc(&ctx->heap); |
| 6232 | + if (prev_term) { |
| 6233 | + prev_term[0] = term_list_from_list_ptr(new_list_item); |
| 6234 | + } else { |
| 6235 | + result = term_list_from_list_ptr(new_list_item); |
| 6236 | + } |
| 6237 | + prev_term = new_list_item; |
| 6238 | + new_list_item[0] = term_nil(); |
| 6239 | + new_list_item[1] = t_head; |
| 6240 | + |
| 6241 | + result_len--; |
| 6242 | + } |
| 6243 | + t = t_tail; |
| 6244 | + } |
| 6245 | + } while (!term_is_nil(t)); |
| 6246 | + } |
| 6247 | + |
| 6248 | + return result; |
| 6249 | +} |
| 6250 | + |
6132 | 6251 | static term nif_lists_member(Context *ctx, int argc, term argv[]) |
6133 | 6252 | { |
6134 | 6253 | UNUSED(argc) |
|
0 commit comments