Browse Source

Persist the `val` register across yield

The `val` variable is a register value that we need to be able to return
at any time from `decode_iter`. If it happened that a yield was
triggered while processing trailing whitespace the lack of persistance
caused decode to return a term intialized from a random integer value.
Obviously the Erlang VM did not enjoy this.

Thanks to @michalpalka for the report.

Fixes #66
pull/72/head 0.13.0
Paul J. Davis 10 years ago
parent
commit
5cd89c1eda
4 changed files with 36 additions and 18 deletions
  1. +12
    -10
      c_src/decoder.c
  2. +1
    -1
      c_src/jiffy.c
  3. +7
    -7
      src/jiffy.erl
  4. +16
    -0
      test/jiffy_13_whitespace_tests.erl

+ 12
- 10
c_src/decoder.c View File

@ -675,7 +675,7 @@ decode_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{ {
Decoder* d; Decoder* d;
jiffy_st* st = (jiffy_st*) enif_priv_data(env); jiffy_st* st = (jiffy_st*) enif_priv_data(env);
ERL_NIF_TERM tmp_argv[4];
ERL_NIF_TERM tmp_argv[5];
ERL_NIF_TERM opts; ERL_NIF_TERM opts;
ERL_NIF_TERM val; ERL_NIF_TERM val;
@ -690,8 +690,9 @@ decode_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
tmp_argv[0] = argv[0]; tmp_argv[0] = argv[0];
tmp_argv[1] = enif_make_resource(env, d); tmp_argv[1] = enif_make_resource(env, d);
tmp_argv[2] = enif_make_list(env, 0);
tmp_argv[2] = st->atom_error;
tmp_argv[3] = enif_make_list(env, 0); tmp_argv[3] = enif_make_list(env, 0);
tmp_argv[4] = enif_make_list(env, 0);
enif_release_resource(d); enif_release_resource(d);
@ -716,7 +717,7 @@ decode_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
} }
} }
return decode_iter(env, 4, tmp_argv);
return decode_iter(env, 5, tmp_argv);
} }
ERL_NIF_TERM ERL_NIF_TERM
@ -729,25 +730,25 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ERL_NIF_TERM objs; ERL_NIF_TERM objs;
ERL_NIF_TERM curr; ERL_NIF_TERM curr;
ERL_NIF_TERM val;
ERL_NIF_TERM val = argv[2];
ERL_NIF_TERM ret; ERL_NIF_TERM ret;
size_t start; size_t start;
if(argc != 4) {
if(argc != 5) {
return enif_make_badarg(env); return enif_make_badarg(env);
} else if(!enif_inspect_binary(env, argv[0], &bin)) { } else if(!enif_inspect_binary(env, argv[0], &bin)) {
return enif_make_badarg(env); return enif_make_badarg(env);
} else if(!enif_get_resource(env, argv[1], st->res_dec, (void**) &d)) { } else if(!enif_get_resource(env, argv[1], st->res_dec, (void**) &d)) {
return enif_make_badarg(env); return enif_make_badarg(env);
} else if(!enif_is_list(env, argv[2])) {
return enif_make_badarg(env);
} else if(!enif_is_list(env, argv[3])) { } else if(!enif_is_list(env, argv[3])) {
return enif_make_badarg(env); return enif_make_badarg(env);
} else if(!enif_is_list(env, argv[4])) {
return enif_make_badarg(env);
} }
dec_init(d, env, argv[0], &bin); dec_init(d, env, argv[0], &bin);
objs = argv[2];
curr = argv[3];
objs = argv[3];
curr = argv[4];
//fprintf(stderr, "Parsing:\r\n"); //fprintf(stderr, "Parsing:\r\n");
start = d->i; start = d->i;
@ -755,10 +756,11 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
//fprintf(stderr, "state: %d\r\n", dec_curr(d)); //fprintf(stderr, "state: %d\r\n", dec_curr(d));
if(should_yield(d->i - start, d->bytes_per_iter)) { if(should_yield(d->i - start, d->bytes_per_iter)) {
consume_timeslice(env, d->i - start, d->bytes_per_iter); consume_timeslice(env, d->i - start, d->bytes_per_iter);
return enif_make_tuple4(
return enif_make_tuple5(
env, env,
st->atom_iter, st->atom_iter,
argv[1], argv[1],
val,
objs, objs,
curr curr
); );

+ 1
- 1
c_src/jiffy.c View File

@ -78,7 +78,7 @@ unload(ErlNifEnv* env, void* priv)
static ErlNifFunc funcs[] = static ErlNifFunc funcs[] =
{ {
{"nif_decode_init", 2, decode_init}, {"nif_decode_init", 2, decode_init},
{"nif_decode_iter", 4, decode_iter},
{"nif_decode_iter", 5, decode_iter},
{"nif_encode_init", 2, encode_init}, {"nif_encode_init", 2, encode_init},
{"nif_encode_iter", 3, encode_iter} {"nif_encode_iter", 3, encode_iter}
}; };

+ 7
- 7
src/jiffy.erl View File

@ -18,8 +18,8 @@ decode(Data, Opts) when is_binary(Data), is_list(Opts) ->
throw(Error); throw(Error);
{partial, EJson} -> {partial, EJson} ->
finish_decode(EJson); finish_decode(EJson);
{iter, Decoder, Objs, Curr} ->
decode_loop(Data, Decoder, Objs, Curr);
{iter, Decoder, Val, Objs, Curr} ->
decode_loop(Data, Decoder, Val, Objs, Curr);
EJson -> EJson ->
EJson EJson
end; end;
@ -120,14 +120,14 @@ init() ->
erlang:load_nif(filename:join(PrivDir, "jiffy"), 0). erlang:load_nif(filename:join(PrivDir, "jiffy"), 0).
decode_loop(Data, Decoder, Objs, Curr) ->
case nif_decode_iter(Data, Decoder, Objs, Curr) of
decode_loop(Data, Decoder, Val, Objs, Curr) ->
case nif_decode_iter(Data, Decoder, Val, Objs, Curr) of
{error, _} = Error -> {error, _} = Error ->
throw(Error); throw(Error);
{partial, EJson} -> {partial, EJson} ->
finish_decode(EJson); finish_decode(EJson);
{iter, NewDecoder, NewObjs, NewCurr} ->
decode_loop(Data, NewDecoder, NewObjs, NewCurr);
{iter, NewDecoder, NewVal, NewObjs, NewCurr} ->
decode_loop(Data, NewDecoder, NewVal, NewObjs, NewCurr);
EJson -> EJson ->
EJson EJson
end. end.
@ -156,7 +156,7 @@ not_loaded(Line) ->
nif_decode_init(_Data, _Opts) -> nif_decode_init(_Data, _Opts) ->
?NOT_LOADED. ?NOT_LOADED.
nif_decode_iter(_Data, _Decoder, _, _) ->
nif_decode_iter(_Data, _Decoder, _, _, _) ->
?NOT_LOADED. ?NOT_LOADED.
nif_encode_init(_Data, _Options) -> nif_encode_init(_Data, _Options) ->

+ 16
- 0
test/jiffy_13_whitespace_tests.erl View File

@ -0,0 +1,16 @@
% This file is part of Jiffy released under the MIT license.
% See the LICENSE file for more information.
-module(jiffy_13_whitespace_tests).
-include_lib("eunit/include/eunit.hrl").
trailing_whitespace_test_() ->
Str = [<<"{\"a\":\"b\"}">>, gen_ws(2040)],
Obj = {[{<<"a">>, <<"b">>}]},
?_assertEqual(Obj, jiffy:decode(Str)).
gen_ws(N) ->
[" " || _ <- lists:seq(1, N)].

Loading…
Cancel
Save