Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pre-encoded JSON support, take 2 #140

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
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
223 changes: 119 additions & 104 deletions c_src/encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,114 @@ 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");
}
next_object_elt:
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(enif_compare(tuple[0], e->atoms->atom_partial_object) == 0) {
ErlNifBinary bin;
if(!enif_inspect_binary(env, tuple[1], &bin)) {
return enc_error(e, "internal_error");
}
if(bin.size > 0) {
if(!first && !enc_comma(e)) {
return enc_error(e, "internal_error");
}
if(!enc_unknown(e, tuple[1])) {
return enc_error(e, "internal_error");
}
first = 0;
}
goto next_object_elt;
}
if(!first && !enc_comma(e)) {
return enc_error(e, "internal_error");
}
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;
const ERL_NIF_TERM* tuple;
int arity;
ErlNifBinary bin;

if(first && !enc_start_array(e)) {
return enc_error(e, "internal_error");
}
next_array_elt:
if(enif_is_empty_list(env, curr)) {
if(!enc_end_array(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) &&
(arity == 2) &&
(enif_compare(tuple[0], e->atoms->atom_partial_array) == 0) &&
enif_inspect_binary(env, tuple[1], &bin)) {
if (bin.size > 0) {
if(!first && !enc_comma(e)) {
return enc_error(e, "internal_error");
}
if(!enc_unknown(e, tuple[1])) {
return enc_error(e, "internal_error");
}
first = 0;
}
goto next_array_elt;
}
if(!first && !enc_comma(e)) {
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 @@ -682,63 +790,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 @@ -781,47 +841,18 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
}
} 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(!enc_unknown(e, curr)) {
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");
if(!enif_is_list(env, tuple[0])) {
ret = enc_obj_error(e, "invalid_object", 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);
ret = enc_object_element(e, 1, tuple[0], &stack);
if (ret) { goto done; }
#if MAP_TYPE_PRESENT
} else if(enif_is_map(env, curr)) {
if(!enc_map_to_ejson(env, curr, &curr)) {
Expand All @@ -831,24 +862,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
2 changes: 2 additions & 0 deletions c_src/jiffy.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ 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_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
2 changes: 2 additions & 0 deletions c_src/jiffy.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ typedef struct {
ERL_NIF_TERM atom_null;
ERL_NIF_TERM atom_true;
ERL_NIF_TERM atom_false;
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
28 changes: 23 additions & 5 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,23 +16,29 @@
| json_string()
| json_number()
| json_object()
| json_array().
| json_array()
| json_preencoded().

-type json_array() :: [json_value()].
-type json_array() :: [json_value()|json_partial_array()].
-type json_string() :: atom() | binary().
-type json_number() :: integer() | float().

-type json_partial_array() :: {'$partial_array$', iodata()}.
-type json_partial_object() :: {'$partial_object$', iodata()}.

-ifdef(JIFFY_NO_MAPS).

-type json_object() :: {[{json_string(),json_value()}]}.
-type json_object() :: {[({json_string(),json_value()})|json_partial_object()]}.

-else.

-type json_object() :: {[{json_string(),json_value()}]}
-type json_object() :: {[({json_string(),json_value()})|json_partial_object()]}
| #{json_string() => json_value()}.

-endif.

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

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

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


-spec partial_encode(json_array(), encode_options()) -> json_partial_array();
(json_object(), encode_options()) -> json_partial_object().
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 Expand Up @@ -160,6 +176,8 @@ finish_encode([<<_/binary>>=B | Rest], Acc) ->
finish_encode([Val | Rest], Acc) when is_integer(Val) ->
Bin = list_to_binary(integer_to_list(Val)),
finish_encode(Rest, [Bin | Acc]);
finish_encode([{json, Json} | Rest], Acc) ->
finish_encode(Rest, [Json | Acc]);
finish_encode([InvalidEjson | _], _) ->
throw({error, {invalid_ejson, InvalidEjson}});
finish_encode(_, _) ->
Expand Down
Loading