From 03ea63c762c6a6d9b7f827130834eb43fe25eefa Mon Sep 17 00:00:00 2001 From: jihyun Date: Mon, 3 Feb 2014 00:04:10 +0900 Subject: [PATCH 1/2] Add erlang map support --- Makefile | 7 +++-- c_src/decoder.c | 78 ++++++++++++++++++++++++++++++++++++++++++++----- c_src/encoder.c | 25 ++++++++++++++++ c_src/jiffy.c | 6 +++- c_src/jiffy.h | 6 ++++ src/jiffy.erl | 17 ++++++----- test/util.erl | 11 +++++++ 7 files changed, 133 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index 7227ce4..bb2f305 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,5 @@ REBAR?=./rebar - all: build @@ -41,7 +40,11 @@ check: build etap eunit %.beam: %.erl - erlc -o test/ $< + rm -f .test_map + erl -eval '#{}, halt().' -noshell || touch .test_map + @if test ! -d .test_map; then \ + erlc -DTEST_MAP -o test/ $<; \ + fi .PHONY: all clean distclean depends build etap eunit check diff --git a/c_src/decoder.c b/c_src/decoder.c index fc892da..1757a18 100644 --- a/c_src/decoder.c +++ b/c_src/decoder.c @@ -59,10 +59,11 @@ typedef struct { char* st_data; int st_size; int st_top; + int to_map; } Decoder; -void -dec_init(Decoder* d, ErlNifEnv* env, ERL_NIF_TERM arg, ErlNifBinary* bin) +int +dec_init(Decoder* d, ErlNifEnv* env, ERL_NIF_TERM arg, ERL_NIF_TERM opts, ErlNifBinary* bin) { int i; @@ -80,6 +81,7 @@ dec_init(Decoder* d, ErlNifEnv* env, ERL_NIF_TERM arg, ErlNifBinary* bin) d->st_data = (char*) enif_alloc(STACK_SIZE_INC * sizeof(char)); d->st_size = STACK_SIZE_INC; d->st_top = 0; + d->to_map = 0; for(i = 0; i < d->st_size; i++) { d->st_data[i] = st_invalid; @@ -87,6 +89,18 @@ dec_init(Decoder* d, ErlNifEnv* env, ERL_NIF_TERM arg, ErlNifBinary* bin) d->st_data[0] = st_value; d->st_top++; + +#if MAP_SUPPORT + ERL_NIF_TERM val; + while(enif_get_list_cell(env, opts, &val, &opts)) { + if(enif_compare(val, d->atoms->atom_map) == 0) { + d->to_map = 1; + } else { + return 0; + } + } +#endif + return 1; } void @@ -574,9 +588,50 @@ parse: return 1; } +#if MAP_SUPPORT +ERL_NIF_TERM +make_object_map(ErlNifEnv* env, ERL_NIF_TERM pairs) +{ + ERL_NIF_TERM ret = enif_make_new_map(env); + ERL_NIF_TERM key, val; + + while(enif_get_list_cell(env, pairs, &val, &pairs)) { + if(!enif_get_list_cell(env, pairs, &key, &pairs)) { + assert(0 == 1 && "Unbalanced object pairs."); + } + enif_make_map_put(env, ret, key, val, &ret); + } + + return ret; +} +ERL_NIF_TERM +make_empty_object_map(ErlNifEnv* env) { + return enif_make_new_map(env); +} +#endif + +ERL_NIF_TERM +make_empty_object(Decoder* d) { + ErlNifEnv* env = d->env; +#if MAP_SUPPORT + if(d->to_map) { + return make_empty_object_map(env); + } +#endif + + return enif_make_tuple1(env, enif_make_list(env, 0)); +} + ERL_NIF_TERM -make_object(ErlNifEnv* env, ERL_NIF_TERM pairs) +make_object(Decoder* d, ERL_NIF_TERM pairs) { + ErlNifEnv* env = d->env; +#if MAP_SUPPORT + if(d->to_map) { + return make_object_map(env, pairs); + } +#endif + ERL_NIF_TERM ret = enif_make_list(env, 0); ERL_NIF_TERM key, val; @@ -617,13 +672,22 @@ decode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ERL_NIF_TERM val; ERL_NIF_TERM ret; - if(argc != 1) { + if(argc != 2) { return enif_make_badarg(env); } else if(!enif_inspect_binary(env, argv[0], &bin)) { return enif_make_badarg(env); } - dec_init(d, env, argv[0], &bin); + if(!dec_init(d, env, argv[0], argv[1], &bin)) { + return enif_make_badarg(env); + } + +#if !MAP_SUPPORT + if(d->to_map) { + ret = dec_error(d, "map_unavailable"); + goto done; + } +#endif //fprintf(stderr, "Parsing:\r\n"); while(d->i < bin.size) { @@ -770,7 +834,7 @@ decode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) dec_pop(d, st_key); dec_pop(d, st_object); dec_pop(d, st_value); - val = enif_make_tuple1(env, curr); + val = make_empty_object(d); if(!enif_get_list_cell(env, objs, &curr, &objs)) { ret = dec_error(d, "internal_error"); goto done; @@ -839,7 +903,7 @@ decode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) } dec_pop(d, st_object); dec_pop(d, st_value); - val = make_object(env, curr); + val = make_object(d, curr); if(!enif_get_list_cell(env, objs, &curr, &objs)) { ret = dec_error(d, "internal_error"); goto done; diff --git a/c_src/encoder.c b/c_src/encoder.c index c23b309..e748e59 100644 --- a/c_src/encoder.c +++ b/c_src/encoder.c @@ -623,6 +623,7 @@ encode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) goto done; } } else if(enif_get_tuple(env, curr, &arity, &tuple)) { + // Old-fasioned object definition: {[{Key1, Val1}, {Key2, Val2}]} if(arity != 1) { ret = enc_error(e, "invalid_ejson"); goto done; @@ -665,6 +666,30 @@ encode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 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); +#if MAP_SUPPORT + } else if(enif_is_map(env, curr)) { + ErlNifMapIterator iter; + if(!enif_map_iterator_create(env, curr, &iter, ERL_NIF_MAP_ITERATOR_HEAD)) { + ret = enc_error(e, "internal_error"); + goto done; + } + + curr = enif_make_list(env, 0); + ERL_NIF_TERM key, val, tup; + + while(!enif_map_iterator_is_tail(env, &iter)) { + if(!enif_map_iterator_get_pair(env, &iter, &key, &val)) { + ret = enc_error(e, "internal_error"); + goto done; + } + tup = enif_make_tuple2(env, key, val); + curr = enif_make_list_cell(env, tup, curr); + + enif_map_iterator_next(env, &iter); + } + + stack = enif_make_list_cell(env, enif_make_tuple1(env, curr), stack); +#endif } else if(enif_is_list(env, curr)) { if(!enc_start_array(e)) { ret = enc_error(e, "internal_error"); diff --git a/c_src/jiffy.c b/c_src/jiffy.c index 964222c..284049d 100644 --- a/c_src/jiffy.c +++ b/c_src/jiffy.c @@ -28,6 +28,10 @@ load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info) st->ref_object = make_atom(env, "$object_ref$"); st->ref_array = make_atom(env, "$array_ref$"); +#if MAP_SUPPORT + st->atom_map = make_atom(env, "map"); +#endif + *priv = (void*) st; return 0; @@ -54,7 +58,7 @@ unload(ErlNifEnv* env, void* priv) static ErlNifFunc funcs[] = { - {"nif_decode", 1, decode}, + {"nif_decode", 2, decode}, {"nif_encode", 2, encode} }; diff --git a/c_src/jiffy.h b/c_src/jiffy.h index 3dda545..d86cc67 100644 --- a/c_src/jiffy.h +++ b/c_src/jiffy.h @@ -6,6 +6,8 @@ #include "erl_nif.h" +#define MAP_SUPPORT ((ERL_NIF_MAJOR_VERSION == 2 && ERL_NIF_MINOR_VERSION >= 6) || (ERL_NIF_MAJOR_VERSION > 2)) + typedef struct { ERL_NIF_TERM atom_ok; ERL_NIF_TERM atom_error; @@ -19,6 +21,9 @@ typedef struct { ERL_NIF_TERM atom_uescape; ERL_NIF_TERM atom_pretty; ERL_NIF_TERM atom_force_utf8; +#ifdef MAP_SUPPORT + ERL_NIF_TERM atom_map; +#endif ERL_NIF_TERM ref_object; ERL_NIF_TERM ref_array; @@ -42,4 +47,5 @@ int unicode_from_pair(int hi, int lo); int unicode_uescape(int c, char* buf); int double_to_shortest(char *buf, size_t size, size_t* len, double val); + #endif // Included JIFFY_H diff --git a/src/jiffy.erl b/src/jiffy.erl index c4b3d69..e4458f6 100644 --- a/src/jiffy.erl +++ b/src/jiffy.erl @@ -2,22 +2,25 @@ % See the LICENSE file for more information. -module(jiffy). --export([decode/1, encode/1, encode/2]). +-export([decode/1, decode/2, encode/1, encode/2]). -define(NOT_LOADED, not_loaded(?LINE)). -on_load(init/0). -decode(Data) when is_binary(Data) -> - case nif_decode(Data) of +decode(Data) -> + decode(Data, []). + +decode(Data, Options) when is_list(Data) -> + decode(iolist_to_binary(Data), Options); +decode(Data, Options) when is_binary(Data) -> + case nif_decode(Data, Options) of {error, _} = Error -> throw(Error); {partial, EJson} -> finish_decode(EJson); EJson -> EJson - end; -decode(Data) when is_list(Data) -> - decode(iolist_to_binary(Data)). + end. encode(Data) -> @@ -99,7 +102,7 @@ init() -> not_loaded(Line) -> erlang:nif_error({not_loaded, [{module, ?MODULE}, {line, Line}]}). -nif_decode(_Data) -> +nif_decode(_Data, _Options) -> ?NOT_LOADED. nif_encode(_Data, _Options) -> diff --git a/test/util.erl b/test/util.erl index ceaef89..e5826d7 100644 --- a/test/util.erl +++ b/test/util.erl @@ -27,8 +27,19 @@ check_good({J, E}, Options) -> etap:is(do_encode(E, Options), J, ok_enc(E, J)); check_good({J, E, J2}, Options) -> etap:is(jiffy:decode(J), E, ok_dec(J, E)), + check_map({J, J2}, Options), etap:is(do_encode(E, Options), J2, ok_enc(E, J2)). +-ifdef(TEST_MAP). +check_map({J, J2}, Options) -> + E2 = jiffy:decode(J, [map]), + % etap function breaks all tests because of etap:plan, so stop + % if map test failed + J2 = do_encode(E2, Options). +-else. +check_map(_, _) -> ok. +-endif. + check_error({J, E}) -> etap:fun_is( fun({error, E1}) when E1 == E -> true; (E1) -> E1 end, From 0d0cd252406a5624394828b22b090442f74d8f5f Mon Sep 17 00:00:00 2001 From: jihyun Date: Sun, 9 Feb 2014 11:07:41 +0900 Subject: [PATCH 2/2] Clean up codes - Remove #if macros when possible - Remove shell scripts on Makefile --- Makefile | 7 +------ c_src/decoder.c | 31 ++++++++++++++++++------------- c_src/jiffy.c | 5 +---- c_src/jiffy.h | 3 +-- c_src/util.c | 9 +++++++++ test/util.erl | 16 ++++++++-------- 6 files changed, 38 insertions(+), 33 deletions(-) diff --git a/Makefile b/Makefile index bb2f305..831deb5 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,6 @@ REBAR?=./rebar all: build - clean: $(REBAR) clean rm -rf logs @@ -40,11 +39,7 @@ check: build etap eunit %.beam: %.erl - rm -f .test_map - erl -eval '#{}, halt().' -noshell || touch .test_map - @if test ! -d .test_map; then \ - erlc -DTEST_MAP -o test/ $<; \ - fi + erlc -DTEST_MAP -o test/ $< .PHONY: all clean distclean depends build etap eunit check diff --git a/c_src/decoder.c b/c_src/decoder.c index 1757a18..58bb0ae 100644 --- a/c_src/decoder.c +++ b/c_src/decoder.c @@ -90,7 +90,6 @@ dec_init(Decoder* d, ErlNifEnv* env, ERL_NIF_TERM arg, ERL_NIF_TERM opts, ErlNif d->st_data[0] = st_value; d->st_top++; -#if MAP_SUPPORT ERL_NIF_TERM val; while(enif_get_list_cell(env, opts, &val, &opts)) { if(enif_compare(val, d->atoms->atom_map) == 0) { @@ -99,7 +98,6 @@ dec_init(Decoder* d, ErlNifEnv* env, ERL_NIF_TERM arg, ERL_NIF_TERM opts, ErlNif return 0; } } -#endif return 1; } @@ -588,36 +586,47 @@ parse: return 1; } -#if MAP_SUPPORT ERL_NIF_TERM make_object_map(ErlNifEnv* env, ERL_NIF_TERM pairs) { - ERL_NIF_TERM ret = enif_make_new_map(env); + ERL_NIF_TERM ret; ERL_NIF_TERM key, val; +#if MAP_SUPPORT + ret = enif_make_new_map(env); + while(enif_get_list_cell(env, pairs, &val, &pairs)) { if(!enif_get_list_cell(env, pairs, &key, &pairs)) { assert(0 == 1 && "Unbalanced object pairs."); } enif_make_map_put(env, ret, key, val, &ret); } +#else + assert(0 == 1 && "maps not supported"); +#endif return ret; } + + ERL_NIF_TERM make_empty_object_map(ErlNifEnv* env) { - return enif_make_new_map(env); -} + ERL_NIF_TERM ret; +#if MAP_SUPPORT + ret = enif_make_new_map(env); +#else + assert(0 == 1 && "maps not supported"); #endif + return ret; +} + ERL_NIF_TERM make_empty_object(Decoder* d) { ErlNifEnv* env = d->env; -#if MAP_SUPPORT if(d->to_map) { return make_empty_object_map(env); } -#endif return enif_make_tuple1(env, enif_make_list(env, 0)); } @@ -626,11 +635,9 @@ ERL_NIF_TERM make_object(Decoder* d, ERL_NIF_TERM pairs) { ErlNifEnv* env = d->env; -#if MAP_SUPPORT if(d->to_map) { return make_object_map(env, pairs); } -#endif ERL_NIF_TERM ret = enif_make_list(env, 0); ERL_NIF_TERM key, val; @@ -682,12 +689,10 @@ decode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return enif_make_badarg(env); } -#if !MAP_SUPPORT - if(d->to_map) { + if(d->to_map && !maps_enabled()) { ret = dec_error(d, "map_unavailable"); goto done; } -#endif //fprintf(stderr, "Parsing:\r\n"); while(d->i < bin.size) { diff --git a/c_src/jiffy.c b/c_src/jiffy.c index 284049d..0f106c3 100644 --- a/c_src/jiffy.c +++ b/c_src/jiffy.c @@ -23,15 +23,12 @@ load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info) st->atom_uescape = make_atom(env, "uescape"); st->atom_pretty = make_atom(env, "pretty"); st->atom_force_utf8 = make_atom(env, "force_utf8"); + st->atom_map = make_atom(env, "map"); // Markers used in encoding st->ref_object = make_atom(env, "$object_ref$"); st->ref_array = make_atom(env, "$array_ref$"); -#if MAP_SUPPORT - st->atom_map = make_atom(env, "map"); -#endif - *priv = (void*) st; return 0; diff --git a/c_src/jiffy.h b/c_src/jiffy.h index d86cc67..f609aa3 100644 --- a/c_src/jiffy.h +++ b/c_src/jiffy.h @@ -21,9 +21,7 @@ typedef struct { ERL_NIF_TERM atom_uescape; ERL_NIF_TERM atom_pretty; ERL_NIF_TERM atom_force_utf8; -#ifdef MAP_SUPPORT ERL_NIF_TERM atom_map; -#endif ERL_NIF_TERM ref_object; ERL_NIF_TERM ref_array; @@ -32,6 +30,7 @@ typedef struct { ERL_NIF_TERM make_atom(ErlNifEnv* env, const char* name); ERL_NIF_TERM make_ok(jiffy_st* st, ErlNifEnv* env, ERL_NIF_TERM data); ERL_NIF_TERM make_error(jiffy_st* st, ErlNifEnv* env, const char* error); +int maps_enabled(void); ERL_NIF_TERM decode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM encode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); diff --git a/c_src/util.c b/c_src/util.c index f1be3ec..460f332 100644 --- a/c_src/util.c +++ b/c_src/util.c @@ -24,3 +24,12 @@ make_error(jiffy_st* st, ErlNifEnv* env, const char* error) { return enif_make_tuple2(env, st->atom_error, make_atom(env, error)); } + +int +maps_enabled(void) { +#if MAP_SUPPORT + return 1; +#else + return 0; +#endif +} diff --git a/test/util.erl b/test/util.erl index e5826d7..6f35810 100644 --- a/test/util.erl +++ b/test/util.erl @@ -30,15 +30,15 @@ check_good({J, E, J2}, Options) -> check_map({J, J2}, Options), etap:is(do_encode(E, Options), J2, ok_enc(E, J2)). --ifdef(TEST_MAP). check_map({J, J2}, Options) -> - E2 = jiffy:decode(J, [map]), - % etap function breaks all tests because of etap:plan, so stop - % if map test failed - J2 = do_encode(E2, Options). --else. -check_map(_, _) -> ok. --endif. + try jiffy:decode(J, [map]) of + E2 -> + % etap function breaks all tests because of etap:plan, so stop + % if map test failed + J2 = do_encode(E2, Options) + catch throw:{error, {1, map_unavailable}} -> + ok + end. check_error({J, E}) -> etap:fun_is(