From 66797a92a39763c04ad662dbd5eed66dbee1d0b3 Mon Sep 17 00:00:00 2001 From: Sargun Dhillon Date: Wed, 22 Nov 2017 01:53:27 -0800 Subject: [PATCH] Add return_truncated decode flag --- README.md | 3 +++ c_src/decoder.c | 11 ++++++++++- c_src/jiffy.c | 2 ++ c_src/jiffy.h | 2 ++ src/jiffy.erl | 4 +++- test/jiffy_18_return_truncated_tests.erl | 25 ++++++++++++++++++++++++ 6 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 test/jiffy_18_return_truncated_tests.erl diff --git a/README.md b/README.md index 58fbc0f2..05c2983b 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,9 @@ The options for decode are: JSON term is decoded the return value of decode/2 becomes `{has_trailer, FirstTerm, RestData::iodata()}`. This is useful to decode multiple terms in a single binary. +* `return_truncated` - If the JSON passed to the decoder is incomplete + return `{truncated, Pos}`, where `Pos`, is the length of JSON read + before hitting the end of the interpretable term. * `dedupe_keys` - If a key is repeated in a JSON object this flag will ensure that the parsed object only contains a single entry containing the last value seen. This mirrors the parsing beahvior diff --git a/c_src/decoder.c b/c_src/decoder.c index eb14a11e..24f4e4c7 100644 --- a/c_src/decoder.c +++ b/c_src/decoder.c @@ -52,6 +52,7 @@ typedef struct { size_t bytes_per_red; int is_partial; int return_maps; + int return_truncated; int return_trailer; int dedupe_keys; int copy_strings; @@ -84,6 +85,7 @@ dec_new(ErlNifEnv* env) d->is_partial = 0; d->return_maps = 0; d->return_trailer = 0; + d->return_truncated = 0; d->dedupe_keys = 0; d->copy_strings = 0; d->null_term = d->atoms->atom_null; @@ -689,6 +691,8 @@ decode_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) #endif } else if(enif_compare(val, d->atoms->atom_return_trailer) == 0) { d->return_trailer = 1; + } else if(enif_compare(val, d->atoms->atom_return_truncated) == 0) { + d->return_truncated = 1; } else if(enif_compare(val, d->atoms->atom_dedupe_keys) == 0) { d->dedupe_keys = 1; } else if(enif_compare(val, d->atoms->atom_copy_strings) == 0) { @@ -1035,7 +1039,12 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) } if(dec_curr(d) != st_done) { - ret = dec_error(d, "truncated_json"); + if (d->return_truncated) { + ret = enif_make_tuple2(env, d->atoms->atom_truncated, + enif_make_int(d->env, d->i+1)); + } else { + ret = dec_error(d, "truncated_json"); + } } else if(d->is_partial) { ret = enif_make_tuple2(env, d->atoms->atom_partial, val); } else { diff --git a/c_src/jiffy.c b/c_src/jiffy.c index 03ded3ed..49943acd 100644 --- a/c_src/jiffy.c +++ b/c_src/jiffy.c @@ -24,9 +24,11 @@ load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info) st->atom_pretty = make_atom(env, "pretty"); st->atom_force_utf8 = make_atom(env, "force_utf8"); st->atom_iter = make_atom(env, "iter"); + st->atom_truncated = make_atom(env, "truncated"); st->atom_bytes_per_iter = make_atom(env, "bytes_per_iter"); st->atom_return_maps = make_atom(env, "return_maps"); st->atom_return_trailer = make_atom(env, "return_trailer"); + st->atom_return_truncated = make_atom(env, "return_truncated"); st->atom_has_trailer = make_atom(env, "has_trailer"); st->atom_nil = make_atom(env, "nil"); st->atom_use_nil = make_atom(env, "use_nil"); diff --git a/c_src/jiffy.h b/c_src/jiffy.h index ef03a06c..e5004c9b 100644 --- a/c_src/jiffy.h +++ b/c_src/jiffy.h @@ -27,9 +27,11 @@ typedef struct { ERL_NIF_TERM atom_pretty; ERL_NIF_TERM atom_force_utf8; ERL_NIF_TERM atom_iter; + ERL_NIF_TERM atom_truncated; ERL_NIF_TERM atom_bytes_per_iter; ERL_NIF_TERM atom_return_maps; ERL_NIF_TERM atom_return_trailer; + ERL_NIF_TERM atom_return_truncated; ERL_NIF_TERM atom_has_trailer; ERL_NIF_TERM atom_nil; ERL_NIF_TERM atom_use_nil; diff --git a/src/jiffy.erl b/src/jiffy.erl index a116211c..1fa7c7e3 100644 --- a/src/jiffy.erl +++ b/src/jiffy.erl @@ -34,11 +34,13 @@ -endif. -type jiffy_decode_result() :: json_value() - | {has_trailer, json_value(), binary()}. + | {has_trailer, json_value(), binary()} + | {truncated, non_neg_integer()}. -type decode_option() :: return_maps | use_nil | return_trailer + | return_truncated | dedupe_keys | {null_term, any()} | {bytes_per_iter, non_neg_integer()} diff --git a/test/jiffy_18_return_truncated_tests.erl b/test/jiffy_18_return_truncated_tests.erl new file mode 100644 index 00000000..cb905bd0 --- /dev/null +++ b/test/jiffy_18_return_truncated_tests.erl @@ -0,0 +1,25 @@ +% This file is part of Jiffy released under the MIT license. +% See the LICENSE file for more information. + +-module(jiffy_18_return_truncated_tests). + +-include_lib("eunit/include/eunit.hrl"). + +cases() -> + [ + {<<"">>, 1}, + {<<"{">>, 2} + ]. + +return_truncated_test_() -> + Opts = [return_truncated], + {"Test return_truncated", lists:map(fun({Data, Pos}) -> + ?_assertEqual({truncated, Pos}, jiffy:decode(Data, Opts)) + end, cases())}. + +error_truncated_test_() -> + Opts = [], + {"Test truncated error case", lists:map(fun({Data, Pos}) -> + Error = {error, {Pos, truncated_json}}, + ?_assertException(throw, Error, jiffy:decode(Data, Opts)) + end, cases())}.