-
Notifications
You must be signed in to change notification settings - Fork 6
/
json65.h
293 lines (245 loc) · 11.1 KB
/
json65.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
/*
JSON65 - A JSON parser for the 6502 microprocessor.
https://github.com/ppelleti/json65
Copyright © 2018 Patrick Pelletier
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef J65_H
#define J65_H
#include <stdint.h>
#include <stddef.h> /* for size_t */
/*
An event which is passed to the callback function, indicating
what has happened in the parser. Most events have no associated
data, but a few have a string and/or an integer, as noted below,
which may be retrieved with j65_get_string() or j65_get_integer().
*/
enum j65_event {
J65_NULL = 0,
J65_FALSE = 1,
J65_TRUE = 2,
J65_INTEGER = 3, /* integer and string */
J65_NUMBER = 4, /* string */
J65_STRING = 5, /* string */
J65_KEY = 6, /* string */
J65_START_OBJ = 7,
J65_END_OBJ = 8,
J65_START_ARRAY = 9,
J65_END_ARRAY = 10,
};
/*
A status value returned by j65_parse(). J65_DONE indicates that a
complete JSON value has been parsed successfully. J65_WANT_MORE
indicates that j65_parse() should be called again with more input.
(If the end of the file has been reached, this can be considered an
"unexpected end of file" error.)
Negative values indicate errors. The error may be one of the
predefined errors below, or it may be an error returned by the
callback. User-defined error numbers should be between
J65_USER_ERROR and -1, inclusive.
If you want to define your own error codes, I recommend doing it
something like this:
enum {
MYERR_MISSING_REQUIRED_KEY = J65_USER_ERROR,
MYERR_UNSUPPORTED_KEY,
MYERR_VALUE_OUT_OF_RANGE,
MYERR_SOME_OTHER_ERROR,
};
This way, your error codes will begin at J65_USER_ERROR, and
grow upwards (towards 0).
*/
enum j65_status {
J65_DONE = 1,
J65_WANT_MORE = 2,
/* errors */
J65_PARSE_ERROR = -128,
J65_ILLEGAL_CHAR,
J65_ILLEGAL_ESCAPE,
J65_NESTING_TOO_DEEP,
J65_STRING_TOO_LONG,
J65_EXPECTED_STRING,
J65_EXPECTED_COLON,
J65_EXPECTED_COMMA,
J65_EXPECTED_OBJ_END,
J65_EXPECTED_ARRAY_END,
J65_USER_ERROR, /* must be last. not generated by parser. */
};
/*
The state for a JSON parser. Initialize it by calling j65_init().
It is too big to be allocated on the cc65 stack, so you should either
allocate it statically, or on the heap.
*/
typedef struct {
uint8_t dummy[512];
} j65_parser;
/*
The type of the callback function passed to j65_init. This is called
from within j65_parse() whenever a parsing "event" occurs. The event
type (the j65_event enumeration, but passed as a uint8_t) indicates
which event occurred. Depending on the event type, additional
information may be available by calling one of the accessor functions
on the j65_parser. (See functions below.)
If the event was processed successfully, the callback should return
zero. If the callback wishes to terminate the parsing early, and
cause j65_parse() to return immediately, the callback should return
a negative value. (Specifically, it should return an integer between
J65_USER_ERROR and -1, inclusive.)
*/
typedef int8_t __fastcall__ (*j65_callback)(j65_parser *p, uint8_t event);
/*
Initializes a j65_parser structure. The arguments passed will be
stored in the j65_parser structure. Specifically:
ctx is a pointer with no predefined meaning. It may be retrieved
by calling j65_get_context() on the parser.
cb is the callback which should be called by j65_parse() when an
event occurs.
max_depth is the maximum depth of nested objects and arrays allowed
when parsing the JSON. max_depth will automatically be capped at
224. It may sometimes be helpful to limit max_depth to a
smaller value. (For example, if you are going to build up a tree
and then walk it recursively, the 6502 stack cannot hold 224
return addresses, so you could limit max_depth to a value somewhat
less than 128, to prevent overflowing the stack.) To obtain the
value actually used for max_depth, call j65_get_max_depth() on the
parser.
If max_depth is 0, then it will be set to the maximum allowable
value, which is 224. So, if you do not wish to furhter limit the
maximum depth, pass 0 for max_depth. This means that the smallest
value you can actually set max_depth to is 1, which means that you
can only have a top-level array or object, but no arrays or objects
inside of it. (Hypothetically, a max_depth of 0 would mean only
top-level scalars are accepted, and no arrays or objects would be
allowed at all. But we do not let you set max_depth to 0.)
Once the j65_parser has been initialized, you may call j65_parse()
on it.
*/
void __fastcall__ j65_init (j65_parser *p,
void *ctx,
j65_callback cb,
uint8_t max_depth);
/*
Parses the JSON in buf, of length len. j65_parse() may be called
multiple times (as long as it returns J65_WANT_MORE) to parse input
incrementally.
j65_parse() calls the callback (supplied to j65_init()) as events
occur.
The return value is the j65_status enumeration, returned as an
int8_t. J65_DONE indicates the parsing completed successfully.
J65_WANT_MORE indicates that j65_parse() should be called again
with more input. Any negative value indicates an error, either
one generated by the parser, or one returned by the callback.
*/
int8_t __fastcall__ j65_parse (j65_parser *p, const char *buf, size_t len);
/*
Returns the string associated with the current event. This call is
only valid inside the callback function, and only when the event is
one of J65_INTEGER, J65_NUMBER, J65_STRING, or J65_KEY.
The string returned is only valid until the callback returns.
The string is NUL-terminated, so it is not necessary to call
j65_get_length(), unless you wish to support strings with
embedded NUL characters.
In the case of a J65_NUMBER OR J65_KEY event, backslash escape
sequences in the string have already been substituted. The string
is UTF-8 encoded.
In the case of a J65_NUMBER event, beware that although the number
has been validated to contain only characters that are legal in a
number, the number has not been fully validated to conform to the
format for a legal number (specified in section 6 of RFC 8259).
For example, the string might be "10.10.10-e++", which is not a
valid number. So, you will need to more fully validate the number
when parsing it, and return a user error from the callback if
necessary.
*/
const char * __fastcall__ j65_get_string (const j65_parser *p);
/*
In the cases where j65_get_string() is valid, j65_get_length() is
valid, too. It returns the length of the string returned by
j65_get_string(). This is necessary if you wish to support
strings with embedded NUL characters, and it may also be useful
in other situations, such as if you want to avoid the performance
hit of calling strlen() on the string returned by j65_get_string().
*/
uint8_t __fastcall__ j65_get_length (const j65_parser *p);
/*
Returns the integer associated with the current event. This call is
only valid inside the callback function, and only when the event is
J65_INTEGER. Note that in a J65_INTEGER event, both j65_get_integer()
and j65_get_string() are valid, so you may process the integer
either as an integer or a string.
If a number cannot be represented as an int32_t, either because it
is non-integral, or because it is too large, then a J65_NUMBER
event is generated instead of J65_INTEGER.
*/
int32_t __fastcall__ j65_get_integer (const j65_parser *p);
/*
Returns the offset within the file of the beginning of the current
line. (In other words, the total number of bytes processed by this
j65_parser, prior to the first byte of the current line.)
j65_get_line_offset() may be called either within the callback
function (for any event type), or after j65_parse() returns.
This can be useful if you want to print the current line when an
error occurs.
*/
uint32_t __fastcall__ j65_get_line_offset (const j65_parser *p);
/*
Returns the line number of the current line.
j65_get_line_number() may be called either within the callback
function (for any event type), or after j65_parse() returns.
The line number is 0-based, so you will probably want to add 1
before displaying it to the user.
Lines may be terminated either by a linefeed (UNIX standard) or
a carriage return (Apple II standard). However, a carriage
return is not counted if it is immediately preceded by a linefeed.
(Thus allowing MS-DOS/Windows standard line endings to be supported
as well.)
*/
uint32_t __fastcall__ j65_get_line_number (const j65_parser *p);
/*
Like j65_get_line_number(), but returns the current column number
within the current line. Like j65_get_line_number(), the column
number is 0-based, and may be obtained at any time, either during
or after parsing.
When inside the callback, the column number usually indicates the
last byte of the value which generated the current event, or the
byte immediately after it. When j65_parse() returns with an error,
the column may either point to the byte where the error occurred,
or the last byte of the value which generated the error, or the
byte immediately following it.
The column number simply counts bytes, so the number may be
unintuitive if your input string contains tabs or multibyte
UTF-8 characters.
*/
uint32_t __fastcall__ j65_get_column_number (const j65_parser *p);
/*
Returns the current depth of nested arrays and objects.
It can be between 0 and max_depth (which is never more than
224), inclusive. Depth 0 only occurs for top-level scalars.
*/
uint8_t __fastcall__ j65_get_current_depth (const j65_parser *p);
/*
Returns the maximum value that j65_get_current_depth() can have.
This is the value of max_depth which was supplied to j65_init(),
unless the value supplied was 0 or was greater than 224, in which
case the max depth will be 224.
If this depth is exceeded, the parser will return the error code
J65_NESTING_TOO_DEEP.
*/
uint8_t __fastcall__ j65_get_max_depth (const j65_parser *p);
/*
Returns the ctx pointer which was supplied to j65_init().
This can be used for passing arbitrary data to the callback.
*/
void * __fastcall__ j65_get_context (const j65_parser *p);
#endif /* J65_H */