Просмотр исходного кода

Add erlang map support

pull/53/head
jihyun 11 лет назад
Родитель
Сommit
03ea63c762
7 измененных файлов: 133 добавлений и 17 удалений
  1. +5
    -2
      Makefile
  2. +71
    -7
      c_src/decoder.c
  3. +25
    -0
      c_src/encoder.c
  4. +5
    -1
      c_src/jiffy.c
  5. +6
    -0
      c_src/jiffy.h
  6. +10
    -7
      src/jiffy.erl
  7. +11
    -0
      test/util.erl

+ 5
- 2
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

+ 71
- 7
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;

+ 25
- 0
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");

+ 5
- 1
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}
};

+ 6
- 0
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

+ 10
- 7
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) ->

+ 11
- 0
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,

Загрузка…
Отмена
Сохранить