You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by kx...@apache.org on 2015/07/25 14:42:42 UTC

[13/19] jiffy commit: updated refs/heads/upstream to f661ee9

Add new return_trailer option

Previously Jiffy would throw an error about trailing data if there is
any non-whitespace character encounter after the first term had been
decoded.

This patch adds a decoder option `return_trailer` that will instead
return a sub-binary starting at the first non-whitespace character. This
allows users to be able to decode multiple terms from a single iodata()
term.

Thanks to @vlm for the original patch.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-jiffy/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-jiffy/commit/6d2278e9
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-jiffy/tree/6d2278e9
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-jiffy/diff/6d2278e9

Branch: refs/heads/upstream
Commit: 6d2278e90694bb484845ad149d484d0a3cae10ec
Parents: 238c653
Author: Paul J. Davis <pa...@gmail.com>
Authored: Wed Jul 15 16:18:13 2015 -0500
Committer: Paul J. Davis <pa...@gmail.com>
Committed: Wed Jul 15 18:40:27 2015 -0500

----------------------------------------------------------------------
 README.md                              |  4 ++++
 c_src/decoder.c                        | 18 ++++++++++++++++--
 c_src/jiffy.c                          |  2 ++
 c_src/jiffy.h                          |  2 ++
 src/jiffy.erl                          |  8 ++++++--
 test/jiffy_11_proper_tests.erl         | 21 +++++++++++++++++++++
 test/jiffy_15_return_trailer_tests.erl | 19 +++++++++++++++++++
 7 files changed, 70 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-jiffy/blob/6d2278e9/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 7f51ce1..f354ecf 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,10 @@ The options for decode are:
   instead of `null`.
 * `use_nil` - Returns the atom `nil` instead of `null` when decoding
   JSON. This is a short hand for `{null_term, nil}`.
+* `return_trailer` - If any non-whitespace is found after the first
+  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.
 
 `jiffy:encode/1,2`
 ------------------

http://git-wip-us.apache.org/repos/asf/couchdb-jiffy/blob/6d2278e9/c_src/decoder.c
----------------------------------------------------------------------
diff --git a/c_src/decoder.c b/c_src/decoder.c
index f39575a..60b3257 100644
--- a/c_src/decoder.c
+++ b/c_src/decoder.c
@@ -52,6 +52,7 @@ typedef struct {
     size_t          bytes_per_iter;
     int             is_partial;
     int             return_maps;
+    int             return_trailer;
     ERL_NIF_TERM    null_term;
 
     char*           p;
@@ -80,6 +81,7 @@ dec_new(ErlNifEnv* env)
     d->bytes_per_iter = DEFAULT_BYTES_PER_ITER;
     d->is_partial = 0;
     d->return_maps = 0;
+    d->return_trailer = 0;
     d->null_term = d->atoms->atom_null;
 
     d->p = NULL;
@@ -710,6 +712,8 @@ decode_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
 #else
             return enif_make_badarg(env);
 #endif
+        } else if(enif_compare(val, d->atoms->atom_return_trailer) == 0) {
+            d->return_trailer = 1;
         } else if(enif_compare(val, d->atoms->atom_use_nil) == 0) {
             d->null_term = d->atoms->atom_nil;
         } else if(get_null_term(env, val, &(d->null_term))) {
@@ -733,6 +737,7 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
     ERL_NIF_TERM objs;
     ERL_NIF_TERM curr;
     ERL_NIF_TERM val = argv[2];
+    ERL_NIF_TERM trailer;
     ERL_NIF_TERM ret;
     size_t start;
 
@@ -1030,8 +1035,7 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
                         d->i++;
                         break;
                     default:
-                        ret = dec_error(d, "invalid_trailing_data");
-                        goto done;
+                        goto decode_done;
                 }
                 break;
 
@@ -1041,6 +1045,16 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
         }
     }
 
+decode_done:
+
+    if(d->i < bin.size && d->return_trailer) {
+        trailer = enif_make_sub_binary(env, argv[0], d->i, bin.size - d->i);
+        val = enif_make_tuple3(env, d->atoms->atom_has_trailer, val, trailer);
+    } else if(d->i < bin.size) {
+        ret = dec_error(d, "invalid_trailing_data");
+        goto done;
+    }
+
     if(dec_curr(d) != st_done) {
         ret = dec_error(d, "truncated_json");
     } else if(d->is_partial) {

http://git-wip-us.apache.org/repos/asf/couchdb-jiffy/blob/6d2278e9/c_src/jiffy.c
----------------------------------------------------------------------
diff --git a/c_src/jiffy.c b/c_src/jiffy.c
index 9048f5e..1ea60a3 100644
--- a/c_src/jiffy.c
+++ b/c_src/jiffy.c
@@ -26,6 +26,8 @@ load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
     st->atom_iter = make_atom(env, "iter");
     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_has_trailer = make_atom(env, "has_trailer");
     st->atom_nil = make_atom(env, "nil");
     st->atom_use_nil = make_atom(env, "use_nil");
     st->atom_null_term = make_atom(env, "null_term");

http://git-wip-us.apache.org/repos/asf/couchdb-jiffy/blob/6d2278e9/c_src/jiffy.h
----------------------------------------------------------------------
diff --git a/c_src/jiffy.h b/c_src/jiffy.h
index 7a2b7d5..61c7b70 100644
--- a/c_src/jiffy.h
+++ b/c_src/jiffy.h
@@ -28,6 +28,8 @@ typedef struct {
     ERL_NIF_TERM    atom_iter;
     ERL_NIF_TERM    atom_bytes_per_iter;
     ERL_NIF_TERM    atom_return_maps;
+    ERL_NIF_TERM    atom_return_trailer;
+    ERL_NIF_TERM    atom_has_trailer;
     ERL_NIF_TERM    atom_nil;
     ERL_NIF_TERM    atom_use_nil;
     ERL_NIF_TERM    atom_null_term;

http://git-wip-us.apache.org/repos/asf/couchdb-jiffy/blob/6d2278e9/src/jiffy.erl
----------------------------------------------------------------------
diff --git a/src/jiffy.erl b/src/jiffy.erl
index a272cf9..4517acb 100644
--- a/src/jiffy.erl
+++ b/src/jiffy.erl
@@ -23,8 +23,12 @@
 -type json_number() :: integer() | float().
 -type json_object() :: {[{json_string(),json_value()}]}.
 
+-type decode_result() :: json_value()
+                        | {has_trailer, json_value(), binary()}.
+
 -type decode_option() :: return_maps
                         | use_nil
+                        | return_trailer
                         | {null_term, any()}
                         | {bytes_per_iter, non_neg_integer()}.
 
@@ -37,10 +41,10 @@
 -type decode_options() :: [decode_option()].
 -type encode_options() :: [encode_option()].
 
--export_type([json_value/0]).
+-export_type([json_value/0, jiffy_decode_result/0]).
 
 
--spec decode(iolist() | binary()) -> json_value().
+-spec decode(iolist() | binary()) -> jiffy_decode_result().
 decode(Data) ->
     decode(Data, []).
 

http://git-wip-us.apache.org/repos/asf/couchdb-jiffy/blob/6d2278e9/test/jiffy_11_proper_tests.erl
----------------------------------------------------------------------
diff --git a/test/jiffy_11_proper_tests.erl b/test/jiffy_11_proper_tests.erl
index 2c254bf..23aa38d 100644
--- a/test/jiffy_11_proper_tests.erl
+++ b/test/jiffy_11_proper_tests.erl
@@ -24,6 +24,7 @@ proper_encode_decode_test_() ->
     [
         run(prop_enc_dec),
         run(prop_enc_dec_pretty),
+        run(prop_dec_trailer),
         run(prop_enc_no_crash),
         run(prop_dec_no_crash_bin),
         run(prop_dec_no_crash_any)
@@ -37,6 +38,26 @@ prop_enc_dec() ->
         end
     ).
 
+prop_dec_trailer() ->
+    ?FORALL({T1, T2}, {json(), json()},
+        begin
+            B1 = jiffy:encode(T1),
+            B2 = jiffy:encode(T2),
+            Combiners = [
+                <<" ">>,
+                <<"\r\t">>,
+                <<"\n   \t">>,
+                <<"                     ">>
+            ],
+            lists:foreach(fun(Comb) ->
+                Bin = <<B1/binary, Comb/binary, B2/binary>>,
+                {has_trailer, T1, Rest} = jiffy:decode(Bin, [return_trailer]),
+                T2 = jiffy:decode(Rest)
+            end, Combiners),
+            true
+        end
+    ).
+
 -ifndef(JIFFY_NO_MAPS).
 to_map_ejson({Props}) ->
     NewProps = [{K, to_map_ejson(V)} || {K, V} <- Props],

http://git-wip-us.apache.org/repos/asf/couchdb-jiffy/blob/6d2278e9/test/jiffy_15_return_trailer_tests.erl
----------------------------------------------------------------------
diff --git a/test/jiffy_15_return_trailer_tests.erl b/test/jiffy_15_return_trailer_tests.erl
new file mode 100644
index 0000000..af80a46
--- /dev/null
+++ b/test/jiffy_15_return_trailer_tests.erl
@@ -0,0 +1,19 @@
+% This file is part of Jiffy released under the MIT license.
+% See the LICENSE file for more information.
+
+-module(jiffy_15_return_trailer_tests).
+
+-include_lib("eunit/include/eunit.hrl").
+
+trailer_test_() ->
+    Opts = [return_trailer],
+    Cases = [
+        {<<"true">>, true},
+        {<<"true;">>, {has_trailer, true, <<";">>}},
+        {<<"true[]">>, {has_trailer, true, <<"[]">>}},
+        {<<"[]{}">>, {has_trailer, [], <<"{}">>}},
+        {<<"1 2 3">>, {has_trailer, 1, <<"2 3">>}}
+    ],
+    {"Test return_trailer", lists:map(fun({Data, Result}) ->
+        ?_assertEqual(Result, jiffy:decode(Data, Opts))
+    end, Cases)}.
\ No newline at end of file