source: titan/titan/jsmn.h @ 24291

Last change on this file since 24291 was 17355, checked in by nit, 12 years ago

[titan] add json (jsmn) functions

File size: 6.6 KB
Line 
1#ifndef JSMN_H
2#define JSMN_H
3
4/**
5 * JSON type identifier. Basic types are:
6 *      o Object
7 *      o Array
8 *      o String
9 *      o Other primitive: number, boolean (true/false) or null
10 */
11typedef enum
12{
13        JSMN_PRIMITIVE = 0,
14        JSMN_OBJECT = 1,
15        JSMN_ARRAY = 2,
16        JSMN_STRING = 3
17} jsmntype_t;
18
19typedef enum
20{
21        // Not enough tokens were provided
22        JSMN_ERROR_NOMEM = -1,
23        // Invalid character inside JSON string
24        JSMN_ERROR_INVAL = -2,
25        // The string is not a full JSON packet, more bytes expected
26        JSMN_ERROR_PART = -3,
27        // Everything was fine
28        JSMN_SUCCESS = 0
29} jsmnerr_t;
30
31/**
32 * JSON token description.
33 * @param               type    type (object, array, string etc.)
34 * @param               start   start position in JSON data string
35 * @param               end             end position in JSON data string
36 */
37typedef struct
38{
39        jsmntype_t type;
40        int start;
41        int end;
42        int size;
43} jsmntok_t;
44
45/**
46 * JSON parser. Contains an array of token blocks available. Also stores
47 * the string being parsed now and current position in that string
48 */
49typedef struct
50{
51        unsigned int pos; // offset in the JSON string
52        int toknext; // next token to allocate
53        int toksuper; // suporior token node, e.g parent object or array
54} jsmn_parser;
55
56/**
57 * Allocates a fresh unused token from the token pull.
58 */
59static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, size_t num_tokens)
60{
61        unsigned int i;
62        for(i = parser->toknext; i < num_tokens; i++)
63        {
64                if(tokens[i].start == -1 && tokens[i].end == -1)
65                {
66                        parser->toknext = i + 1;
67                        return &tokens[i];
68                }
69        }
70        return NULL;
71}
72
73/**
74 * Fills token type and boundaries.
75 */
76static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, int start, int end)
77{
78        token->type = type;
79        token->start = start;
80        token->end = end;
81        token->size = 0;
82}
83
84/**
85 * Fills next available token with JSON primitive.
86 */
87static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, jsmntok_t *tokens, size_t num_tokens)
88{
89        jsmntok_t *token;
90        int start;
91
92        start = parser->pos;
93
94        for(; js[parser->pos] != '\0'; parser->pos++)
95        {
96                switch(js[parser->pos])
97                {
98#ifndef JSMN_STRICT
99                        // In strict mode primitive must be followed by "," or "}" or "]"
100                        case '\t' : case '\r' : case '\n' : case ' ' : case ':':
101#endif
102                        case ','  : case ']'  : case '}' :
103                                goto found;
104                }
105                if(js[parser->pos] < 32 || js[parser->pos] >= 127)
106                {
107                        parser->pos = start;
108                        return JSMN_ERROR_INVAL;
109                }
110        }
111#ifdef JSMN_STRICT
112        // In strict mode primitive must be followed by a comma/object/array
113        parser->pos = start;
114        return JSMN_ERROR_PART;
115#endif
116
117found:
118        token = jsmn_alloc_token(parser, tokens, num_tokens);
119        if(token == NULL)
120        {
121                parser->pos = start;
122                return JSMN_ERROR_NOMEM;
123        }
124        jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
125        parser->pos--;
126        return JSMN_SUCCESS;
127}
128
129/**
130 * Filsl next token with JSON string.
131 */
132static int jsmn_parse_string(jsmn_parser *parser, const char *js, jsmntok_t *tokens, size_t num_tokens)
133{
134        jsmntok_t *token;
135
136        int start = parser->pos;
137
138        parser->pos++;
139
140        // Skip starting quote
141        for(; js[parser->pos] != '\0'; parser->pos++)
142        {
143                char c = js[parser->pos];
144
145                // Quote: end of string
146                if(c == '\"')
147                {
148                        token = jsmn_alloc_token(parser, tokens, num_tokens);
149                        if(token == NULL)
150                        {
151                                parser->pos = start;
152                                return JSMN_ERROR_NOMEM;
153                        }
154                        jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
155                        return JSMN_SUCCESS;
156                }
157
158                // Backslash: Quoted symbol expected
159                if(c == '\\')
160                {
161                        parser->pos++;
162                        switch (js[parser->pos])
163                        {
164                                // Allowed escaped symbols
165                                case '\"': case '/': case '\\': case 'b':
166                                case 'f': case 'r': case 'n': case 't':
167                                        break;
168                                // Allows escaped symbol \uXXXX
169                                case 'u':
170                                        // TODO
171                                        break;
172                                // Unexpected symbol
173                                default:
174                                        parser->pos = start;
175                                        return JSMN_ERROR_INVAL;
176                        }
177                }
178        }
179        parser->pos = start;
180        return JSMN_ERROR_PART;
181}
182
183/**
184 * Parse JSON string and fill tokens.
185 */
186jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, jsmntok_t *tokens, unsigned int num_tokens)
187{
188        int r;
189        int i;
190        jsmntok_t *token;
191
192        // initialize the rest of tokens (they could be reallocated)
193        for(i = parser->toknext; i < num_tokens; i++)
194                jsmn_fill_token(&tokens[i], JSMN_PRIMITIVE, -1, -1);
195
196        for(; js[parser->pos] != '\0'; parser->pos++)
197        {
198                char c;
199                jsmntype_t type;
200
201                c = js[parser->pos];
202                switch(c)
203                {
204                        case '{': case '[':
205                                token = jsmn_alloc_token(parser, tokens, num_tokens);
206                                if(token == NULL)
207                                        return JSMN_ERROR_NOMEM;
208                                if(parser->toksuper != -1)
209                                        tokens[parser->toksuper].size++;
210                                token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
211                                token->start = parser->pos;
212                                parser->toksuper = parser->toknext - 1;
213                                break;
214                        case '}': case ']':
215                                type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
216                                for(i = parser->toknext - 1; i >= 0; i--)
217                                {
218                                        token = &tokens[i];
219                                        if(token->start != -1 && token->end == -1)
220                                        {
221                                                if(token->type != type)
222                                                        return JSMN_ERROR_INVAL;
223
224                                                parser->toksuper = -1;
225                                                token->end = parser->pos + 1;
226                                                break;
227                                        }
228                                }
229                                // Error if unmatched closing bracket
230                                if(i == -1) return JSMN_ERROR_INVAL;
231                                for(; i >= 0; i--)
232                                {
233                                        token = &tokens[i];
234                                        if(token->start != -1 && token->end == -1)
235                                        {
236                                                parser->toksuper = i;
237                                                break;
238                                        }
239                                }
240                                break;
241                        case '\"':
242                                r = jsmn_parse_string(parser, js, tokens, num_tokens);
243                                if(r < 0) return r;
244                                if(parser->toksuper != -1)
245                                        tokens[parser->toksuper].size++;
246                                break;
247                        case '\t': case '\r': case '\n': case ':': case ',': case ' ':
248                                break;
249#ifdef JSMN_STRICT
250                        // In strict mode primitives are: numbers and booleans
251                        case '-': case '0': case '1': case '2': case '3': case '4':
252                        case '5': case '6': case '7': case '8': case '9':
253                        case 't': case 'f': case 'n':
254#else
255                        // In non-strict mode every unquoted value is a primitive
256                        default:
257#endif
258                                r = jsmn_parse_primitive(parser, js, tokens, num_tokens);
259                                if(r < 0) return r;
260                                if(parser->toksuper != -1)
261                                        tokens[parser->toksuper].size++;
262                                break;
263
264#ifdef JSMN_STRICT
265                        // Unexpected char in strict mode
266                        default:
267                                return JSMN_ERROR_INVAL;
268#endif
269
270                }
271        }
272
273        for(i = parser->toknext - 1; i >= 0; i--)
274        {
275                // Unmatched opened object or array */
276                if(tokens[i].start != -1 && tokens[i].end == -1)
277                        return JSMN_ERROR_PART;
278        }
279
280        return JSMN_SUCCESS;
281}
282
283/**
284 * Creates a new parser based over a given  buffer with an array of tokens
285 * available.
286 */
287void jsmn_init(jsmn_parser *parser)
288{
289        parser->pos = 0;
290        parser->toknext = 0;
291        parser->toksuper = -1;
292}
293
294#endif
Note: See TracBrowser for help on using the repository browser.