ubjson.h
22.7 KB
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
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
/*
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @defgroup sys_ubjson Universal Binary JSON
* @ingroup sys
* @brief A library to read and write UBJSON serialized data.
* @{
*
* @file
* @brief Headers for the UBJSON module
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef UBJSON_H
#define UBJSON_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#if defined(MODULE_MSP430_COMMON)
# include "msp430_types.h"
#elif !defined(__linux__)
# include <sys/types.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* ***************************************************************************
* READ FUNCTIONS / DEFINITIONS
*************************************************************************** */
/**
* @brief Status code of ubjson_read(), ubjson_read_array() and ubjson_read_object() callback
*
* When ubjson_read(), ubjson_read_array() and ubjson_read_object() iteratively invokes the gives callback function.
* The callback function then has to invoke another function such as ubjson_get_i32(), depending on the parameter `type`.
*/
typedef enum {
/**
* @brief There is no such value.
*
* Only used in the callback of ubjson_read() for parameter `type2`.
*/
UBJSON_ABSENT,
/**
* @brief The next datum is a null value.
*
* As you might have already guessed: you cannot read a null value.
*/
UBJSON_TYPE_NULL,
/**
* @brief The next datum is a no-op value.
*
* As you might have already guessed: you cannot read a no-op value.
*/
UBJSON_TYPE_NOOP,
/**
* @brief The next datum is a boolean.
*
* The `content` is the boolean value.
* Use ubjson_get_bool() or use `content` verbatim.
*/
UBJSON_TYPE_BOOL,
/**
* @brief The next datum is an integer that fits into an int32_t.
*
* Use ubjson_get_i32() to read the value.
* `content` is one of ::ubjson_int32_type_t.
*/
UBJSON_TYPE_INT32,
/**
* @brief The next datum is an integer that fits into an int64_t.
*
* Use ubjson_get_i64() to read the value.
*/
UBJSON_TYPE_INT64,
/**
* @brief The next datum is a 32 bit floating point value.
*
* Use ubjson_get_float() to read the value.
*/
UBJSON_TYPE_FLOAT,
/**
* @brief The next datum is a 64 bit floating point value.
*
* Use ubjson_get_double() to read the value.
*/
UBJSON_TYPE_DOUBLE,
/* NOTE: High-precision numbers are not implemented, yet. Implement if needed. */
/* UBJSON_TYPE_HP_NUMBER, */
/**
* @brief The next datum is a string (blob).
*
* Use ubjson_get_string() to read the value.
* `content` is the length of the blob.
*/
UBJSON_TYPE_STRING,
/**
* @brief The next datum is an array.
*
* Use ubjson_read_array() to read its contents.
*/
UBJSON_ENTER_ARRAY,
/**
* @brief The next datum is an object.
*
* Use ubjson_read_object() to read its contents.
*/
UBJSON_ENTER_OBJECT,
/**
* @brief The next datum is an array index.
*
* This value is emitted for every index in a call to ubjson_read_array().
*
* `content1` is the array index.
* `type2` and `content2` describe the value of the index.
*
* Arrays can be nested.
*/
UBJSON_INDEX,
/**
* @brief The next datum is an object key.
*
* This value is emitted for every index in a call to ubjson_read_object().
*
* `content1` is the length of the key, invoke ubjson_get_string().
* `type2` and `content2` describe the value.
*
* Objects can be nested.
*/
UBJSON_KEY,
} ubjson_type_t;
/**
* @brief Length of the UBJSON_TYPE_INT32 datum.
*/
typedef enum {
UBJSON_INT32_INT8, /**< The stream contains an int8_t. */
UBJSON_INT32_UINT8, /**< The stream contains an uint8_t. */
UBJSON_INT32_INT16, /**< The stream contains an int16_t. */
UBJSON_INT32_INT32, /**< The stream contains an int32_t. */
} ubjson_int32_type_t;
/**
* @brief Return value of ::ubjson_read_callback_t and ubjson_read()
*
* The callback invoked by ubjson_read(), ubjson_read_array() or ubjson_read_object() can return an error value.
* The error value is then returned by the read function.
*
* The values UBJSON_INVALID_DATA, UBJSON_PREMATURELY_ENDED, and UBJSON_SIZE_ERROR are returned on encoding errors, too.
*/
typedef enum {
UBJSON_OKAY, /**< success / do continue */
UBJSON_ABORTED, /**< aborted / do abort */
UBJSON_INVALID_DATA, /**< invalid marker or type*/
UBJSON_PREMATURELY_ENDED, /**< the stream abruptly ended */
UBJSON_SIZE_ERROR, /**< the length of a field exceeded SSIZE_MAX */
} ubjson_read_callback_result_t;
struct ubjson_cookie;
/**
* @brief A cookie passed between the read and write functions.
* @details You probably want to wrap the cookie in some other data structure,
* which you retrieve with container_of() in the callback.
*/
typedef struct ubjson_cookie ubjson_cookie_t;
/**
* @brief Method called by ubjson_read() to get more data.
* @param[in] cookie The cookie that was passed to ubjson_read().
* @param[out] buf The buffer that should be written to.
* @param[in] max_len The length of the buffer. Always `>= 1`.
* @return @arg `< 0` on error. UBJSON_PREMATURELY_ENDED will be return by ubjson_read().
* @arg `> 0` the amount of read data, which must not exceed max_len.
*/
typedef ssize_t (*ubjson_read_t)(ubjson_cookie_t *__restrict cookie, void *buf, size_t max_len);
/**
* @brief Method called by ubjson_read() to denote the next element in the structure.
* @details Depending on the value of type1 a different function, such as ubjson_get_i32(),
* must be invoked by the callback function.
*
* With ubjson_read_array() or ubjson_read_object() the value of type1 is
* UBJSON_INDEX or UBJSON_KEY, resp.
* @param[in] cookie The cookie that was passed to ubjson_read().
* @param[in] type1 The type of the next datum.
* @param[in] content1 The sub-type of the next datum.
* @param[in] type2 The type of the value that belongs to the next key/index, or UBJSON_ABSENT.
* @param[in] content2 The sub-type of the value that belongs to the next key/index.
* @returns Either UBJSON_OKAY or UBJSON_ABORTED.
*/
typedef ubjson_read_callback_result_t (*ubjson_read_callback_t)(ubjson_cookie_t *__restrict cookie,
ubjson_type_t type1, ssize_t content1,
ubjson_type_t type2, ssize_t content2);
/**
* @brief Method called by ubjson_write_null() and friends.
* @details The function in invoked multiple times per written value.
* You should use some kind of buffer if you send the data over a stream.
*
* The function must write the whole buffer before returning.
* @param[in] cookie The cookie that was passed to ubjson_write_init().
* @param[in] buf Data to write, never NULL.
* @param[in] len Data to write, always >= 0.
* @returns @arg `< 0` to indicate an error.
* @arg `> 0` to indicate success.
*/
typedef ssize_t (*ubjson_write_t)(ubjson_cookie_t *__restrict cookie, const void *buf, size_t len);
/**
* @brief See @ref ubjson_cookie_t.
*/
struct ubjson_cookie {
/**
* @brief Read/write function
* @internal
*/
union {
ubjson_read_t read; /**< read function */
ubjson_write_t write; /**< write function */
} rw;
/**
* @brief Callback function
* @internal
*/
union {
ubjson_read_callback_t read; /**< Callback when a datum was read. */
} callback; /**< @internal */
/**
* @brief One byte push-back buffer.
* @internal
*/
char marker;
};
/**
* @brief Used to read with a setup cookie.
* @details You need to use this function instead of ubjson_read() only if
* your UBJSON data contains further UBJSON serialized data in a string.
*
* UBJSON data in a typed array may or may not work.
* @param[in] cookie The cookie that is passed to the callback function.
* @returns The same as ubjson_read().
*/
ubjson_read_callback_result_t ubjson_read_next(ubjson_cookie_t *__restrict cookie);
/**
* @brief The entry function to read UBJSON serialized data.
* @details This function invokes the callback function.
* The value of type1 in the callback indicates which function
* to call inside the callback function, e.g. ubjson_get_i32().
*
* Nested calls to ubjson_read_array(), ubjson_read_object() or ubjson_read_next()
* invoke the same callback function, possibly multiple times.
*
* You probably want to wrap the cookie in some other data structure,
* which you retrieve with container_of() in the callback.
* @param[in] cookie The cookie that is passed to the callback function.
* @param[in] read The function that is called to receive more data.
* @param[in] callback The callback function.
* @returns See \ref ubjson_read_callback_result_t
*/
static inline ubjson_read_callback_result_t ubjson_read(ubjson_cookie_t *__restrict cookie,
ubjson_read_t read,
ubjson_read_callback_t callback)
{
cookie->rw.read = read;
cookie->callback.read = callback;
cookie->marker = 0;
return ubjson_read_next(cookie);
}
/**
* @brief Use in a callback if type1 is UBJSON_KEY or UBJSON_INDEX.
* @details Call like ``ubjson_peek_value(cookie, &type2, &content2)``.
* @param[in] cookie The cookie that was passed to the callback.
* @param[in,out] type Pointer to a variable that was initialized with the value of type2, returns the new type1.
* @param[in,out] content Pointer to a variable that was initialized with the value of content2, returns the new content1.
* @returns The same as ubjson_read().
*/
ubjson_read_callback_result_t ubjson_peek_value(ubjson_cookie_t *__restrict cookie,
ubjson_type_t *type, ssize_t *content);
/**
* @brief Call if type1 of the callback was UBJSON_TYPE_INT32.
* @details The value of content1 is one of ubjson_int32_type_t.
* @param[in] cookie The cookie that was passed to the callback function.
* @param[in] content The content1 that was passed to the callback function.
* @param[out] dest The read datum.
* @returns The result of the read callback, probably the amount of read bytes.
*/
ssize_t ubjson_get_i32(ubjson_cookie_t *__restrict cookie, ssize_t content, int32_t *dest);
/**
* @brief Call if type1 of the callback was UBJSON_TYPE_INT64.
* @param[in] cookie The cookie that was passed to the callback function.
* @param[in] content The content1 that was passed to the callback function.
* @param[out] dest The read datum.
* @returns The result of the read callback, probably the amount of read bytes.
*/
ssize_t ubjson_get_i64(ubjson_cookie_t *__restrict cookie, ssize_t content, int64_t *dest);
/**
* @brief Call if type1 of the callback was UBJSON_TYPE_STRING.
* @details content1 is the length of the string/blob.
* The result is not null-terminated!
* @param[in] cookie The cookie that was passed to the callback function.
* @param[in] content The content1 that was passed to the callback function.
* @param[out] dest Buffer to read the string into.
* @returns The result of the read callback, probably the amount of read bytes.
*/
ssize_t ubjson_get_string(ubjson_cookie_t *__restrict cookie, ssize_t content, void *dest);
/**
* @brief Call if type1 of the callback was UBJSON_TYPE_BOOL.
* @details content1 is the value of the bool. The function only exists for symmetry.
* @param[in] cookie The cookie that was passed to the callback function.
* @param[in] content The content1 that was passed to the callback function.
* @param[out] dest The read datum.
* @returns `1`, the invocation cannot fail.
*/
static inline ssize_t ubjson_get_bool(ubjson_cookie_t *__restrict cookie, ssize_t content, bool *dest)
{
(void) cookie;
*dest = content;
return 1;
}
/**
* @brief Call if type1 of the callback was UBJSON_TYPE_FLOAT.
* @param[in] cookie The cookie that was passed to the callback function.
* @param[in] content The content1 that was passed to the callback function.
* @param[out] dest The read datum.
* @returns The result of the read callback, probably the amount of read bytes.
*/
static inline ssize_t ubjson_get_float(ubjson_cookie_t *__restrict cookie, ssize_t content, float *dest)
{
(void) content;
union {
float f;
int32_t i;
} value;
ubjson_read_callback_result_t result = ubjson_get_i32(cookie, UBJSON_INT32_INT32, &value.i);
*dest = value.f;
return result;
}
/**
* @brief Call if type1 of the callback was UBJSON_TYPE_DOUBLE.
* @param[in] cookie The cookie that was passed to the callback function.
* @param[in] content The content1 that was passed to the callback function.
* @param[out] dest The read datum.
* @returns The result of the read callback, probably the amount of read bytes.
*/
static inline ssize_t ubjson_get_double(ubjson_cookie_t *__restrict cookie, ssize_t content, double *dest)
{
(void) content;
union {
double f;
int64_t i;
} value;
ubjson_read_callback_result_t result = ubjson_get_i64(cookie, -1, &value.i);
*dest = value.f;
return result;
}
/**
* @brief Call if type1 of the callback was UBJSON_ENTER_ARRAY.
* @details Inside this call the callback function will be invoked multiple times,
* once per array element, with type1=UBJSON_INDEX,
* and content1=running index in the array.
*
* Use ubjson_peek_value() to determine the type of the element.
* @param[in] cookie The cookie that was passed to the callback function.
* @returns The same as ubjson_read().
*/
ubjson_read_callback_result_t ubjson_read_array(ubjson_cookie_t *__restrict cookie);
/**
* @brief Call if type1 of the callback was UBJSON_ENTER_OBJECT.
* @details Inside this call the callback function will be invoked multiple times,
* once per object element, with type1=UBJSON_KEY,
* and content1=length of the key string.
*
* First read the key with ubjson_get_string(), then
* use ubjson_peek_value() to determine the type of the element.
* @param[in] cookie The cookie that was passed to the callback function.
* @returns The same as ubjson_read().
*/
ubjson_read_callback_result_t ubjson_read_object(ubjson_cookie_t *__restrict cookie);
/* ***************************************************************************
* WRITE FUNCTIONS / DEFINITIONS
*************************************************************************** */
/**
* @brief The first call when you serialize data to UBJSON.
* @details There is no corresponding "ubjson_write_finish" function.
* The programmer needs to ensure that the API is used correctly.
* The library won't complain if you write multiple values that are not
* inside an array or object. The result will just not be properly serialized.
* @param[out] cookie The cookie that will be passed to ubjson_write_null() and friends.
* @param[in] write_fun The function that will be called to write data.
*/
static inline void ubjson_write_init(ubjson_cookie_t *__restrict cookie, ubjson_write_t write_fun)
{
cookie->rw.write = write_fun;
}
/**
* @brief Write a null value.
* @param[in] cookie The cookie that was initialized with ubjson_write_init().
*/
ssize_t ubjson_write_null(ubjson_cookie_t *__restrict cookie);
/**
* @brief Write a no-operation value.
* @param[in] cookie The cookie that was initialized with ubjson_write_init().
* @returns The result of the supplied @ref ubjson_write_t function.
*/
ssize_t ubjson_write_noop(ubjson_cookie_t *__restrict cookie);
/**
* @brief Write a boolean value.
* @param[in] cookie The cookie that was initialized with ubjson_write_init().
* @param[in] value The boolean value to write.
* @returns The result of the supplied @ref ubjson_write_t function.
*/
ssize_t ubjson_write_bool(ubjson_cookie_t *__restrict cookie, bool value);
/**
* @brief Write an integer value.
* @details The library will determine the smallest serialization for the value itself.
* @param[in] cookie The cookie that was initialized with ubjson_write_init().
* @param[in] value The integer value to write.
*/
ssize_t ubjson_write_i32(ubjson_cookie_t *__restrict cookie, int32_t value);
/**
* @brief Write an integer value.
* @details The library will determine the smallest serialization for the value itself.
* @param[in] cookie The cookie that was initialized with ubjson_write_init().
* @param[in] value The integer value to write.
* @returns The result of the supplied @ref ubjson_write_t function.
*/
ssize_t ubjson_write_i64(ubjson_cookie_t *__restrict cookie, int64_t value);
/**
* @brief Write a floating point value.
* @param[in] cookie The cookie that was initialized with ubjson_write_init().
* @param[in] value The integer value to write.
* @returns The result of the supplied @ref ubjson_write_t function.
*/
ssize_t ubjson_write_float(ubjson_cookie_t *__restrict cookie, float value);
/**
* @brief Write a floating point value.
* @param[in] cookie The cookie that was initialized with ubjson_write_init().
* @param[in] value The integer value to write.
* @returns The result of the supplied @ref ubjson_write_t function.
*/
ssize_t ubjson_write_double(ubjson_cookie_t *__restrict cookie, double value);
/**
* @brief Write a string or blob.
* @param[in] cookie The cookie that was initialized with ubjson_write_init().
* @param[in] value The string or blob to write.
* @param[in] len The length of the string or blob.
* @returns The result of the supplied @ref ubjson_write_t function.
*/
ssize_t ubjson_write_string(ubjson_cookie_t *__restrict cookie, const void *value, size_t len);
/**
* @brief Open an array.
* @details Write multiple elements inside this array.
* Call ubjson_close_array() after the whole content was written.
* @param[in] cookie The cookie that was initialized with ubjson_write_init().
* @returns The result of the supplied @ref ubjson_write_t function.
*/
ssize_t ubjson_open_array(ubjson_cookie_t *__restrict cookie);
/**
* @brief Open an array with a known length.
* @details Do not call ubjson_close_array().
* @param[in] cookie The cookie that was initialized with ubjson_write_init().
* @param[in] len Length of the array.
* @returns The result of the supplied @ref ubjson_write_t function.
*/
ssize_t ubjson_open_array_len(ubjson_cookie_t *__restrict cookie, size_t len);
/**
* @brief Close an array that was opened with ubjson_open_array().
* @param[in] cookie The cookie that was initialized with ubjson_write_init().
* @returns The result of the supplied @ref ubjson_write_t function.
*/
ssize_t ubjson_close_array(ubjson_cookie_t *__restrict cookie);
/**
* @brief Open an object.
* @details Write multiple keys inside this object.
* Call ubjson_close_object() after the whole content was written.
*
* For each element first write the key with ubjson_write_key(),
* then invoke the function to write the value.
* @param[in] cookie The cookie that was initialized with ubjson_write_init().
* @returns The result of the supplied @ref ubjson_write_t function.
*/
ssize_t ubjson_open_object(ubjson_cookie_t *__restrict cookie);
/**
* @brief Open an object with a known length.
* @details For each element first write the key with ubjson_write_key(),
* then invoke the function to write the value.
*
* Do not call ubjson_close_object().
* @param[in] cookie The cookie that was initialized with ubjson_write_init().
* @param[in] len Number of keys inside the object.
* @returns The result of the supplied @ref ubjson_write_t function.
*/
ssize_t ubjson_open_object_len(ubjson_cookie_t *__restrict cookie, size_t len);
/**
* @brief Write a key inside an object.
* @details For each element first write the key,
* then invoke the function to write the value.
*
* It is up to the programmer to ensure that there are no duplicated keys.
* @param[in] cookie The cookie that was initialized with ubjson_write_init().
* @param[in] value The key, should be a UTF-8 string.
* @param[in] len The length of the key.
* @returns The result of the supplied @ref ubjson_write_t function.
*/
ssize_t ubjson_write_key(ubjson_cookie_t *__restrict cookie, const void *value, size_t len);
/**
* @brief Close an array that was opened with ubjson_open_object().
* @param[in] cookie The cookie that was initialized with ubjson_write_init().
* @returns The result of the supplied @ref ubjson_write_t function.
*/
ssize_t ubjson_close_object(ubjson_cookie_t *__restrict cookie);
#ifdef __cplusplus
}
#endif
#endif /* UBJSON_H */
/** @} */