diff --git a/README.md b/README.md index 133a18b..63fee43 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,8 @@ Mapping (JSON -> Erlang) {"this": "json"} :-> [{<<"this">>: <<"json">>}] %% optional proplist {"this": "json"} :-> {struct, [{<<"this">>: <<"json">>}]} %% optional struct JSONObject :-> #rec{...} %% decoder must be predefined + -1.123e12 :-> {1, 1123, 9} %% optional {number_format, decimal} + 1.123e-6 :-> {0, 1123, -9} %% optional {number_format, decimal} Mapping (Erlang -> JSON) ----------------------- diff --git a/c_src/decoder.c b/c_src/decoder.c index f3b9f41..4d09a84 100644 --- a/c_src/decoder.c +++ b/c_src/decoder.c @@ -18,6 +18,7 @@ typedef struct{ size_t offset; ERL_NIF_TERM input; ERL_NIF_TERM format; //struct, eep18, proplist + ERL_NIF_TERM number_format; //float, decimal ERL_NIF_TERM error; ERL_NIF_TERM *stack_top; ERL_NIF_TERM *stack_down; @@ -314,6 +315,71 @@ parse_string_as_existing_atom(State* st){ return (ERL_NIF_TERM)0; } +static inline ERL_NIF_TERM +parse_decimal(State *st){ + long long value; + long long scale; + long long sign; + char *endptr; + + char *sub_start; + long long sub; + long long i; + + value = strtoll((char *)st->cur, &endptr, 10); + if (value < 0) { + sign = 1; + value = -value; + } else + sign = 0; + + if (*endptr == '.') { + sub_start = endptr + 1; + if(*sub_start == '+' || *sub_start == '-') + return (ERL_NIF_TERM)0; + + sub = strtoll(sub_start, &endptr, 10); + if(sub_start == endptr){ + return (ERL_NIF_TERM)0; + }else if(errno == ERANGE){ + st->error = st->priv->am_erange; + return (ERL_NIF_TERM)0; + } + + scale = sub_start - endptr; + for (i = scale; i < 0; i++) + value *= 10; + value += sub; + + if(value < 0) { + st->error = st->priv->am_erange; + return (ERL_NIF_TERM)0; + } + } else { + scale = 0; + } + + if ((*endptr | 0x20) == 'e') { + sub_start = endptr + 1; + sub = strtoll(sub_start, &endptr, 10); + if(sub_start == endptr){ + return (ERL_NIF_TERM)0; + }else if(errno == ERANGE){ + st->error = st->priv->am_erange; + return (ERL_NIF_TERM)0; + } + scale += sub; + } + + st->cur = (unsigned char*)endptr; + return enif_make_tuple3( + st->env, + enif_make_int64(st->env, sign), + enif_make_int64(st->env, value), + enif_make_int64(st->env, scale) + ); +} + static inline ERL_NIF_TERM parse_number(State *st){ long long int_num; @@ -330,6 +396,9 @@ parse_number(State *st){ } if(*endptr == '.' || *endptr == 'e' || *endptr == 'E'){ + if(st->number_format == st->priv->am_decimal){ + return parse_decimal(st); + } float_num = strtod((char *)st->cur, &endptr); if(errno != ERANGE){ st->cur = (unsigned char*)endptr; @@ -403,15 +472,16 @@ decode_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]){ if(!enif_inspect_binary(env, argv[0], &input)){ return enif_make_badarg(env); } - assert(argc == 2 || argc == 4 ); + assert(argc == 3 || argc == 5 ); State st; st.priv = (PrivData*)enif_priv_data(env); st.resource = NULL; st.input = argv[0]; st.format = argv[1]; - if (argc == 4){ // whith resource - assert(enif_get_resource(env, argv[2], st.priv->decoder_RSTYPE, (void**)&st.resource)); - st.strict_flag = enif_is_identical(st.priv->am_true, argv[3]) ? 1 : 0; + st.number_format = argv[2]; + if (argc == 5){ // whith resource + assert(enif_get_resource(env, argv[3], st.priv->decoder_RSTYPE, (void**)&st.resource)); + st.strict_flag = enif_is_identical(st.priv->am_true, argv[4]) ? 1 : 0; } st.offset = JS_OFFSET; st.buf_size = st.offset + input.size + 4; diff --git a/c_src/jsonx.c b/c_src/jsonx.c index a4e1809..4b214c9 100644 --- a/c_src/jsonx.c +++ b/c_src/jsonx.c @@ -50,6 +50,8 @@ load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info){ if(!enif_make_existing_atom(env, "eep18", &(pdata->am_eep18), ERL_NIF_LATIN1)) return 1; if(!enif_make_existing_atom(env, "no_match", &(pdata->am_no_match), ERL_NIF_LATIN1)) return 1; + if(!enif_make_existing_atom(env, "decimal", &(pdata->am_decimal), ERL_NIF_LATIN1)) return 1; + if(!enif_make_existing_atom(env, "float", &(pdata->am_float), ERL_NIF_LATIN1)) return 1; *priv_data = (void*)pdata; return 0; @@ -223,8 +225,8 @@ static ErlNifFunc nif_funcs[] = { {"encode1", 1, encode_nif}, {"encode_res", 2, encode_nif}, // with resource - {"decode_opt", 2, decode_nif}, // with options - {"decode_res", 4, decode_nif}, // with options, resource and strict flag + {"decode_opt", 3, decode_nif}, // with options + {"decode_res", 5, decode_nif}, // with options, resource and strict flag {"make_encoder_resource", 7, make_encoder_resource_nif}, {"make_decoder_resource", 6, make_decoder_resource_nif} }; diff --git a/c_src/jsonx.h b/c_src/jsonx.h index 5e14ff7..539325c 100644 --- a/c_src/jsonx.h +++ b/c_src/jsonx.h @@ -24,6 +24,9 @@ typedef struct{ ERL_NIF_TERM am_eep18; ERL_NIF_TERM am_no_match; + ERL_NIF_TERM am_decimal; + ERL_NIF_TERM am_float; + ErlNifResourceType* encoder_RSTYPE; ErlNifResourceType* decoder_RSTYPE; }PrivData; diff --git a/src/jsonx.erl b/src/jsonx.erl index 9b8e2d0..f00c7c7 100644 --- a/src/jsonx.erl +++ b/src/jsonx.erl @@ -10,6 +10,7 @@ %%