Skip to content

Commit a073148

Browse files
committed
Release 1.0
0 parents  commit a073148

11 files changed

+1418
-0
lines changed

LICENSE

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
This is the MIT license.
2+
3+
Copyright (c) 2013 Yuriy Iskra <[email protected]>.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6+
7+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8+
9+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Makefile

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
REBAR = rebar
2+
3+
.PHONY: all xref erl test clean doc
4+
5+
all: erl xref test
6+
7+
erl:
8+
$(REBAR) compile
9+
10+
xref:
11+
$(REBAR) xref
12+
13+
test:
14+
@mkdir -p .eunit
15+
$(REBAR) eunit
16+
17+
clean:
18+
@$(REBAR) clean
19+
@-rm -rvf deps ebin doc .eunit
20+
@-rm c_src/*.o
21+
@-rm priv/*.so
22+
@-rm */*~
23+
24+
doc:
25+
$(REBAR) doc

README.md

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
JSONX is an Erlang library for efficient decode and encode JSON, written in C.
3+
Works with binaries as strings, arrays as lists and it only knows how to decode UTF-8 (and ASCII).
4+
5+
Decode (json -> erlang)
6+
=======================
7+
8+
- null -> atom null
9+
- true -> atom true
10+
- false -> atom false
11+
- string -> binary
12+
- number -> number
13+
- array -> list
14+
- object -> {struct, PropList}, optional eep18 or proplist.
15+
16+
Encode (erlang -> json)
17+
=======================
18+
19+
- atom null -> null
20+
- atom true -> true
21+
- atom true -> false
22+
- any other atom -> string
23+
- binary -> string
24+
- number -> number
25+
- {struct, PropList} -> object
26+
- {PropList} -> object
27+
- PropList -> object
28+
- {json, IOList} -> include IOList with no validation

c_src/decoder.c

+306
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
// Copyright 2013 Yuriy Iskra <[email protected]>
2+
3+
#include <string.h>
4+
#include <ctype.h>
5+
#include <errno.h>
6+
#include <assert.h>
7+
#include "erl_nif.h"
8+
#include "jsonx.h"
9+
#include "jsonx_str.h"
10+
11+
#include <stdio.h>
12+
#define JS_OFFSET (8 * sizeof(ERL_NIF_TERM))
13+
14+
typedef struct{
15+
ErlNifEnv* env;
16+
Atoms *atoms;
17+
size_t buf_size;
18+
unsigned char *buf;
19+
unsigned char *cur;
20+
size_t offset;
21+
ERL_NIF_TERM input;
22+
ERL_NIF_TERM format; //struct, eep18, proplist
23+
ERL_NIF_TERM error;
24+
ERL_NIF_TERM *stack_top;
25+
ERL_NIF_TERM *stack_down;
26+
} State;
27+
28+
static inline ERL_NIF_TERM parse_json(State* st);
29+
static inline ERL_NIF_TERM parse_array(State* st);
30+
static inline ERL_NIF_TERM parse_object(State* st);
31+
static inline ERL_NIF_TERM parse_string(State* st);
32+
static inline ERL_NIF_TERM parse_number(State* st);
33+
static inline ERL_NIF_TERM parse_true(State* st);
34+
static inline ERL_NIF_TERM parse_false(State* st);
35+
static inline ERL_NIF_TERM parse_null(State* st);
36+
37+
38+
static inline void
39+
grow_stack(State *st){
40+
size_t new_offset = 4 * st->offset;
41+
size_t new_size = st->buf_size - st->offset + new_offset;
42+
unsigned char *new_buf = enif_alloc(new_size);
43+
unsigned char *new_cur = (new_buf + new_size) - ((st->buf + st->buf_size) - st->cur);
44+
ERL_NIF_TERM *new_down = (ERL_NIF_TERM*)new_buf;
45+
ERL_NIF_TERM *new_top = new_down + (st->stack_top - st->stack_down);
46+
memcpy(new_cur, st->cur, (st->buf + st->buf_size) - st->cur);
47+
memcpy(new_down, st->stack_down, (void*)st->stack_top - (void*)st->stack_down);
48+
49+
enif_free(st->buf);
50+
st->buf_size = new_size;
51+
st->offset = new_offset;
52+
st->cur = new_cur;
53+
st->buf = new_buf;
54+
st->stack_down = new_down;
55+
st->stack_top = new_top;
56+
}
57+
58+
static inline void
59+
push_term(State* st, ERL_NIF_TERM val){
60+
if(((unsigned char*)st->stack_top + sizeof(ERL_NIF_TERM)) > (st->cur)){
61+
grow_stack(st);
62+
}
63+
*(st->stack_top++) = val;
64+
}
65+
66+
static inline unsigned char
67+
look_ah(State *st){
68+
while(isspace(*st->cur))
69+
st->cur++;
70+
return *(st->cur);
71+
}
72+
73+
static inline ERL_NIF_TERM
74+
parse_array(State* st){
75+
ERL_NIF_TERM term;
76+
77+
st->cur++;
78+
if(look_ah(st) == ']'){
79+
st->cur++;
80+
return enif_make_list(st->env, 0);
81+
}
82+
83+
size_t stack_off = st->stack_top - st->stack_down;
84+
for(;;){
85+
if((term = parse_json(st))){
86+
push_term(st, term);
87+
unsigned char c = look_ah(st);
88+
st->cur++;
89+
if(c == ','){
90+
continue;
91+
}else if(c == ']'){
92+
ERL_NIF_TERM *down = st->stack_down + stack_off;
93+
term = enif_make_list_from_array(st->env, down, st->stack_top - down);
94+
st->stack_top = down;
95+
return term;
96+
}else{
97+
st->error = st->atoms->am_esyntax;
98+
return 0;
99+
}
100+
}else{
101+
return 0;
102+
}
103+
}
104+
return enif_make_atom(st->env, "array");
105+
}
106+
107+
static inline ERL_NIF_TERM
108+
parse_object(State* st){
109+
ERL_NIF_TERM plist;
110+
ERL_NIF_TERM key, val, pair;
111+
unsigned char c;
112+
113+
st->cur++;
114+
if(look_ah(st) == '}'){
115+
st->cur++;
116+
plist = enif_make_list(st->env, 0);
117+
goto ret;
118+
}
119+
size_t stack_off = st->stack_top - st->stack_down;
120+
for(;;){
121+
if(look_ah(st) == '"'){
122+
if((key = parse_string(st))){
123+
if(look_ah(st) == ':'){
124+
st->cur++;
125+
if((val = parse_json(st))){
126+
pair = enif_make_tuple2(st->env, key, val);
127+
push_term(st, pair);
128+
c = look_ah(st);
129+
st->cur++;
130+
if(c == ','){
131+
continue;
132+
}else if(c == '}'){
133+
ERL_NIF_TERM *down = st->stack_down + stack_off;
134+
plist = enif_make_list_from_array(st->env, down, st->stack_top - down);
135+
st->stack_top = down;
136+
goto ret;
137+
}
138+
}
139+
}
140+
}
141+
}
142+
if(!st->error){
143+
st->error = st->atoms->am_esyntax;
144+
}
145+
return 0;
146+
}
147+
ret:
148+
if(st->format == st->atoms->am_struct){
149+
return enif_make_tuple2(st->env, st->atoms->am_struct, plist);
150+
}else if(st->format == st->atoms->am_eep18){
151+
return enif_make_tuple1(st->env, plist);
152+
}else if(st->format == st->atoms->am_proplist){
153+
return plist;
154+
}
155+
assert(0);
156+
}
157+
158+
static inline ERL_NIF_TERM
159+
parse_string(State* st){
160+
unsigned char *endptr;
161+
unsigned char *endstr;
162+
ERL_NIF_TERM ret;
163+
if(check_noescaped_jstr(st->cur, &endptr)){
164+
if(*endptr == '"'){
165+
ret = enif_make_sub_binary(st->env, st->input, st->cur - (st->buf + st->offset) + 1, endptr - st->cur - 1);
166+
st->cur = endptr + 1;
167+
return ret;
168+
}else if(*endptr == '\\'){
169+
if(check_with_unescape_jstr(endptr, &endstr, &endptr)){
170+
unsigned char *dst = enif_make_new_binary(st->env, endstr - st->cur - 1, &ret);
171+
memcpy(dst, st->cur + 1, endstr - st->cur - 1);
172+
st->cur = endptr + 1;
173+
return ret;
174+
}
175+
}
176+
}
177+
st->error = st->atoms->am_estr;
178+
return 0;
179+
}
180+
181+
static inline ERL_NIF_TERM
182+
parse_number(State *st){
183+
long long int_num;
184+
double float_num;
185+
char *endptr;
186+
errno = 0;
187+
188+
int_num = strtoll((char *)st->cur, &endptr, 10);
189+
if((char*)st->cur == endptr){
190+
return 0;
191+
}else if(errno == ERANGE){
192+
st->error = st->atoms->am_erange;
193+
return 0;
194+
}
195+
196+
if(*endptr == '.' || *endptr == 'e' || *endptr == 'E'){
197+
float_num = strtod((char *)st->cur, &endptr);
198+
if(errno != ERANGE){
199+
st->cur = (unsigned char*)endptr;
200+
return enif_make_double(st->env, float_num);
201+
}
202+
else{
203+
st->error = st->atoms->am_erange;
204+
return 0;
205+
}
206+
}
207+
else{
208+
st->cur = (unsigned char*)endptr;
209+
return enif_make_int64(st->env, int_num);
210+
}
211+
}
212+
213+
214+
static inline ERL_NIF_TERM
215+
parse_true(State* st){
216+
if(!(strncmp("rue", (char*)(++st->cur), 3))){
217+
st->cur = st->cur + 3;
218+
return st->atoms->am_true;
219+
}
220+
st->error = st->atoms->am_esyntax;
221+
return 0;
222+
}
223+
224+
static inline ERL_NIF_TERM
225+
parse_false(State* st){
226+
if(!(strncmp("alse", (char*)(++st->cur), 4))){
227+
st->cur = st->cur + 4;
228+
return st->atoms->am_false;
229+
}
230+
st->error = st->atoms->am_esyntax;
231+
return 0;
232+
}
233+
234+
static inline ERL_NIF_TERM
235+
parse_null(State* st){
236+
if(!(strncmp("ull", (char*)(++st->cur), 3))){
237+
st->cur = st->cur + 3;
238+
return st->atoms->am_null;
239+
}
240+
st->error = st->atoms->am_esyntax;
241+
return 0;
242+
}
243+
244+
static inline ERL_NIF_TERM
245+
parse_json(State *st){
246+
ERL_NIF_TERM num;
247+
switch(look_ah(st)){
248+
case '\"' : return parse_string(st);
249+
case '{' : return parse_object(st);
250+
case '[' : return parse_array(st);
251+
case 't' : return parse_true(st);
252+
case 'f' : return parse_false(st);
253+
case 'n' : return parse_null(st);
254+
default:
255+
if((num = parse_number(st))){
256+
return num;
257+
}
258+
if(!st->error){
259+
st->error = st->atoms->am_esyntax;
260+
}
261+
return 0;
262+
}
263+
}
264+
265+
ERL_NIF_TERM
266+
decode_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]){
267+
ErlNifBinary input;
268+
if(!enif_inspect_binary(env, argv[0], &input)){
269+
return enif_make_badarg(env);
270+
}
271+
272+
State st;
273+
st.offset = JS_OFFSET;
274+
st.buf_size = st.offset + input.size + 4;
275+
st.buf = enif_alloc(st.buf_size);
276+
st.env = env;
277+
st.input = argv[0];
278+
st.atoms = (Atoms*)enif_priv_data(env);
279+
if(argc == 2){
280+
st.format = argv[1];
281+
}else{
282+
st.format = st.atoms->am_struct;
283+
}
284+
st.stack_top = st.stack_down = (ERL_NIF_TERM*)st.buf;
285+
st.cur = st.buf + st.offset;
286+
st.error = 0;
287+
memcpy(st.cur, input.data, input.size);
288+
st.buf[st.buf_size - 1] = 0U;
289+
st.buf[st.buf_size - 2] = 0U;
290+
st.buf[st.buf_size - 3] = 0U;
291+
st.buf[st.buf_size - 4] = 0U;
292+
293+
ERL_NIF_TERM ret = parse_json(&st);
294+
if(ret){
295+
if(look_ah(&st) != 0U){
296+
ret = 0;
297+
st.error = st.atoms->am_etrailing;
298+
}
299+
}
300+
enif_free(st.buf);
301+
if(!ret){
302+
return enif_make_tuple3(env, st.atoms->am_error, st.error,
303+
enif_make_ulong(env, st.cur - (st.buf + st.offset)));
304+
}
305+
return ret;
306+
}

0 commit comments

Comments
 (0)