lwjson.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. /**
  2. * \file lwjson.h
  3. * \brief LwJSON - Lightweight JSON format parser
  4. */
  5. /*
  6. * Copyright (c) 2024 Tilen MAJERLE
  7. *
  8. * Permission is hereby granted, free of charge, to any person
  9. * obtaining a copy of this software and associated documentation
  10. * files (the "Software"), to deal in the Software without restriction,
  11. * including without limitation the rights to use, copy, modify, merge,
  12. * publish, distribute, sublicense, and/or sell copies of the Software,
  13. * and to permit persons to whom the Software is furnished to do so,
  14. * subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be
  17. * included in all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  20. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  21. * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
  22. * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  23. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  24. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  25. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  26. * OTHER DEALINGS IN THE SOFTWARE.
  27. *
  28. * This file is part of LwJSON - Lightweight JSON format parser.
  29. *
  30. * Author: Tilen MAJERLE <tilen@majerle.eu>
  31. * Version: v1.8.1
  32. */
  33. #ifndef LWJSON_HDR_H
  34. #define LWJSON_HDR_H
  35. #include <stdint.h>
  36. #include <string.h>
  37. #include "lwjson_opt.h"
  38. #ifdef __cplusplus
  39. extern "C" {
  40. #endif /* __cplusplus */
  41. /**
  42. * \defgroup LWJSON Lightweight JSON format parser
  43. * \brief LwJSON - Lightweight JSON format parser
  44. * \{
  45. */
  46. /**
  47. * \brief Get size of statically allocated array
  48. * \param[in] x: Object to get array size of
  49. * \return Number of elements in array
  50. */
  51. #define LWJSON_ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0]))
  52. /**
  53. * \brief List of supported JSON types
  54. */
  55. typedef enum {
  56. LWJSON_TYPE_STRING, /*!< String/Text format. Everything that has beginning and ending quote character */
  57. LWJSON_TYPE_NUM_INT, /*!< Number type for integer */
  58. LWJSON_TYPE_NUM_REAL, /*!< Number type for real number */
  59. LWJSON_TYPE_OBJECT, /*!< Object data type */
  60. LWJSON_TYPE_ARRAY, /*!< Array data type */
  61. LWJSON_TYPE_TRUE, /*!< True boolean value */
  62. LWJSON_TYPE_FALSE, /*!< False boolean value */
  63. LWJSON_TYPE_NULL, /*!< Null value */
  64. } lwjson_type_t;
  65. #if defined(LWJSON_DEV)
  66. extern const char* const lwjson_type_strings[];
  67. #endif
  68. /**
  69. * \brief Real data type
  70. */
  71. typedef LWJSON_CFG_REAL_TYPE lwjson_real_t;
  72. /**
  73. * \brief Integer data type
  74. */
  75. typedef LWJSON_CFG_INT_TYPE lwjson_int_t;
  76. /**
  77. * \brief JSON token
  78. */
  79. typedef struct lwjson_token {
  80. struct lwjson_token* next; /*!< Next token on a list */
  81. lwjson_type_t type; /*!< Token type */
  82. const char* token_name; /*!< Token name (if exists) */
  83. size_t token_name_len; /*!< Length of token name (this is needed to support const input strings to parse) */
  84. union {
  85. struct {
  86. const char* token_value; /*!< Pointer to the beginning of the string */
  87. size_t
  88. token_value_len; /*!< Length of token value (this is needed to support const input strings to parse) */
  89. } str; /*!< String data */
  90. lwjson_real_t num_real; /*!< Real number format */
  91. lwjson_int_t num_int; /*!< Int number format */
  92. struct lwjson_token* first_child; /*!< First children object for object or array type */
  93. } u; /*!< Union with different data types */
  94. } lwjson_token_t;
  95. /**
  96. * \brief JSON result enumeration
  97. */
  98. typedef enum {
  99. lwjsonOK = 0x00, /*!< Function returns successfully */
  100. lwjsonERR, /*!< Generic error message */
  101. lwjsonERRJSON, /*!< Error JSON format */
  102. lwjsonERRMEM, /*!< Memory error */
  103. lwjsonERRPAR, /*!< Parameter error */
  104. lwjsonSTREAMWAITFIRSTCHAR, /*!< Streaming parser did not yet receive first valid character
  105. indicating start of JSON sequence */
  106. lwjsonSTREAMDONE, /*!< Streaming parser is done,
  107. closing character matched the stream opening one */
  108. lwjsonSTREAMINPROG, /*!< Stream parsing is still in progress */
  109. } lwjsonr_t;
  110. /**
  111. * \brief LwJSON instance
  112. */
  113. typedef struct {
  114. lwjson_token_t* tokens; /*!< Pointer to array of tokens */
  115. size_t tokens_len; /*!< Size of all tokens */
  116. size_t next_free_token_pos; /*!< Position of next free token instance */
  117. lwjson_token_t first_token; /*!< First token on a list */
  118. struct {
  119. uint8_t parsed : 1; /*!< Flag indicating JSON parsing has finished successfully */
  120. } flags; /*!< List of flags */
  121. } lwjson_t;
  122. lwjsonr_t lwjson_init(lwjson_t* lwobj, lwjson_token_t* tokens, size_t tokens_len);
  123. lwjsonr_t lwjson_parse_ex(lwjson_t* lwobj, const void* json_data, size_t len);
  124. lwjsonr_t lwjson_parse(lwjson_t* lwobj, const char* json_str);
  125. const lwjson_token_t* lwjson_find(lwjson_t* lwobj, const char* path);
  126. const lwjson_token_t* lwjson_find_ex(lwjson_t* lwobj, const lwjson_token_t* token, const char* path);
  127. lwjsonr_t lwjson_free(lwjson_t* lwobj);
  128. void lwjson_print_token(const lwjson_token_t* token);
  129. void lwjson_print_json(const lwjson_t* lwobj);
  130. /**
  131. * \brief Object type for streaming parser
  132. */
  133. typedef enum {
  134. LWJSON_STREAM_TYPE_NONE, /*!< No entry - not used */
  135. LWJSON_STREAM_TYPE_OBJECT, /*!< Object indication */
  136. LWJSON_STREAM_TYPE_OBJECT_END, /*!< Object end indication */
  137. LWJSON_STREAM_TYPE_ARRAY, /*!< Array indication */
  138. LWJSON_STREAM_TYPE_ARRAY_END, /*!< Array end indication */
  139. LWJSON_STREAM_TYPE_KEY, /*!< Key string */
  140. LWJSON_STREAM_TYPE_STRING, /*!< Strin type */
  141. LWJSON_STREAM_TYPE_TRUE, /*!< True primitive */
  142. LWJSON_STREAM_TYPE_FALSE, /*!< False primitive */
  143. LWJSON_STREAM_TYPE_NULL, /*!< Null primitive */
  144. LWJSON_STREAM_TYPE_NUMBER, /*!< Generic number */
  145. } lwjson_stream_type_t;
  146. /**
  147. * \brief Stream parsing stack object
  148. */
  149. typedef struct {
  150. lwjson_stream_type_t type; /*!< Streaming type - current value */
  151. union {
  152. char name[LWJSON_CFG_STREAM_KEY_MAX_LEN
  153. + 1]; /*!< Last known key name, used only for \ref LWJSON_STREAM_TYPE_KEY type */
  154. uint16_t index; /*!< Current index when type is an array */
  155. } meta; /*!< Meta information */
  156. } lwjson_stream_stack_t;
  157. typedef enum {
  158. LWJSON_STREAM_STATE_WAITINGFIRSTCHAR = 0x00, /*!< State to wait for very first opening character */
  159. LWJSON_STREAM_STATE_PARSING, /*!< In parsing of the first char state - detecting next character state */
  160. LWJSON_STREAM_STATE_PARSING_STRING, /*!< Parse string primitive */
  161. LWJSON_STREAM_STATE_PARSING_PRIMITIVE, /*!< Parse any primitive that is non-string, either "true", "false", "null" or a number */
  162. LWJSON_STREAM_STATE_EXPECTING_COMMA_OR_END, /*!< Expecting ',', '}' or ']' */
  163. LWJSON_STREAM_STATE_EXPECTING_COLON, /*!< Expecting ':' */
  164. } lwjson_stream_state_t;
  165. /* Forward declaration */
  166. struct lwjson_stream_parser;
  167. /**
  168. * \brief Callback function for various events
  169. *
  170. */
  171. typedef void (*lwjson_stream_parser_callback_fn)(struct lwjson_stream_parser* jsp, lwjson_stream_type_t type);
  172. /**
  173. * \brief LwJSON streaming structure
  174. */
  175. typedef struct lwjson_stream_parser {
  176. lwjson_stream_stack_t
  177. stack[LWJSON_CFG_STREAM_STACK_SIZE]; /*!< Stack used for parsing. TODO: Add conditional compilation flag */
  178. size_t stack_pos; /*!< Current stack position */
  179. lwjson_stream_state_t parse_state; /*!< Parser state */
  180. lwjson_stream_parser_callback_fn evt_fn; /*!< Event function for user */
  181. void* user_data; /*!< User data for callback function */
  182. /* State */
  183. union {
  184. struct {
  185. char buff[LWJSON_CFG_STREAM_STRING_MAX_LEN
  186. + 1]; /*!< Buffer to write temporary data. TODO: Size to be variable with define */
  187. size_t buff_pos; /*!< Buffer position for next write (length of bytes in buffer) */
  188. size_t buff_total_pos; /*!< Total buffer position used up to now (in several data chunks) */
  189. uint8_t is_last; /*!< Status indicates if this is the last part of the string */
  190. } str; /*!< String structure. It is only used for keys and string objects.
  191. Use primitive part for all other options */
  192. struct {
  193. char buff[LWJSON_CFG_STREAM_PRIMITIVE_MAX_LEN + 1]; /*!< Temporary write buffer */
  194. size_t buff_pos; /*!< Buffer position for next write */
  195. } prim; /*!< Primitive object. Used for all types, except key or string */
  196. /* Todo: Add other types */
  197. } data; /*!< Data union used to parse various */
  198. char prev_c; /*!< History of characters */
  199. } lwjson_stream_parser_t;
  200. lwjsonr_t lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn);
  201. lwjsonr_t lwjson_stream_set_user_data(lwjson_stream_parser_t* jsp, void* user_data);
  202. void* lwjson_stream_get_user_data(lwjson_stream_parser_t* jsp);
  203. lwjsonr_t lwjson_stream_reset(lwjson_stream_parser_t* jsp);
  204. lwjsonr_t lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c);
  205. /**
  206. * \brief Get number of tokens used to parse JSON
  207. * \param[in] lwobj: Pointer to LwJSON instance
  208. * \return Number of tokens used to parse JSON
  209. */
  210. #define lwjson_get_tokens_used(lwobj) (((lwobj) != NULL) ? ((lwobj)->next_free_token_pos + 1) : 0)
  211. /**
  212. * \brief Get very first token of LwJSON instance
  213. * \param[in] lwobj: Pointer to LwJSON instance
  214. * \return Pointer to first token
  215. */
  216. #define lwjson_get_first_token(lwobj) (((lwobj) != NULL) ? (&(lwobj)->first_token) : NULL)
  217. /**
  218. * \brief Get token value for \ref LWJSON_TYPE_NUM_INT type
  219. * \param[in] token: token with integer type
  220. * \return Int number if type is integer, `0` otherwise
  221. */
  222. #define lwjson_get_val_int(token) \
  223. ((lwjson_int_t)(((token) != NULL && (token)->type == LWJSON_TYPE_NUM_INT) ? (token)->u.num_int : 0))
  224. /**
  225. * \brief Get token value for \ref LWJSON_TYPE_NUM_REAL type
  226. * \param[in] token: token with real type
  227. * \return Real numbeer if type is real, `0` otherwise
  228. */
  229. #define lwjson_get_val_real(token) \
  230. ((lwjson_real_t)(((token) != NULL && (token)->type == LWJSON_TYPE_NUM_REAL) ? (token)->u.num_real : 0))
  231. /**
  232. * \brief Get first child token for \ref LWJSON_TYPE_OBJECT or \ref LWJSON_TYPE_ARRAY types
  233. * \param[in] token: token with integer type
  234. * \return Pointer to first child or `NULL` if parent token is not object or array
  235. */
  236. #define lwjson_get_first_child(token) \
  237. (const void*)(((token) != NULL && ((token)->type == LWJSON_TYPE_OBJECT || (token)->type == LWJSON_TYPE_ARRAY)) \
  238. ? (token)->u.first_child \
  239. : NULL)
  240. /**
  241. * \brief Get string value from JSON token
  242. * \param[in] token: Token with string type
  243. * \param[out] str_len: Pointer to variable holding length of string.
  244. * Set to `NULL` if not used
  245. * \return Pointer to string or `NULL` if invalid token type
  246. */
  247. static inline const char*
  248. lwjson_get_val_string(const lwjson_token_t* token, size_t* str_len) {
  249. if (token != NULL && token->type == LWJSON_TYPE_STRING) {
  250. if (str_len != NULL) {
  251. *str_len = token->u.str.token_value_len;
  252. }
  253. return token->u.str.token_value;
  254. }
  255. return NULL;
  256. }
  257. /**
  258. * \brief Get length of string for \ref LWJSON_TYPE_STRING token type
  259. * \param[in] token: token with string type
  260. * \return Length of string in units of bytes
  261. */
  262. #define lwjson_get_val_string_length(token) \
  263. ((size_t)(((token) != NULL && (token)->type == LWJSON_TYPE_STRING) ? (token)->u.str.token_value_len : 0))
  264. /**
  265. * \brief Compare string token with user input string for a case-sensitive match
  266. * \param[in] token: Token with string type
  267. * \param[in] str: NULL-terminated string to compare
  268. * \return `1` if equal, `0` otherwise
  269. */
  270. static inline uint8_t
  271. lwjson_string_compare(const lwjson_token_t* token, const char* str) {
  272. if (token != NULL && token->type == LWJSON_TYPE_STRING) {
  273. return strncmp(token->u.str.token_value, str, token->u.str.token_value_len) == 0;
  274. }
  275. return 0;
  276. }
  277. /**
  278. * \brief Compare string token with user input string for a case-sensitive match
  279. * \param[in] token: Token with string type
  280. * \param[in] str: NULL-terminated string to compare
  281. * \param[in] len: Length of the string in bytes
  282. * \return `1` if equal, `0` otherwise
  283. */
  284. static inline uint8_t
  285. lwjson_string_compare_n(const lwjson_token_t* token, const char* str, size_t len) {
  286. if (token != NULL && token->type == LWJSON_TYPE_STRING && len <= token->u.str.token_value_len) {
  287. return strncmp(token->u.str.token_value, str, len) == 0;
  288. }
  289. return 0;
  290. }
  291. /**
  292. * \name LWJSON_STREAM_SEQ
  293. * \brief Helper functions for stack analysis in a callback function
  294. * \note Useful exclusively for streaming functions
  295. * \{
  296. */
  297. /**
  298. * \brief Check the sequence of JSON stack, starting from start_number index
  299. * \note This applies only to one sequence element. Other macros, starting with
  300. * `lwjson_stack_seq_X` (where X is the sequence length), provide
  301. * more parameters for longer sequences.
  302. *
  303. * \param[in] jsp: LwJSON stream instance
  304. * \param[in] start_num: Start number in the stack. Typically starts with `0`, but user may choose another
  305. * number, if intention is to check partial sequence only
  306. * \param[in] sp0: Stream stack type. Value of \ref lwjson_stream_type_t, but only last part of the enum.
  307. * If user is interested in the \ref LWJSON_STREAM_TYPE_OBJECT,
  308. * you should only write `OBJECT` as parameter.
  309. * Idea behind is to make code smaller and easier to read, especially when
  310. * using other sequence values with more parameters.
  311. * \return `0` if sequence doesn't match, non-zero otherwise
  312. */
  313. #define lwjson_stack_seq_1(jsp, start_num, sp0) ((jsp)->stack[(start_num)].type == LWJSON_STREAM_TYPE_##sp0)
  314. #define lwjson_stack_seq_2(jsp, start_num, sp0, sp1) \
  315. (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) && lwjson_stack_seq_1((jsp), (start_num) + 1, sp1))
  316. #define lwjson_stack_seq_3(jsp, start_num, sp0, sp1, sp2) \
  317. (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) && lwjson_stack_seq_2((jsp), (start_num) + 1, sp1, sp2))
  318. #define lwjson_stack_seq_4(jsp, start_num, sp0, sp1, sp2, sp3) \
  319. (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) && lwjson_stack_seq_3((jsp), (start_num) + 1, sp1, sp2, sp3))
  320. #define lwjson_stack_seq_5(jsp, start_num, sp0, sp1, sp2, sp3, sp4) \
  321. (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) && lwjson_stack_seq_4((jsp), (start_num) + 1, sp1, sp2, sp3, sp4))
  322. #define lwjson_stack_seq_6(jsp, start_num, sp0, sp1, sp2, sp3, sp4, sp5) \
  323. (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) \
  324. && lwjson_stack_seq_5((jsp), (start_num) + 1, sp1, sp2, sp3, sp4, sp5))
  325. #define lwjson_stack_seq_7(jsp, start_num, sp0, sp1, sp2, sp3, sp4, sp5, sp6) \
  326. (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) \
  327. && lwjson_stack_seq_6((jsp), (start_num) + 1, sp1, sp2, sp3, sp4, sp5, sp6))
  328. #define lwjson_stack_seq_8(jsp, start_num, sp0, sp1, sp2, sp3, sp4, sp5, sp6, sp7) \
  329. (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) \
  330. && lwjson_stack_seq_7((jsp), (start_num) + 1, sp1, sp2, sp3, sp4, sp5, sp6, sp7))
  331. /**
  332. * \}
  333. */
  334. /**
  335. * \}
  336. */
  337. #ifdef __cplusplus
  338. }
  339. #endif /* __cplusplus */
  340. #endif /* LWJSON_HDR_H */