Browse Source

Initial support for the new map type

This patch adds initial support for decoding/encoding to/from the new
maps data type.

I'd like to thank Jihyun Yu (yjh0502) for the initial versions of this
work.
pull/65/head
Paul J. Davis 11 years ago
parent
commit
b96de951a2
9 changed files with 160 additions and 11 deletions
  1. +48
    -6
      c_src/decoder.c
  2. +51
    -0
      c_src/encoder.c
  3. +1
    -0
      c_src/jiffy.c
  4. +5
    -0
      c_src/jiffy.h
  5. +3
    -0
      rebar.config
  6. +9
    -5
      rebar.config.script
  7. +13
    -0
      src/jiffy.erl
  8. +12
    -0
      src/jiffy_utf8.erl
  9. +18
    -0
      test/jiffy_tests.erl

+ 48
- 6
c_src/decoder.c View File

@ -51,6 +51,7 @@ typedef struct {
size_t bytes_per_iter; size_t bytes_per_iter;
int is_partial; int is_partial;
int return_maps;
char* p; char* p;
unsigned char* u; unsigned char* u;
@ -77,6 +78,7 @@ dec_new(ErlNifEnv* env)
d->bytes_per_iter = DEFAULT_BYTES_PER_ITER; d->bytes_per_iter = DEFAULT_BYTES_PER_ITER;
d->is_partial = 0; d->is_partial = 0;
d->return_maps = 0;
d->p = NULL; d->p = NULL;
d->u = NULL; d->u = NULL;
@ -606,11 +608,41 @@ parse:
} }
ERL_NIF_TERM ERL_NIF_TERM
make_object(ErlNifEnv* env, ERL_NIF_TERM pairs)
make_empty_object(ErlNifEnv* env, int ret_map)
{ {
ERL_NIF_TERM ret = enif_make_list(env, 0);
ERL_NIF_TERM key, val;
#if MAP_TYPE_PRESENT
if(ret_map) {
return enif_make_new_map(env);
}
#endif
return enif_make_tuple1(env, enif_make_list(env, 0));
}
int
make_object(ErlNifEnv* env, ERL_NIF_TERM pairs, ERL_NIF_TERM* out, int ret_map)
{
ERL_NIF_TERM ret;
ERL_NIF_TERM key;
ERL_NIF_TERM val;
#if MAP_TYPE_PRESENT
if(ret_map) {
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.");
}
if(!enif_make_map_put(env, ret, key, val, &ret)) {
return 0;
}
}
*out = ret;
return 1;
}
#endif
ret = enif_make_list(env, 0);
while(enif_get_list_cell(env, pairs, &val, &pairs)) { while(enif_get_list_cell(env, pairs, &val, &pairs)) {
if(!enif_get_list_cell(env, pairs, &key, &pairs)) { if(!enif_get_list_cell(env, pairs, &key, &pairs)) {
assert(0 == 1 && "Unbalanced object pairs."); assert(0 == 1 && "Unbalanced object pairs.");
@ -618,8 +650,9 @@ make_object(ErlNifEnv* env, ERL_NIF_TERM pairs)
val = enif_make_tuple2(env, key, val); val = enif_make_tuple2(env, key, val);
ret = enif_make_list_cell(env, val, ret); ret = enif_make_list_cell(env, val, ret);
} }
*out = enif_make_tuple1(env, ret);
return enif_make_tuple1(env, ret);
return 1;
} }
ERL_NIF_TERM ERL_NIF_TERM
@ -668,6 +701,12 @@ decode_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
while(enif_get_list_cell(env, opts, &val, &opts)) { while(enif_get_list_cell(env, opts, &val, &opts)) {
if(get_bytes_per_iter(env, val, &(d->bytes_per_iter))) { if(get_bytes_per_iter(env, val, &(d->bytes_per_iter))) {
continue; continue;
} else if(enif_compare(val, d->atoms->atom_return_maps) == 0) {
#if MAP_TYPE_PRESENT
d->return_maps = 1;
#else
return enif_make_badarg(env);
#endif
} else { } else {
return enif_make_badarg(env); return enif_make_badarg(env);
} }
@ -862,7 +901,7 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
dec_pop(d, st_key); dec_pop(d, st_key);
dec_pop(d, st_object); dec_pop(d, st_object);
dec_pop(d, st_value); dec_pop(d, st_value);
val = enif_make_tuple1(env, curr);
val = make_empty_object(env, d->return_maps);
if(!enif_get_list_cell(env, objs, &curr, &objs)) { if(!enif_get_list_cell(env, objs, &curr, &objs)) {
ret = dec_error(d, "internal_error"); ret = dec_error(d, "internal_error");
goto done; goto done;
@ -931,7 +970,10 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
} }
dec_pop(d, st_object); dec_pop(d, st_object);
dec_pop(d, st_value); dec_pop(d, st_value);
val = make_object(env, curr);
if(!make_object(env, curr, &val, d->return_maps)) {
ret = dec_error(d, "internal_object_error");
goto done;
}
if(!enif_get_list_cell(env, objs, &curr, &objs)) { if(!enif_get_list_cell(env, objs, &curr, &objs)) {
ret = dec_error(d, "internal_error"); ret = dec_error(d, "internal_error");
goto done; goto done;

+ 51
- 0
c_src/encoder.c View File

@ -500,6 +500,49 @@ enc_comma(Encoder* e)
return 1; return 1;
} }
#if MAP_TYPE_PRESENT
int
enc_map_to_ejson(ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM* out)
{
ErlNifMapIterator iter;
size_t size;
ERL_NIF_TERM list;
ERL_NIF_TERM tuple;
ERL_NIF_TERM key;
ERL_NIF_TERM val;
if(!enif_get_map_size(env, map, &size)) {
fprintf(stderr, "bad map size\r\n");
return 0;
}
list = enif_make_list(env, 0);
if(size == 0) {
*out = enif_make_tuple1(env, list);
return 1;
}
if(!enif_map_iterator_create(env, map, &iter, ERL_NIF_MAP_ITERATOR_HEAD)) {
fprintf(stderr, "bad iterator create\r\n");
return 0;
}
do {
if(!enif_map_iterator_get_pair(env, &iter, &key, &val)) {
fprintf(stderr, "bad get pair\r\n");
return 0;
}
tuple = enif_make_tuple2(env, key, val);
list = enif_make_list_cell(env, tuple, list);
} while(enif_map_iterator_next(env, &iter));
*out = enif_make_tuple1(env, list);
return 1;
}
#endif
ERL_NIF_TERM ERL_NIF_TERM
encode_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) encode_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{ {
@ -743,6 +786,14 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
stack = enif_make_list_cell(env, curr, stack); 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, e->atoms->ref_object, stack);
stack = enif_make_list_cell(env, tuple[1], stack); stack = enif_make_list_cell(env, tuple[1], stack);
#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);
#endif
} else if(enif_is_list(env, curr)) { } else if(enif_is_list(env, curr)) {
if(!enc_start_array(e)) { if(!enc_start_array(e)) {
ret = enc_error(e, "internal_error"); ret = enc_error(e, "internal_error");

+ 1
- 0
c_src/jiffy.c View File

@ -25,6 +25,7 @@ load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
st->atom_force_utf8 = make_atom(env, "force_utf8"); st->atom_force_utf8 = make_atom(env, "force_utf8");
st->atom_iter = make_atom(env, "iter"); st->atom_iter = make_atom(env, "iter");
st->atom_bytes_per_iter = make_atom(env, "bytes_per_iter"); st->atom_bytes_per_iter = make_atom(env, "bytes_per_iter");
st->atom_return_maps = make_atom(env, "return_maps");
// Markers used in encoding // Markers used in encoding
st->ref_object = make_atom(env, "$object_ref$"); st->ref_object = make_atom(env, "$object_ref$");

+ 5
- 0
c_src/jiffy.h View File

@ -8,6 +8,10 @@
#define DEFAULT_BYTES_PER_ITER 2048 #define DEFAULT_BYTES_PER_ITER 2048
#define MAP_TYPE_PRESENT \
((ERL_NIF_MAJOR_VERSION == 2 && ERL_NIF_MINOR_VERSION >= 6) \
|| (ERL_NIF_MAJOR_VERSION > 2))
typedef struct { typedef struct {
ERL_NIF_TERM atom_ok; ERL_NIF_TERM atom_ok;
ERL_NIF_TERM atom_error; ERL_NIF_TERM atom_error;
@ -23,6 +27,7 @@ typedef struct {
ERL_NIF_TERM atom_force_utf8; ERL_NIF_TERM atom_force_utf8;
ERL_NIF_TERM atom_iter; ERL_NIF_TERM atom_iter;
ERL_NIF_TERM atom_bytes_per_iter; ERL_NIF_TERM atom_bytes_per_iter;
ERL_NIF_TERM atom_return_maps;
ERL_NIF_TERM ref_object; ERL_NIF_TERM ref_object;
ERL_NIF_TERM ref_array; ERL_NIF_TERM ref_array;

+ 3
- 0
rebar.config View File

@ -25,6 +25,9 @@
{"win32", "CXXFLAGS", "-g -Wall -O3"} {"win32", "CXXFLAGS", "-g -Wall -O3"}
]}. ]}.
{erl_opts, [
{platform_define, "R1(1|2|3|4|5|6)", 'JIFFY_NO_MAPS'}
]}.
{eunit_opts, [ {eunit_opts, [
verbose, verbose,

+ 9
- 5
rebar.config.script View File

@ -7,9 +7,7 @@
% %
% This script is based on the example provided with Rebar. % This script is based on the example provided with Rebar.
ErlOpts = {erl_opts, [
{d, 'JIFFY_DEV'}
]},
ErlOpts = [{d, 'JIFFY_DEV'}],
Proper = [ Proper = [
{proper, ".*", {git, "git://github.com/manopapad/proper.git", "master"}} {proper, ".*", {git, "git://github.com/manopapad/proper.git", "master"}}
@ -21,13 +19,19 @@ DevMarker = filename:join([ConfigPath, ".jiffy.dev"]),
case filelib:is_file(DevMarker) of case filelib:is_file(DevMarker) of
true -> true ->
% Don't override existing dependencies % Don't override existing dependencies
NewConfig = case lists:keyfind(deps, 1, CONFIG) of
Config0 = case lists:keyfind(deps, 1, CONFIG) of
false -> false ->
CONFIG ++ [{deps, Proper}]; CONFIG ++ [{deps, Proper}];
{deps, DepsList} -> {deps, DepsList} ->
lists:keyreplace(deps, 1, CONFIG, {deps, DepsList ++ Proper}) lists:keyreplace(deps, 1, CONFIG, {deps, DepsList ++ Proper})
end, end,
NewConfig ++ [ErlOpts];
Config1 = case lists:keyfind(erl_opts, 1, Config0) of
false ->
Config0 ++ [{erl_opts, ErlOpts}];
{erl_opts, Opts} ->
NewOpts = {erl_opts, Opts ++ ErlOpts},
lists:keyreplace(erl_opts, 1, Config0, NewOpts)
end;
false -> false ->
CONFIG CONFIG
end. end.

+ 13
- 0
src/jiffy.erl View File

@ -67,7 +67,20 @@ finish_decode({Pairs}) when is_list(Pairs) ->
finish_decode(Vals) when is_list(Vals) -> finish_decode(Vals) when is_list(Vals) ->
finish_decode_arr(Vals, []); finish_decode_arr(Vals, []);
finish_decode(Val) -> finish_decode(Val) ->
maybe_map(Val).
-ifndef(JIFFY_NO_MAPS).
maybe_map(Obj) when is_map(Obj) ->
maps:map(fun finish_decode_map/2, Obj);
maybe_map(Val) ->
Val.
finish_decode_map(_, V) ->
finish_decode(V).
-else.
maybe_map(Val) ->
Val. Val.
-endif.
finish_decode_obj([], Acc) -> finish_decode_obj([], Acc) ->
{lists:reverse(Acc)}; {lists:reverse(Acc)};

+ 12
- 0
src/jiffy_utf8.erl View File

@ -12,8 +12,20 @@ fix(Values) when is_list(Values) ->
fix(Bin) when is_binary(Bin) -> fix(Bin) when is_binary(Bin) ->
fix_bin(Bin); fix_bin(Bin);
fix(Val) -> fix(Val) ->
maybe_map(Val).
-ifndef(JIFFY_NO_MAPS).
maybe_map(Obj) when is_map(Obj) ->
maps:fold(fun fix_map/3, maps:new(), Obj);
maybe_map(Val) ->
Val. Val.
fix_map(K, V, Acc) ->
maps:put(fix(K), fix(V), Acc).
-else.
maybe_map(Val) ->
Val.
-endif.
fix_props([], Acc) -> fix_props([], Acc) ->
{lists:reverse(Acc)}; {lists:reverse(Acc)};

+ 18
- 0
test/jiffy_tests.erl View File

@ -26,6 +26,24 @@ prop_encode_decode() ->
end end
). ).
-ifndef(JIFFY_NO_MAPS).
to_map_ejson({Props}) ->
NewProps = [{K, to_map_ejson(V)} || {K, V} <- Props],
maps:from_list(NewProps);
to_map_ejson(Vals) when is_list(Vals) ->
[to_map_ejson(V) || V <- Vals];
to_map_ejson(Val) ->
Val.
prop_map_encode_decode() ->
?FORALL(Data, json(),
begin
MapData = to_map_ejson(Data),
MapData == jiffy:decode(jiffy:encode(MapData), [return_maps])
end
).
-endif.
prop_encode_decode_pretty() -> prop_encode_decode_pretty() ->
?FORALL(Data, json(), ?FORALL(Data, json(),
begin begin

Loading…
Cancel
Save