From 4ab9a683133a1f22c72b88db51882cbee766bb03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Mon, 8 Apr 2019 12:57:16 +0200 Subject: [PATCH] Use an array for the position stack rather than an Erlang list --- c_src/encoder.c | 74 +++++++++++++++-------------- c_src/termstack.c | 87 ++++++++++++++++++++++++++++++++++ c_src/termstack.h | 28 +++++++++++ test/jiffy_05_array_tests.erl | 10 ++++ test/jiffy_06_object_tests.erl | 10 ++++ 5 files changed, 174 insertions(+), 35 deletions(-) create mode 100644 c_src/termstack.c create mode 100644 c_src/termstack.h diff --git a/c_src/encoder.c b/c_src/encoder.c index afc3c14..8f87c9b 100644 --- a/c_src/encoder.c +++ b/c_src/encoder.c @@ -5,8 +5,8 @@ #include #include -#include "erl_nif.h" #include "jiffy.h" +#include "termstack.h" #define BIN_INC_SIZE 2048 @@ -588,7 +588,7 @@ encode_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) } tmp_argv[0] = enif_make_resource(env, e); - tmp_argv[1] = enif_make_list(env, 1, argv[0]); + tmp_argv[1] = enif_make_tuple1(env, argv[0]); tmp_argv[2] = enif_make_list(env, 0); enif_release_resource(e); @@ -624,14 +624,15 @@ encode_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ERL_NIF_TERM encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - Encoder* e; jiffy_st* st = (jiffy_st*) enif_priv_data(env); + Encoder* e; + TermStack stack; ERL_NIF_TERM ret = 0; - ERL_NIF_TERM stack; ERL_NIF_TERM curr; ERL_NIF_TERM item; + ERL_NIF_TERM saved_stack; const ERL_NIF_TERM* tuple; int arity; ErlNifSInt64 lval; @@ -644,8 +645,6 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return enif_make_badarg(env); } else if(!enif_get_resource(env, argv[0], st->res_enc, (void**) &e)) { return enif_make_badarg(env); - } else if(!enif_is_list(env, argv[1])) { - return enif_make_badarg(env); } else if(!enif_is_list(env, argv[2])) { return enif_make_badarg(env); } @@ -654,34 +653,35 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return enif_make_badarg(env); } - stack = argv[1]; + if(!termstack_restore(env, argv[1], &stack)) { + return enif_make_badarg(env); + } + e->iolist = argv[2]; start = e->iosize + e->i; - while(!enif_is_empty_list(env, stack)) { - + while(!termstack_is_empty(&stack)) { bytes_written += (e->iosize + e->i) - start; if(should_yield(env, &bytes_written, e->bytes_per_red)) { + saved_stack = termstack_save(env, &stack); + termstack_destroy(&stack); + return enif_make_tuple4( env, st->atom_iter, argv[0], - stack, + saved_stack, e->iolist ); } - if(!enif_get_list_cell(env, stack, &curr, &stack)) { - ret = enc_error(e, "internal_error"); - goto done; - } + curr = termstack_pop(&stack); + if(enif_is_identical(curr, e->atoms->ref_object)) { - if(!enif_get_list_cell(env, stack, &curr, &stack)) { - ret = enc_error(e, "internal_error"); - goto done; - } + curr = termstack_pop(&stack); + if(enif_is_empty_list(env, curr)) { if(!enc_end_object(e)) { ret = enc_error(e, "internal_error"); @@ -713,14 +713,13 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ret = enc_error(e, "internal_error"); goto done; } - stack = enif_make_list_cell(env, curr, stack); - stack = enif_make_list_cell(env, e->atoms->ref_object, stack); - stack = enif_make_list_cell(env, tuple[1], stack); + + termstack_push(&stack, curr); + termstack_push(&stack, e->atoms->ref_object); + termstack_push(&stack, tuple[1]); } else if(enif_is_identical(curr, e->atoms->ref_array)) { - if(!enif_get_list_cell(env, stack, &curr, &stack)) { - ret = enc_error(e, "internal_error"); - goto done; - } + curr = termstack_pop(&stack); + if(enif_is_empty_list(env, curr)) { if(!enc_end_array(e)) { ret = enc_error(e, "internal_error"); @@ -736,9 +735,10 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ret = enc_error(e, "internal_error"); goto done; } - stack = enif_make_list_cell(env, curr, stack); - stack = enif_make_list_cell(env, e->atoms->ref_array, stack); - stack = enif_make_list_cell(env, item, stack); + + termstack_push(&stack, curr); + termstack_push(&stack, e->atoms->ref_array); + termstack_push(&stack, item); } else if(enif_compare(curr, e->atoms->atom_null) == 0) { if(!enc_literal(e, "null", 4)) { ret = enc_error(e, "null"); @@ -819,16 +819,18 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ret = enc_error(e, "internal_error"); goto done; } - stack = enif_make_list_cell(env, curr, stack); - stack = enif_make_list_cell(env, e->atoms->ref_object, stack); - stack = enif_make_list_cell(env, tuple[1], stack); + + termstack_push(&stack, curr); + termstack_push(&stack, e->atoms->ref_object); + termstack_push(&stack, tuple[1]); #if MAP_TYPE_PRESENT } else if(enif_is_map(env, curr)) { if(!enc_map_to_ejson(env, curr, &curr)) { ret = enc_error(e, "internal_error"); goto done; } - stack = enif_make_list_cell(env, curr, stack); + + termstack_push(&stack, curr); #endif } else if(enif_is_list(env, curr)) { if(!enc_start_array(e)) { @@ -846,9 +848,10 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ret = enc_error(e, "internal_error"); goto done; } - stack = enif_make_list_cell(env, curr, stack); - stack = enif_make_list_cell(env, e->atoms->ref_array, stack); - stack = enif_make_list_cell(env, item, stack); + + termstack_push(&stack, curr); + termstack_push(&stack, e->atoms->ref_array); + termstack_push(&stack, item); } else { if(!enc_unknown(e, curr)) { ret = enc_error(e, "internal_error"); @@ -869,6 +872,7 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) } done: + termstack_destroy(&stack); return ret; } diff --git a/c_src/termstack.c b/c_src/termstack.c new file mode 100644 index 0000000..a558b0a --- /dev/null +++ b/c_src/termstack.c @@ -0,0 +1,87 @@ +// This file is part of Jiffy released under the MIT license. +// See the LICENSE file for more information. + +#include +#include +#include + +#include "jiffy.h" +#include "termstack.h" + +ERL_NIF_TERM +termstack_save(ErlNifEnv* env, TermStack* stack) +{ + return enif_make_tuple_from_array(env, stack->elements, stack->top); +} + +int +termstack_restore(ErlNifEnv* env, ERL_NIF_TERM from, TermStack* stack) +{ + const ERL_NIF_TERM* elements; + int arity; + + if(enif_get_tuple(env, from, &arity, &elements)) { + stack->top = arity; + + if(arity <= SMALL_TERMSTACK_SIZE) { + stack->elements = &stack->__default_elements[0]; + stack->size = SMALL_TERMSTACK_SIZE; + } else { + stack->size = arity * 2; + stack->elements = enif_alloc(stack->size * sizeof(ERL_NIF_TERM)); + + if(!stack->elements) { + return 0; + } + } + + memcpy(stack->elements, elements, arity * sizeof(ERL_NIF_TERM)); + return 1; + } + + return 0; +} + +void +termstack_destroy(TermStack* stack) +{ + if(stack->elements != &stack->__default_elements[0]) { + enif_free(stack->elements); + } +} + +inline void +termstack_push(TermStack* stack, ERL_NIF_TERM term) +{ + + if(stack->top == stack->size) { + size_t new_size = stack->size * 2; + size_t num_bytes = new_size * sizeof(ERL_NIF_TERM); + + if (stack->elements == &stack->__default_elements[0]) { + ERL_NIF_TERM* elems = enif_alloc(num_bytes); + memcpy(elems, stack->elements, num_bytes); + stack->elements = elems; + } else { + stack->elements = enif_realloc(stack->elements, num_bytes); + } + + stack->size = new_size; + } + + assert(stack->top < stack->size); + stack->elements[stack->top++] = term; +} + +inline ERL_NIF_TERM +termstack_pop(TermStack* stack) +{ + assert(stack->top > 0 && stack->top <= stack->size); + return stack->elements[--stack->top]; +} + +inline int +termstack_is_empty(TermStack* stack) +{ + return stack->top == 0; +} diff --git a/c_src/termstack.h b/c_src/termstack.h new file mode 100644 index 0000000..ce46769 --- /dev/null +++ b/c_src/termstack.h @@ -0,0 +1,28 @@ +// This file is part of Jiffy released under the MIT license. +// See the LICENSE file for more information. + +#ifndef TERMSTACK_H +#define TERMSTACK_H + +#include "erl_nif.h" + +#define SMALL_TERMSTACK_SIZE 16 + +typedef struct { + ERL_NIF_TERM* elements; + size_t size; + size_t top; + + ERL_NIF_TERM __default_elements[SMALL_TERMSTACK_SIZE]; +} TermStack; + + +ERL_NIF_TERM termstack_save(ErlNifEnv* env, TermStack* stack); +int termstack_restore(ErlNifEnv* env, ERL_NIF_TERM from, TermStack* stack); +void termstack_destroy(TermStack* stack); + +void termstack_push(TermStack* stack, ERL_NIF_TERM term); +ERL_NIF_TERM termstack_pop(TermStack* stack); +int termstack_is_empty(TermStack* stack); + +#endif diff --git a/test/jiffy_05_array_tests.erl b/test/jiffy_05_array_tests.erl index 1fe4542..26e0a65 100644 --- a/test/jiffy_05_array_tests.erl +++ b/test/jiffy_05_array_tests.erl @@ -16,6 +16,16 @@ array_failure_test_() -> [gen(error, Case) || Case <- cases(error)]. +nested_array_test_() -> + Obj = nested(256), + Enc = enc(Obj), + ?_assertEqual(Obj, dec(Enc)). + + +nested(0) -> <<"bottom">>; +nested(N) -> [nested(N - 1)]. + + gen(ok, {J, E}) -> gen(ok, {J, E, J}); gen(ok, {J1, E, J2}) -> diff --git a/test/jiffy_06_object_tests.erl b/test/jiffy_06_object_tests.erl index 827184c..3cb72b0 100644 --- a/test/jiffy_06_object_tests.erl +++ b/test/jiffy_06_object_tests.erl @@ -16,6 +16,16 @@ object_failure_test_() -> [gen(error, Case) || Case <- cases(error)]. +nested_object_test_() -> + Obj = nested(256), + Enc = enc(Obj), + ?_assertEqual(Obj, dec(Enc)). + + +nested(0) -> <<"bottom">>; +nested(N) -> {[{integer_to_binary(N), nested(N - 1)}]}. + + gen(ok, {J, E}) -> gen(ok, {J, E, J}); gen(ok, {J1, E, J2}) ->