Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
245 changes: 138 additions & 107 deletions c_src/encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,33 @@ enc_string(Encoder* e, ERL_NIF_TERM val)
return 1;
}

static inline int
enc_json(Encoder* e, ERL_NIF_TERM val)
{
ErlNifBinary bin;
unsigned char* data;
size_t size;

if(!enif_is_binary(e->env, val)) {
return 0;
}
if(!enif_inspect_binary(e->env, val, &bin)) {
return 0;
}
data = bin.data;
size = bin.size;

if(!enc_ensure(e, size + 2)) {
return 0;
}

memcpy(e->p + e->i, data, size);
e->i += size;
e->count++;

return 1;
}

static inline int
enc_long(Encoder* e, ErlNifSInt64 val)
{
Expand Down Expand Up @@ -571,6 +598,91 @@ enc_map_to_ejson(ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM* out)
}
#endif

ERL_NIF_TERM
enc_object_element(Encoder* e, int first, ERL_NIF_TERM curr, ERL_NIF_TERM* stackp)
{
ErlNifEnv* env = e->env;
ERL_NIF_TERM stack = *stackp;
ERL_NIF_TERM item;
const ERL_NIF_TERM* tuple;
int arity;

if(first && !enc_start_object(e)) {
return enc_error(e, "internal_error");
}
if(enif_is_empty_list(env, curr)) {
if(!enc_end_object(e)) {
return enc_error(e, "internal_error");
}
return 0;
}
if(!enif_get_list_cell(env, curr, &item, &curr)) {
return enc_error(e, "internal_error");
}
if(!enif_get_tuple(env, item, &arity, &tuple)) {
return enc_obj_error(e, "invalid_object_member", item);
}
if(arity != 2) {
return enc_obj_error(e, "invalid_object_member_arity", item);
}
if(!first && !enc_comma(e)) {
return enc_error(e, "internal_error");
}
if(enif_compare(tuple[0], e->atoms->atom_partial_object) == 0) {
if(!enif_is_binary(env, tuple[1])) {
return enc_obj_error(e, "invalid_json_string", curr);
}
if(!enc_json(e, tuple[1])) {
return enc_error(e, "internal_error");
}
stack = enif_make_list_cell(env, curr, stack);
stack = enif_make_list_cell(env, e->atoms->ref_object, stack);
*stackp = stack;
} else {
if(!enc_string(e, tuple[0])) {
return enc_obj_error(e, "invalid_object_member_key", tuple[0]);
}
if(!enc_colon(e)) {
return enc_error(e, "internal_error");
}
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);
*stackp = stack;
}
return 0;
}

ERL_NIF_TERM
enc_array_element(Encoder* e, int first, ERL_NIF_TERM curr, ERL_NIF_TERM* stackp)
{
ErlNifEnv* env = e->env;
ERL_NIF_TERM stack = *stackp;
ERL_NIF_TERM item;

if(first && !enc_start_array(e)) {
return enc_error(e, "internal_error");
}
if(enif_is_empty_list(env, curr)) {
if(!enc_end_array(e)) {
return enc_error(e, "internal_error");
}
return 0;
}
if(!first && !enc_comma(e)) {
return enc_error(e, "internal_error");
}
if(!enif_get_list_cell(env, curr, &item, &curr)) {
return enc_error(e, "internal_error");
}
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);
*stackp = stack;
return 0;
}


ERL_NIF_TERM
encode_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
Expand Down Expand Up @@ -685,63 +797,15 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ret = enc_error(e, "internal_error");
goto done;
}
if(enif_is_empty_list(env, curr)) {
if(!enc_end_object(e)) {
ret = enc_error(e, "internal_error");
goto done;
}
continue;
}
if(!enif_get_list_cell(env, curr, &item, &curr)) {
ret = enc_error(e, "internal_error");
goto done;
}
if(!enif_get_tuple(env, item, &arity, &tuple)) {
ret = enc_obj_error(e, "invalid_object_member", item);
goto done;
}
if(arity != 2) {
ret = enc_obj_error(e, "invalid_object_member_arity", item);
goto done;
}
if(!enc_comma(e)) {
ret = enc_error(e, "internal_error");
goto done;
}
if(!enc_string(e, tuple[0])) {
ret = enc_obj_error(e, "invalid_object_member_key", tuple[0]);
goto done;
}
if(!enc_colon(e)) {
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);
ret = enc_object_element(e, 0, curr, &stack);
if(ret) { goto done; }
} 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;
}
if(enif_is_empty_list(env, curr)) {
if(!enc_end_array(e)) {
ret = enc_error(e, "internal_error");
goto done;
}
continue;
}
if(!enc_comma(e)) {
ret = enc_error(e, "internal_error");
goto done;
}
if(!enif_get_list_cell(env, curr, &item, &curr)) {
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);
ret = enc_array_element(e, 0, curr, &stack);
if(ret) { goto done; }
} else if(enif_compare(curr, e->atoms->atom_null) == 0) {
if(!enc_literal(e, "null", 4)) {
ret = enc_error(e, "null");
Expand Down Expand Up @@ -783,48 +847,31 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
goto done;
}
} else if(enif_get_tuple(env, curr, &arity, &tuple)) {
if(arity != 1) {
ret = enc_obj_error(e, "invalid_ejson", curr);
goto done;
}
if(!enif_is_list(env, tuple[0])) {
ret = enc_obj_error(e, "invalid_object", curr);
goto done;
}
if(!enc_start_object(e)) {
ret = enc_error(e, "internal_error");
goto done;
}
if(enif_is_empty_list(env, tuple[0])) {
if(!enc_end_object(e)) {
if(arity == 1) {
if(!enif_is_list(env, tuple[0])) {
ret = enc_obj_error(e, "invalid_object", curr);
goto done;
}
ret = enc_object_element(e, 1, tuple[0], &stack);
if (ret) { goto done; }
} else if(arity == 2) {
if((enif_compare(tuple[0], e->atoms->atom_json) != 0) &&
(enif_compare(tuple[0], e->atoms->atom_partial_array) != 0)) {
ret = enc_obj_error(e, "invalid_ejson", curr);
goto done;
}
if(!enif_is_binary(env, tuple[1])) {
ret = enc_obj_error(e, "invalid_json_string", curr);
goto done;
}
if(!enc_json(e, tuple[1])) {
ret = enc_error(e, "internal_error");
goto done;
}
continue;
}
if(!enif_get_list_cell(env, tuple[0], &item, &curr)) {
ret = enc_error(e, "internal_error");
goto done;
}
if(!enif_get_tuple(env, item, &arity, &tuple)) {
ret = enc_obj_error(e, "invalid_object_member", item);
goto done;
}
if(arity != 2) {
ret = enc_obj_error(e, "invalid_object_member_arity", item);
goto done;
}
if(!enc_string(e, tuple[0])) {
ret = enc_obj_error(e, "invalid_object_member_key", tuple[0]);
goto done;
}
if(!enc_colon(e)) {
ret = enc_error(e, "internal_error");
} else {
ret = enc_obj_error(e, "invalid_ejson", curr);
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);
#if MAP_TYPE_PRESENT
} else if(enif_is_map(env, curr)) {
if(!enc_map_to_ejson(env, curr, &curr)) {
Expand All @@ -834,24 +881,8 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
stack = enif_make_list_cell(env, curr, stack);
#endif
} else if(enif_is_list(env, curr)) {
if(!enc_start_array(e)) {
ret = enc_error(e, "internal_error");
goto done;
}
if(enif_is_empty_list(env, curr)) {
if(!enc_end_array(e)) {
ret = enc_error(e, "internal_error");
goto done;
}
continue;
}
if(!enif_get_list_cell(env, curr, &item, &curr)) {
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);
ret = enc_array_element(e, 1, curr, &stack);
if(ret) { goto done; }
} else {
if(!enc_unknown(e, curr)) {
ret = enc_error(e, "internal_error");
Expand Down
3 changes: 3 additions & 0 deletions c_src/jiffy.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
st->atom_null = make_atom(env, "null");
st->atom_true = make_atom(env, "true");
st->atom_false = make_atom(env, "false");
st->atom_json = make_atom(env, "json");
st->atom_partial_object = make_atom(env, "$partial_object$");
st->atom_partial_array = make_atom(env, "$partial_array$");
st->atom_bignum = make_atom(env, "bignum");
st->atom_bignum_e = make_atom(env, "bignum_e");
st->atom_bigdbl = make_atom(env, "bigdbl");
Expand Down
3 changes: 3 additions & 0 deletions c_src/jiffy.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ typedef struct {
ERL_NIF_TERM atom_null;
ERL_NIF_TERM atom_true;
ERL_NIF_TERM atom_false;
ERL_NIF_TERM atom_json;
ERL_NIF_TERM atom_partial_object;
ERL_NIF_TERM atom_partial_array;
ERL_NIF_TERM atom_bignum;
ERL_NIF_TERM atom_bignum_e;
ERL_NIF_TERM atom_bigdbl;
Expand Down
17 changes: 15 additions & 2 deletions src/jiffy.erl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
% See the LICENSE file for more information.

-module(jiffy).
-export([decode/1, decode/2, encode/1, encode/2]).
-export([decode/1, decode/2, encode/1, encode/2, partial_encode/2]).
-define(NOT_LOADED, not_loaded(?LINE)).

-compile([no_native]).
Expand All @@ -16,7 +16,8 @@
| json_string()
| json_number()
| json_object()
| json_array().
| json_array()
| json_preencoded().

-type json_array() :: [json_value()].
-type json_string() :: atom() | binary().
Expand All @@ -33,6 +34,8 @@

-endif.

-type json_preencoded() :: {json, Json::binary()}.

-type jiffy_decode_result() :: json_value()
| {has_trailer, json_value(), binary()}.

Expand Down Expand Up @@ -104,6 +107,16 @@ encode(Data, Options) ->
end.


-spec partial_encode(json_array(), encode_options()) -> {'$partial_array$', binary()};
(json_object(), encode_options()) -> {'$partial_object$', binary()}.
partial_encode(Data, Options) when is_list(Data) ->
Json = iolist_to_binary(encode(Data, Options)),
{'$partial_array$', binary_part(Json, 1, byte_size(Json) - 2)};
partial_encode(Data, Options) when is_tuple(Data) ->
Json = iolist_to_binary(encode(Data, Options)),
{'$partial_object$', binary_part(Json, 1, byte_size(Json) - 2)}.


finish_decode({bignum, Value}) ->
list_to_integer(binary_to_list(Value));
finish_decode({bignum_e, Value}) ->
Expand Down
Loading