mirror of
				https://github.com/SuperBFG7/ympd
				synced 2025-10-30 21:33:00 +00:00 
			
		
		
		
	Add frozen
This commit is contained in:
		
							
								
								
									
										16
									
								
								src/frozen/LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/frozen/LICENSE
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com> | ||||
| Copyright (c) 2013 Cesanta Software Limited | ||||
| All rights reserved | ||||
|  | ||||
| This code is dual-licensed: you can redistribute it and/or modify | ||||
| it under the terms of the GNU General Public License version 2 as | ||||
| published by the Free Software Foundation. For the terms of this | ||||
| license, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| You are free to use this code under the terms of the GNU General | ||||
| Public License, but WITHOUT ANY WARRANTY; without even the implied | ||||
| warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||||
| See the GNU General Public License for more details. | ||||
|  | ||||
| Alternatively, you can license this code under a commercial | ||||
| license, as set out in <http://cesanta.com/>. | ||||
							
								
								
									
										994
									
								
								src/frozen/frozen.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										994
									
								
								src/frozen/frozen.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,994 @@ | ||||
| /* | ||||
|  * Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com> | ||||
|  * Copyright (c) 2013 Cesanta Software Limited | ||||
|  * All rights reserved | ||||
|  * | ||||
|  * This library is dual-licensed: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2 as | ||||
|  * published by the Free Software Foundation. For the terms of this | ||||
|  * license, see <http: *www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * You are free to use this library under the terms of the GNU General | ||||
|  * Public License, but WITHOUT ANY WARRANTY; without even the implied | ||||
|  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||||
|  * See the GNU General Public License for more details. | ||||
|  * | ||||
|  * Alternatively, you can license this library under a commercial | ||||
|  * license, as set out in <http://cesanta.com/products.html>. | ||||
|  */ | ||||
|  | ||||
| #define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005+ */ | ||||
|  | ||||
| #include "frozen.h" | ||||
| #include <ctype.h> | ||||
| #include <stdarg.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #if !defined(WEAK) | ||||
| #if (defined(__GNUC__) || defined(__TI_COMPILER_VERSION__)) && !defined(_WIN32) | ||||
| #define WEAK __attribute__((weak)) | ||||
| #else | ||||
| #define WEAK | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #define snprintf cs_win_snprintf | ||||
| #define vsnprintf cs_win_vsnprintf | ||||
| int cs_win_snprintf(char *str, size_t size, const char *format, ...); | ||||
| int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap); | ||||
| #if _MSC_VER >= 1700 | ||||
| #include <stdint.h> | ||||
| #else | ||||
| typedef _int64 int64_t; | ||||
| typedef unsigned _int64 uint64_t; | ||||
| #endif | ||||
| #define PRId64 "I64d" | ||||
| #define PRIu64 "I64u" | ||||
| #if !defined(SIZE_T_FMT) | ||||
| #if _MSC_VER >= 1310 | ||||
| #define SIZE_T_FMT "Iu" | ||||
| #else | ||||
| #define SIZE_T_FMT "u" | ||||
| #endif | ||||
| #endif | ||||
| #else /* _WIN32 */ | ||||
| /* <inttypes.h> wants this for C++ */ | ||||
| #ifndef __STDC_FORMAT_MACROS | ||||
| #define __STDC_FORMAT_MACROS | ||||
| #endif | ||||
| #include <inttypes.h> | ||||
| #if !defined(SIZE_T_FMT) | ||||
| #define SIZE_T_FMT "zu" | ||||
| #endif | ||||
| #endif /* _WIN32 */ | ||||
|  | ||||
| #define INT64_FMT PRId64 | ||||
| #define UINT64_FMT PRIu64 | ||||
|  | ||||
| #ifndef va_copy | ||||
| #define va_copy(x, y) x = y | ||||
| #endif | ||||
|  | ||||
| #ifndef JSON_MAX_PATH_LEN | ||||
| #define JSON_MAX_PATH_LEN 60 | ||||
| #endif | ||||
|  | ||||
| struct frozen { | ||||
|   const char *end; | ||||
|   const char *cur; | ||||
|  | ||||
|   const char *cur_name; | ||||
|   size_t cur_name_len; | ||||
|  | ||||
|   /* For callback API */ | ||||
|   char path[JSON_MAX_PATH_LEN]; | ||||
|   int path_len; | ||||
|   void *callback_data; | ||||
|   json_walk_callback_t callback; | ||||
| }; | ||||
|  | ||||
| struct fstate { | ||||
|   const char *ptr; | ||||
|   int path_len; | ||||
| }; | ||||
|  | ||||
| #define SET_STATE(fr, ptr, str, len)              \ | ||||
|   struct fstate fstate = {(ptr), (fr)->path_len}; \ | ||||
|   append_to_path((fr), (str), (len)); | ||||
|  | ||||
| #define CALL_BACK(fr, tok, value, len)                                        \ | ||||
|   do {                                                                        \ | ||||
|     if ((fr)->callback &&                                                     \ | ||||
|         ((fr)->path_len == 0 || (fr)->path[(fr)->path_len - 1] != '.')) {     \ | ||||
|       struct json_token t = {(value), (len), (tok)};                          \ | ||||
|                                                                               \ | ||||
|       /* Call the callback with the given value and current name */           \ | ||||
|       (fr)->callback((fr)->callback_data, (fr)->cur_name, (fr)->cur_name_len, \ | ||||
|                      (fr)->path, &t);                                         \ | ||||
|                                                                               \ | ||||
|       /* Reset the name */                                                    \ | ||||
|       (fr)->cur_name = NULL;                                                  \ | ||||
|       (fr)->cur_name_len = 0;                                                 \ | ||||
|     }                                                                         \ | ||||
|   } while (0) | ||||
|  | ||||
| static int append_to_path(struct frozen *f, const char *str, int size) { | ||||
|   int n = f->path_len; | ||||
|   f->path_len += | ||||
|       snprintf(f->path + f->path_len, sizeof(f->path) - (f->path_len + 1), | ||||
|                "%.*s", size, str); | ||||
|  | ||||
|   return n; | ||||
| } | ||||
|  | ||||
| static void truncate_path(struct frozen *f, int len) { | ||||
|   f->path_len = len; | ||||
|   f->path[len] = '\0'; | ||||
| } | ||||
|  | ||||
| static int parse_object(struct frozen *f); | ||||
| static int parse_value(struct frozen *f); | ||||
|  | ||||
| #define EXPECT(cond, err_code)      \ | ||||
|   do {                              \ | ||||
|     if (!(cond)) return (err_code); \ | ||||
|   } while (0) | ||||
|  | ||||
| #define TRY(expr)          \ | ||||
|   do {                     \ | ||||
|     int _n = expr;         \ | ||||
|     if (_n < 0) return _n; \ | ||||
|   } while (0) | ||||
|  | ||||
| #define END_OF_STRING (-1) | ||||
|  | ||||
| static int left(const struct frozen *f) { | ||||
|   return f->end - f->cur; | ||||
| } | ||||
|  | ||||
| static int is_space(int ch) { | ||||
|   return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'; | ||||
| } | ||||
|  | ||||
| static void skip_whitespaces(struct frozen *f) { | ||||
|   while (f->cur < f->end && is_space(*f->cur)) f->cur++; | ||||
| } | ||||
|  | ||||
| static int cur(struct frozen *f) { | ||||
|   skip_whitespaces(f); | ||||
|   return f->cur >= f->end ? END_OF_STRING : *(unsigned char *) f->cur; | ||||
| } | ||||
|  | ||||
| static int test_and_skip(struct frozen *f, int expected) { | ||||
|   int ch = cur(f); | ||||
|   if (ch == expected) { | ||||
|     f->cur++; | ||||
|     return 0; | ||||
|   } | ||||
|   return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; | ||||
| } | ||||
|  | ||||
| static int is_alpha(int ch) { | ||||
|   return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); | ||||
| } | ||||
|  | ||||
| static int is_digit(int ch) { | ||||
|   return ch >= '0' && ch <= '9'; | ||||
| } | ||||
|  | ||||
| static int is_hex_digit(int ch) { | ||||
|   return is_digit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); | ||||
| } | ||||
|  | ||||
| static int get_escape_len(const char *s, int len) { | ||||
|   switch (*s) { | ||||
|     case 'u': | ||||
|       return len < 6 ? JSON_STRING_INCOMPLETE | ||||
|                      : is_hex_digit(s[1]) && is_hex_digit(s[2]) && | ||||
|                                is_hex_digit(s[3]) && is_hex_digit(s[4]) | ||||
|                            ? 5 | ||||
|                            : JSON_STRING_INVALID; | ||||
|     case '"': | ||||
|     case '\\': | ||||
|     case '/': | ||||
|     case 'b': | ||||
|     case 'f': | ||||
|     case 'n': | ||||
|     case 'r': | ||||
|     case 't': | ||||
|       return len < 2 ? JSON_STRING_INCOMPLETE : 1; | ||||
|     default: | ||||
|       return JSON_STRING_INVALID; | ||||
|   } | ||||
| } | ||||
|  | ||||
| /* identifier = letter { letter | digit | '_' } */ | ||||
| static int parse_identifier(struct frozen *f) { | ||||
|   EXPECT(is_alpha(cur(f)), JSON_STRING_INVALID); | ||||
|   { | ||||
|     SET_STATE(f, f->cur, "", 0); | ||||
|     while (f->cur < f->end && | ||||
|            (*f->cur == '_' || is_alpha(*f->cur) || is_digit(*f->cur))) { | ||||
|       f->cur++; | ||||
|     } | ||||
|     truncate_path(f, fstate.path_len); | ||||
|     CALL_BACK(f, JSON_TYPE_STRING, fstate.ptr, f->cur - fstate.ptr); | ||||
|   } | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| static int get_utf8_char_len(unsigned char ch) { | ||||
|   if ((ch & 0x80) == 0) return 1; | ||||
|   switch (ch & 0xf0) { | ||||
|     case 0xf0: | ||||
|       return 4; | ||||
|     case 0xe0: | ||||
|       return 3; | ||||
|     default: | ||||
|       return 2; | ||||
|   } | ||||
| } | ||||
|  | ||||
| /* string = '"' { quoted_printable_chars } '"' */ | ||||
| static int parse_string(struct frozen *f) { | ||||
|   int n, ch = 0, len = 0; | ||||
|   TRY(test_and_skip(f, '"')); | ||||
|   { | ||||
|     SET_STATE(f, f->cur, "", 0); | ||||
|     for (; f->cur < f->end; f->cur += len) { | ||||
|       ch = *(unsigned char *) f->cur; | ||||
|       len = get_utf8_char_len((unsigned char) ch); | ||||
|       EXPECT(ch >= 32 && len > 0, JSON_STRING_INVALID); /* No control chars */ | ||||
|       EXPECT(len < left(f), JSON_STRING_INCOMPLETE); | ||||
|       if (ch == '\\') { | ||||
|         EXPECT((n = get_escape_len(f->cur + 1, left(f))) > 0, n); | ||||
|         len += n; | ||||
|       } else if (ch == '"') { | ||||
|         truncate_path(f, fstate.path_len); | ||||
|         CALL_BACK(f, JSON_TYPE_STRING, fstate.ptr, f->cur - fstate.ptr); | ||||
|         f->cur++; | ||||
|         break; | ||||
|       }; | ||||
|     } | ||||
|   } | ||||
|   return ch == '"' ? 0 : JSON_STRING_INCOMPLETE; | ||||
| } | ||||
|  | ||||
| /* number = [ '-' ] digit+ [ '.' digit+ ] [ ['e'|'E'] ['+'|'-'] digit+ ] */ | ||||
| static int parse_number(struct frozen *f) { | ||||
|   int ch = cur(f); | ||||
|   SET_STATE(f, f->cur, "", 0); | ||||
|   if (ch == '-') f->cur++; | ||||
|   EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); | ||||
|   EXPECT(is_digit(f->cur[0]), JSON_STRING_INVALID); | ||||
|   while (f->cur < f->end && is_digit(f->cur[0])) f->cur++; | ||||
|   if (f->cur < f->end && f->cur[0] == '.') { | ||||
|     f->cur++; | ||||
|     EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); | ||||
|     EXPECT(is_digit(f->cur[0]), JSON_STRING_INVALID); | ||||
|     while (f->cur < f->end && is_digit(f->cur[0])) f->cur++; | ||||
|   } | ||||
|   if (f->cur < f->end && (f->cur[0] == 'e' || f->cur[0] == 'E')) { | ||||
|     f->cur++; | ||||
|     EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); | ||||
|     if ((f->cur[0] == '+' || f->cur[0] == '-')) f->cur++; | ||||
|     EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); | ||||
|     EXPECT(is_digit(f->cur[0]), JSON_STRING_INVALID); | ||||
|     while (f->cur < f->end && is_digit(f->cur[0])) f->cur++; | ||||
|   } | ||||
|   truncate_path(f, fstate.path_len); | ||||
|   CALL_BACK(f, JSON_TYPE_NUMBER, fstate.ptr, f->cur - fstate.ptr); | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| /* array = '[' [ value { ',' value } ] ']' */ | ||||
| static int parse_array(struct frozen *f) { | ||||
|   int i = 0, current_path_len; | ||||
|   char buf[20]; | ||||
|   TRY(test_and_skip(f, '[')); | ||||
|   { | ||||
|     CALL_BACK(f, JSON_TYPE_ARRAY_START, NULL, 0); | ||||
|     { | ||||
|       SET_STATE(f, f->cur - 1, "", 0); | ||||
|       while (cur(f) != ']') { | ||||
|         snprintf(buf, sizeof(buf), "[%d]", i); | ||||
|         i++; | ||||
|         current_path_len = append_to_path(f, buf, strlen(buf)); | ||||
|         f->cur_name = | ||||
|             f->path + strlen(f->path) - strlen(buf) + 1 /*opening brace*/; | ||||
|         f->cur_name_len = strlen(buf) - 2 /*braces*/; | ||||
|         TRY(parse_value(f)); | ||||
|         truncate_path(f, current_path_len); | ||||
|         if (cur(f) == ',') f->cur++; | ||||
|       } | ||||
|       TRY(test_and_skip(f, ']')); | ||||
|       truncate_path(f, fstate.path_len); | ||||
|       CALL_BACK(f, JSON_TYPE_ARRAY_END, fstate.ptr, f->cur - fstate.ptr); | ||||
|     } | ||||
|   } | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| static int expect(struct frozen *f, const char *s, int len, | ||||
|                   enum json_token_type tok_type) { | ||||
|   int i, n = left(f); | ||||
|   SET_STATE(f, f->cur, "", 0); | ||||
|   for (i = 0; i < len; i++) { | ||||
|     if (i >= n) return JSON_STRING_INCOMPLETE; | ||||
|     if (f->cur[i] != s[i]) return JSON_STRING_INVALID; | ||||
|   } | ||||
|   f->cur += len; | ||||
|   truncate_path(f, fstate.path_len); | ||||
|  | ||||
|   CALL_BACK(f, tok_type, fstate.ptr, f->cur - fstate.ptr); | ||||
|  | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| /* value = 'null' | 'true' | 'false' | number | string | array | object */ | ||||
| static int parse_value(struct frozen *f) { | ||||
|   int ch = cur(f); | ||||
|  | ||||
|   switch (ch) { | ||||
|     case '"': | ||||
|       TRY(parse_string(f)); | ||||
|       break; | ||||
|     case '{': | ||||
|       TRY(parse_object(f)); | ||||
|       break; | ||||
|     case '[': | ||||
|       TRY(parse_array(f)); | ||||
|       break; | ||||
|     case 'n': | ||||
|       TRY(expect(f, "null", 4, JSON_TYPE_NULL)); | ||||
|       break; | ||||
|     case 't': | ||||
|       TRY(expect(f, "true", 4, JSON_TYPE_TRUE)); | ||||
|       break; | ||||
|     case 'f': | ||||
|       TRY(expect(f, "false", 5, JSON_TYPE_FALSE)); | ||||
|       break; | ||||
|     case '-': | ||||
|     case '0': | ||||
|     case '1': | ||||
|     case '2': | ||||
|     case '3': | ||||
|     case '4': | ||||
|     case '5': | ||||
|     case '6': | ||||
|     case '7': | ||||
|     case '8': | ||||
|     case '9': | ||||
|       TRY(parse_number(f)); | ||||
|       break; | ||||
|     default: | ||||
|       return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; | ||||
|   } | ||||
|  | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| /* key = identifier | string */ | ||||
| static int parse_key(struct frozen *f) { | ||||
|   int ch = cur(f); | ||||
| #if 0 | ||||
|   printf("%s [%.*s]\n", __func__, (int) (f->end - f->cur), f->cur); | ||||
| #endif | ||||
|   if (is_alpha(ch)) { | ||||
|     TRY(parse_identifier(f)); | ||||
|   } else if (ch == '"') { | ||||
|     TRY(parse_string(f)); | ||||
|   } else { | ||||
|     return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; | ||||
|   } | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| /* pair = key ':' value */ | ||||
| static int parse_pair(struct frozen *f) { | ||||
|   int current_path_len; | ||||
|   const char *tok; | ||||
|   skip_whitespaces(f); | ||||
|   tok = f->cur; | ||||
|   TRY(parse_key(f)); | ||||
|   { | ||||
|     f->cur_name = *tok == '"' ? tok + 1 : tok; | ||||
|     f->cur_name_len = *tok == '"' ? f->cur - tok - 2 : f->cur - tok; | ||||
|     current_path_len = append_to_path(f, f->cur_name, f->cur_name_len); | ||||
|   } | ||||
|   TRY(test_and_skip(f, ':')); | ||||
|   TRY(parse_value(f)); | ||||
|   truncate_path(f, current_path_len); | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| /* object = '{' pair { ',' pair } '}' */ | ||||
| static int parse_object(struct frozen *f) { | ||||
|   TRY(test_and_skip(f, '{')); | ||||
|   { | ||||
|     CALL_BACK(f, JSON_TYPE_OBJECT_START, NULL, 0); | ||||
|     { | ||||
|       SET_STATE(f, f->cur - 1, ".", 1); | ||||
|       while (cur(f) != '}') { | ||||
|         TRY(parse_pair(f)); | ||||
|         if (cur(f) == ',') f->cur++; | ||||
|       } | ||||
|       TRY(test_and_skip(f, '}')); | ||||
|       truncate_path(f, fstate.path_len); | ||||
|       CALL_BACK(f, JSON_TYPE_OBJECT_END, fstate.ptr, f->cur - fstate.ptr); | ||||
|     } | ||||
|   } | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| static int doit(struct frozen *f) { | ||||
|   if (f->cur == 0 || f->end < f->cur) return JSON_STRING_INVALID; | ||||
|   if (f->end == f->cur) return JSON_STRING_INCOMPLETE; | ||||
|   return parse_value(f); | ||||
| } | ||||
|  | ||||
| static int json_encode_string(struct json_out *out, const char *p, size_t len) { | ||||
|   size_t i, cl, n = 0; | ||||
|   const char *hex_digits = "0123456789abcdef"; | ||||
|   const char *specials = "btnvfr"; | ||||
|  | ||||
|   for (i = 0; i < len; i++) { | ||||
|     unsigned char ch = ((unsigned char *) p)[i]; | ||||
|     if (ch == '"' || ch == '\\') { | ||||
|       n += out->printer(out, "\\", 1); | ||||
|       n += out->printer(out, p + i, 1); | ||||
|     } else if (ch >= '\b' && ch <= '\r') { | ||||
|       n += out->printer(out, "\\", 1); | ||||
|       n += out->printer(out, &specials[ch - '\b'], 1); | ||||
|     } else if (isprint(ch)) { | ||||
|       n += out->printer(out, p + i, 1); | ||||
|     } else if ((cl = get_utf8_char_len(ch)) == 1) { | ||||
|       n += out->printer(out, "\\u00", 4); | ||||
|       n += out->printer(out, &hex_digits[(ch >> 4) % 0xf], 1); | ||||
|       n += out->printer(out, &hex_digits[ch % 0xf], 1); | ||||
|     } else { | ||||
|       n += out->printer(out, p + i, cl); | ||||
|       i += cl - 1; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return n; | ||||
| } | ||||
|  | ||||
| int json_printer_buf(struct json_out *out, const char *buf, size_t len) WEAK; | ||||
| int json_printer_buf(struct json_out *out, const char *buf, size_t len) { | ||||
|   size_t avail = out->u.buf.size - out->u.buf.len; | ||||
|   size_t n = len < avail ? len : avail; | ||||
|   memcpy(out->u.buf.buf + out->u.buf.len, buf, n); | ||||
|   out->u.buf.len += n; | ||||
|   if (out->u.buf.size > 0) { | ||||
|     size_t idx = out->u.buf.len; | ||||
|     if (idx >= out->u.buf.size) idx = out->u.buf.size - 1; | ||||
|     out->u.buf.buf[idx] = '\0'; | ||||
|   } | ||||
|   return len; | ||||
| } | ||||
|  | ||||
| int json_printer_file(struct json_out *out, const char *buf, size_t len) WEAK; | ||||
| int json_printer_file(struct json_out *out, const char *buf, size_t len) { | ||||
|   return fwrite(buf, 1, len, out->u.fp); | ||||
| } | ||||
|  | ||||
| static int b64idx(int c) { | ||||
|   if (c < 26) { | ||||
|     return c + 'A'; | ||||
|   } else if (c < 52) { | ||||
|     return c - 26 + 'a'; | ||||
|   } else if (c < 62) { | ||||
|     return c - 52 + '0'; | ||||
|   } else { | ||||
|     return c == 62 ? '+' : '/'; | ||||
|   } | ||||
| } | ||||
|  | ||||
| static int b64rev(int c) { | ||||
|   if (c >= 'A' && c <= 'Z') { | ||||
|     return c - 'A'; | ||||
|   } else if (c >= 'a' && c <= 'z') { | ||||
|     return c + 26 - 'a'; | ||||
|   } else if (c >= '0' && c <= '9') { | ||||
|     return c + 52 - '0'; | ||||
|   } else if (c == '+') { | ||||
|     return 62; | ||||
|   } else if (c == '/') { | ||||
|     return 63; | ||||
|   } else { | ||||
|     return 64; | ||||
|   } | ||||
| } | ||||
|  | ||||
| static uint8_t hexdec(const char *s) { | ||||
| #define HEXTOI(x) (x >= '0' && x <= '9' ? x - '0' : x - 'W') | ||||
|   int a = tolower(*(const unsigned char *) s); | ||||
|   int b = tolower(*(const unsigned char *) (s + 1)); | ||||
|   return (HEXTOI(a) << 4) | HEXTOI(b); | ||||
| } | ||||
|  | ||||
| static int b64enc(struct json_out *out, const unsigned char *p, int n) { | ||||
|   char buf[4]; | ||||
|   int i, len = 0; | ||||
|   for (i = 0; i < n; i += 3) { | ||||
|     int a = p[i], b = i + 1 < n ? p[i + 1] : 0, c = i + 2 < n ? p[i + 2] : 0; | ||||
|     buf[0] = b64idx(a >> 2); | ||||
|     buf[1] = b64idx((a & 3) << 4 | (b >> 4)); | ||||
|     buf[2] = b64idx((b & 15) << 2 | (c >> 6)); | ||||
|     buf[3] = b64idx(c & 63); | ||||
|     if (i + 1 >= n) buf[2] = '='; | ||||
|     if (i + 2 >= n) buf[3] = '='; | ||||
|     len += out->printer(out, buf, sizeof(buf)); | ||||
|   } | ||||
|   return len; | ||||
| } | ||||
|  | ||||
| static int b64dec(const char *src, int n, char *dst) { | ||||
|   const char *end = src + n; | ||||
|   int len = 0; | ||||
|   while (src + 3 < end) { | ||||
|     int a = b64rev(src[0]), b = b64rev(src[1]), c = b64rev(src[2]), | ||||
|         d = b64rev(src[3]); | ||||
|     dst[len++] = (a << 2) | (b >> 4); | ||||
|     if (src[2] != '=') { | ||||
|       dst[len++] = (b << 4) | (c >> 2); | ||||
|       if (src[3] != '=') { | ||||
|         dst[len++] = (c << 6) | d; | ||||
|       } | ||||
|     } | ||||
|     src += 4; | ||||
|   } | ||||
|   return len; | ||||
| } | ||||
|  | ||||
| int json_vprintf(struct json_out *out, const char *fmt, va_list xap) WEAK; | ||||
| int json_vprintf(struct json_out *out, const char *fmt, va_list xap) { | ||||
|   int len = 0; | ||||
|   const char *quote = "\"", *null = "null"; | ||||
|   va_list ap; | ||||
|   va_copy(ap, xap); | ||||
|  | ||||
|   while (*fmt != '\0') { | ||||
|     if (strchr(":, \r\n\t[]{}\"", *fmt) != NULL) { | ||||
|       len += out->printer(out, fmt, 1); | ||||
|       fmt++; | ||||
|     } else if (fmt[0] == '%') { | ||||
|       char buf[21]; | ||||
|       size_t skip = 2; | ||||
|  | ||||
|       if (fmt[1] == 'l' && fmt[2] == 'l' && (fmt[3] == 'd' || fmt[3] == 'u')) { | ||||
|         int64_t val = va_arg(ap, int64_t); | ||||
|         const char *fmt2 = fmt[3] == 'u' ? "%" UINT64_FMT : "%" INT64_FMT; | ||||
|         snprintf(buf, sizeof(buf), fmt2, val); | ||||
|         len += out->printer(out, buf, strlen(buf)); | ||||
|         skip += 2; | ||||
|       } else if (fmt[1] == 'z' && fmt[2] == 'u') { | ||||
|         size_t val = va_arg(ap, size_t); | ||||
|         snprintf(buf, sizeof(buf), "%" SIZE_T_FMT, val); | ||||
|         len += out->printer(out, buf, strlen(buf)); | ||||
|         skip += 1; | ||||
|       } else if (fmt[1] == 'M') { | ||||
|         json_printf_callback_t f = va_arg(ap, json_printf_callback_t); | ||||
|         len += f(out, &ap); | ||||
|       } else if (fmt[1] == 'B') { | ||||
|         int val = va_arg(ap, int); | ||||
|         const char *str = val ? "true" : "false"; | ||||
|         len += out->printer(out, str, strlen(str)); | ||||
|       } else if (fmt[1] == 'H') { | ||||
|         const char *hex = "0123456789abcdef"; | ||||
|         int i, n = va_arg(ap, int); | ||||
|         const unsigned char *p = va_arg(ap, const unsigned char *); | ||||
|         len += out->printer(out, quote, 1); | ||||
|         for (i = 0; i < n; i++) { | ||||
|           len += out->printer(out, &hex[(p[i] >> 4) & 0xf], 1); | ||||
|           len += out->printer(out, &hex[p[i] & 0xf], 1); | ||||
|         } | ||||
|         len += out->printer(out, quote, 1); | ||||
|       } else if (fmt[1] == 'V') { | ||||
|         const unsigned char *p = va_arg(ap, const unsigned char *); | ||||
|         int n = va_arg(ap, int); | ||||
|         len += out->printer(out, quote, 1); | ||||
|         len += b64enc(out, p, n); | ||||
|         len += out->printer(out, quote, 1); | ||||
|       } else if (fmt[1] == 'Q' || | ||||
|                  (fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 'Q')) { | ||||
|         size_t l = 0; | ||||
|         const char *p; | ||||
|  | ||||
|         if (fmt[1] == '.') { | ||||
|           l = (size_t) va_arg(ap, int); | ||||
|           skip += 2; | ||||
|         } | ||||
|         p = va_arg(ap, char *); | ||||
|  | ||||
|         if (p == NULL) { | ||||
|           len += out->printer(out, null, 4); | ||||
|         } else { | ||||
|           if (fmt[1] == 'Q') { | ||||
|             l = strlen(p); | ||||
|           } | ||||
|           len += out->printer(out, quote, 1); | ||||
|           len += json_encode_string(out, p, l); | ||||
|           len += out->printer(out, quote, 1); | ||||
|         } | ||||
|       } else { | ||||
|         /* | ||||
|          * we delegate printing to the system printf. | ||||
|          * The goal here is to delegate all modifiers parsing to the system | ||||
|          * printf, as you can see below we still have to parse the format | ||||
|          * types. | ||||
|          * | ||||
|          * Currently, %s with strings longer than 20 chars will require | ||||
|          * double-buffering (an auxiliary buffer will be allocated from heap). | ||||
|          * TODO(dfrank): reimplement %s and %.*s in order to avoid that. | ||||
|          */ | ||||
|  | ||||
|         const char *end_of_format_specifier = "sdfFgGlhuI.*-0123456789"; | ||||
|         size_t n = strspn(fmt + 1, end_of_format_specifier); | ||||
|         char *pbuf = buf; | ||||
|         size_t need_len; | ||||
|         char fmt2[20]; | ||||
|         va_list sub_ap; | ||||
|         strncpy(fmt2, fmt, n + 1 > sizeof(fmt2) ? sizeof(fmt2) : n + 1); | ||||
|         fmt2[n + 1] = '\0'; | ||||
|  | ||||
|         va_copy(sub_ap, ap); | ||||
|         need_len = | ||||
|             vsnprintf(buf, sizeof(buf), fmt2, sub_ap) + 1 /* null-term */; | ||||
|         /* | ||||
|          * TODO(lsm): Fix windows & eCos code path here. Their vsnprintf | ||||
|          * implementation returns -1 on overflow rather needed size. | ||||
|          */ | ||||
|         if (need_len > sizeof(buf)) { | ||||
|           /* | ||||
|            * resulting string doesn't fit into a stack-allocated buffer `buf`, | ||||
|            * so we need to allocate a new buffer from heap and use it | ||||
|            */ | ||||
|           pbuf = (char *) malloc(need_len); | ||||
|           va_copy(sub_ap, ap); | ||||
|           vsnprintf(pbuf, need_len, fmt2, sub_ap); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * however we need to parse the type ourselves in order to advance | ||||
|          * the va_list by the correct amount; there is no portable way to | ||||
|          * inherit the advancement made by vprintf. | ||||
|          * 32-bit (linux or windows) passes va_list by value. | ||||
|          */ | ||||
|         if ((n + 1 == strlen("%" PRId64) && strcmp(fmt2, "%" PRId64) == 0) || | ||||
|             (n + 1 == strlen("%" PRIu64) && strcmp(fmt2, "%" PRIu64) == 0)) { | ||||
|           (void) va_arg(ap, int64_t); | ||||
|           skip += strlen(PRIu64) - 1; | ||||
|         } else if (strcmp(fmt2, "%.*s") == 0) { | ||||
|           (void) va_arg(ap, int); | ||||
|           (void) va_arg(ap, char *); | ||||
|         } else { | ||||
|           switch (fmt2[n]) { | ||||
|             case 'u': | ||||
|             case 'd': | ||||
|               (void) va_arg(ap, int); | ||||
|               break; | ||||
|             case 'g': | ||||
|             case 'f': | ||||
|               (void) va_arg(ap, double); | ||||
|               break; | ||||
|             case 'p': | ||||
|               (void) va_arg(ap, void *); | ||||
|               break; | ||||
|             default: | ||||
|               /* many types are promoted to int */ | ||||
|               (void) va_arg(ap, int); | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         len += out->printer(out, pbuf, strlen(pbuf)); | ||||
|         skip = n + 1; | ||||
|  | ||||
|         /* If buffer was allocated from heap, free it */ | ||||
|         if (pbuf != buf) { | ||||
|           free(pbuf); | ||||
|           pbuf = NULL; | ||||
|         } | ||||
|       } | ||||
|       fmt += skip; | ||||
|     } else if (*fmt == '_' || is_alpha(*fmt)) { | ||||
|       len += out->printer(out, quote, 1); | ||||
|       while (*fmt == '_' || is_alpha(*fmt) || is_digit(*fmt)) { | ||||
|         len += out->printer(out, fmt, 1); | ||||
|         fmt++; | ||||
|       } | ||||
|       len += out->printer(out, quote, 1); | ||||
|     } else { | ||||
|       fmt++; | ||||
|     } | ||||
|   } | ||||
|   va_end(ap); | ||||
|  | ||||
|   return len; | ||||
| } | ||||
|  | ||||
| int json_printf(struct json_out *out, const char *fmt, ...) WEAK; | ||||
| int json_printf(struct json_out *out, const char *fmt, ...) { | ||||
|   int n; | ||||
|   va_list ap; | ||||
|   va_start(ap, fmt); | ||||
|   n = json_vprintf(out, fmt, ap); | ||||
|   va_end(ap); | ||||
|   return n; | ||||
| } | ||||
|  | ||||
| int json_printf_array(struct json_out *out, va_list *ap) WEAK; | ||||
| int json_printf_array(struct json_out *out, va_list *ap) { | ||||
|   int len = 0; | ||||
|   char *arr = va_arg(*ap, char *); | ||||
|   size_t i, arr_size = va_arg(*ap, size_t); | ||||
|   size_t elem_size = va_arg(*ap, size_t); | ||||
|   const char *fmt = va_arg(*ap, char *); | ||||
|   len += json_printf(out, "[", 1); | ||||
|   for (i = 0; arr != NULL && i < arr_size / elem_size; i++) { | ||||
|     union { | ||||
|       int64_t i; | ||||
|       double d; | ||||
|     } val; | ||||
|     memcpy(&val, arr + i * elem_size, | ||||
|            elem_size > sizeof(val) ? sizeof(val) : elem_size); | ||||
|     if (i > 0) len += json_printf(out, ", "); | ||||
|     if (strchr(fmt, 'f') != NULL) { | ||||
|       len += json_printf(out, fmt, val.d); | ||||
|     } else { | ||||
|       len += json_printf(out, fmt, val.i); | ||||
|     } | ||||
|   } | ||||
|   len += json_printf(out, "]", 1); | ||||
|   return len; | ||||
| } | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap) WEAK; | ||||
| int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap) { | ||||
|   int res = _vsnprintf(str, size, format, ap); | ||||
|   va_end(ap); | ||||
|   if (res >= size) { | ||||
|     str[size - 1] = '\0'; | ||||
|   } | ||||
|   return res; | ||||
| } | ||||
|  | ||||
| int cs_win_snprintf(char *str, size_t size, const char *format, ...) WEAK; | ||||
| int cs_win_snprintf(char *str, size_t size, const char *format, ...) { | ||||
|   int res; | ||||
|   va_list ap; | ||||
|   va_start(ap, format); | ||||
|   res = vsnprintf(str, size, format, ap); | ||||
|   va_end(ap); | ||||
|   return res; | ||||
| } | ||||
| #endif /* _WIN32 */ | ||||
|  | ||||
| int json_walk(const char *json_string, int json_string_length, | ||||
|               json_walk_callback_t callback, void *callback_data) WEAK; | ||||
| int json_walk(const char *json_string, int json_string_length, | ||||
|               json_walk_callback_t callback, void *callback_data) { | ||||
|   struct frozen frozen; | ||||
|  | ||||
|   memset(&frozen, 0, sizeof(frozen)); | ||||
|   frozen.end = json_string + json_string_length; | ||||
|   frozen.cur = json_string; | ||||
|   frozen.callback_data = callback_data; | ||||
|   frozen.callback = callback; | ||||
|  | ||||
|   TRY(doit(&frozen)); | ||||
|  | ||||
|   return frozen.cur - json_string; | ||||
| } | ||||
|  | ||||
| struct scan_array_info { | ||||
|   char path[JSON_MAX_PATH_LEN]; | ||||
|   struct json_token *token; | ||||
| }; | ||||
|  | ||||
| static void json_scanf_array_elem_cb(void *callback_data, const char *name, | ||||
|                                      size_t name_len, const char *path, | ||||
|                                      const struct json_token *token) { | ||||
|   struct scan_array_info *info = (struct scan_array_info *) callback_data; | ||||
|  | ||||
|   (void) name; | ||||
|   (void) name_len; | ||||
|  | ||||
|   if (strcmp(path, info->path) == 0) { | ||||
|     *info->token = *token; | ||||
|   } | ||||
| } | ||||
|  | ||||
| int json_scanf_array_elem(const char *s, int len, const char *path, int idx, | ||||
|                           struct json_token *token) WEAK; | ||||
| int json_scanf_array_elem(const char *s, int len, const char *path, int idx, | ||||
|                           struct json_token *token) { | ||||
|   struct scan_array_info info; | ||||
|   info.token = token; | ||||
|   memset(token, 0, sizeof(*token)); | ||||
|   snprintf(info.path, sizeof(info.path), "%s[%d]", path, idx); | ||||
|   json_walk(s, len, json_scanf_array_elem_cb, &info); | ||||
|   return token->len; | ||||
| } | ||||
|  | ||||
| struct json_scanf_info { | ||||
|   int num_conversions; | ||||
|   char *path; | ||||
|   const char *fmt; | ||||
|   void *target; | ||||
|   void *user_data; | ||||
|   int type; | ||||
| }; | ||||
|  | ||||
| int json_unescape(const char *src, int slen, char *dst, int dlen) WEAK; | ||||
| int json_unescape(const char *src, int slen, char *dst, int dlen) { | ||||
|   char *send = (char *) src + slen, *dend = dst + dlen, *orig_dst = dst, *p; | ||||
|   const char *esc1 = "\"\\/bfnrt", *esc2 = "\"\\/\b\f\n\r\t"; | ||||
|  | ||||
|   while (src < send) { | ||||
|     if (*src == '\\') { | ||||
|       if (++src >= send) return JSON_STRING_INCOMPLETE; | ||||
|       if (*src == 'u') { | ||||
|         /* TODO(lsm): \uXXXX escapes drag utf8 lib... Do it at some stage */ | ||||
|         return JSON_STRING_INVALID; | ||||
|       } else if ((p = (char *) strchr(esc1, *src)) != NULL) { | ||||
|         if (dst < dend) *dst = esc2[p - esc1]; | ||||
|       } else { | ||||
|         return JSON_STRING_INVALID; | ||||
|       } | ||||
|     } else { | ||||
|       if (dst < dend) *dst = *src; | ||||
|     } | ||||
|     dst++; | ||||
|     src++; | ||||
|   } | ||||
|  | ||||
|   return dst - orig_dst; | ||||
| } | ||||
|  | ||||
| static void json_scanf_cb(void *callback_data, const char *name, | ||||
|                           size_t name_len, const char *path, | ||||
|                           const struct json_token *token) { | ||||
|   struct json_scanf_info *info = (struct json_scanf_info *) callback_data; | ||||
|  | ||||
|   (void) name; | ||||
|   (void) name_len; | ||||
|  | ||||
|   if (strcmp(path, info->path) != 0) { | ||||
|     /* It's not the path we're looking for, so, just ignore this callback */ | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (token->ptr == NULL) { | ||||
|     /* | ||||
|      * We're not interested here in the events for which we have no value; | ||||
|      * namely, JSON_TYPE_OBJECT_START and JSON_TYPE_ARRAY_START | ||||
|      */ | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   switch (info->type) { | ||||
|     case 'B': | ||||
|       info->num_conversions++; | ||||
|       *(int *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0); | ||||
|       break; | ||||
|     case 'M': { | ||||
|       union { | ||||
|         void *p; | ||||
|         json_scanner_t f; | ||||
|       } u = {info->target}; | ||||
|       info->num_conversions++; | ||||
|       u.f(token->ptr, token->len, info->user_data); | ||||
|       break; | ||||
|     } | ||||
|     case 'Q': { | ||||
|       char **dst = (char **) info->target; | ||||
|       int unescaped_len = json_unescape(token->ptr, token->len, NULL, 0); | ||||
|       if (unescaped_len >= 0 && | ||||
|           (*dst = (char *) malloc(unescaped_len + 1)) != NULL) { | ||||
|         info->num_conversions++; | ||||
|         json_unescape(token->ptr, token->len, *dst, unescaped_len); | ||||
|         (*dst)[unescaped_len] = '\0'; | ||||
|       } | ||||
|       break; | ||||
|     } | ||||
|     case 'H': { | ||||
|       char **dst = (char **) info->user_data; | ||||
|       int i, len = token->len / 2; | ||||
|       *(int *) info->target = len; | ||||
|       if ((*dst = (char *) malloc(len + 1)) != NULL) { | ||||
|         for (i = 0; i < len; i++) { | ||||
|           (*dst)[i] = hexdec(token->ptr + 2 * i); | ||||
|         } | ||||
|         (*dst)[len] = '\0'; | ||||
|         info->num_conversions++; | ||||
|       } | ||||
|       break; | ||||
|     } | ||||
|     case 'V': { | ||||
|       char **dst = (char **) info->target; | ||||
|       int len = token->len * 4 / 3 + 2; | ||||
|       if ((*dst = (char *) malloc(len + 1)) != NULL) { | ||||
|         int n = b64dec(token->ptr, token->len, *dst); | ||||
|         (*dst)[n] = '\0'; | ||||
|         *(int *) info->user_data = n; | ||||
|         info->num_conversions++; | ||||
|       } | ||||
|       break; | ||||
|     } | ||||
|     case 'T': | ||||
|       info->num_conversions++; | ||||
|       *(struct json_token *) info->target = *token; | ||||
|       break; | ||||
|     default: | ||||
|       info->num_conversions += sscanf(token->ptr, info->fmt, info->target); | ||||
|       break; | ||||
|   } | ||||
| } | ||||
|  | ||||
| int json_vscanf(const char *s, int len, const char *fmt, va_list ap) WEAK; | ||||
| int json_vscanf(const char *s, int len, const char *fmt, va_list ap) { | ||||
|   char path[JSON_MAX_PATH_LEN] = "", fmtbuf[20]; | ||||
|   int i = 0; | ||||
|   char *p = NULL; | ||||
|   struct json_scanf_info info = {0, path, fmtbuf, NULL, NULL, 0}; | ||||
|  | ||||
|   while (fmt[i] != '\0') { | ||||
|     if (fmt[i] == '{') { | ||||
|       strcat(path, "."); | ||||
|       i++; | ||||
|     } else if (fmt[i] == '}') { | ||||
|       if ((p = strrchr(path, '.')) != NULL) *p = '\0'; | ||||
|       i++; | ||||
|     } else if (fmt[i] == '%') { | ||||
|       info.target = va_arg(ap, void *); | ||||
|       info.type = fmt[i + 1]; | ||||
|       switch (fmt[i + 1]) { | ||||
|         case 'M': | ||||
|         case 'V': | ||||
|         case 'H': | ||||
|           info.user_data = va_arg(ap, void *); | ||||
|         /* FALLTHROUGH */ | ||||
|         case 'B': | ||||
|         case 'Q': | ||||
|         case 'T': | ||||
|           i += 2; | ||||
|           break; | ||||
|         default: { | ||||
|           const char *delims = ", \t\r\n]}"; | ||||
|           int conv_len = strcspn(fmt + i + 1, delims) + 1; | ||||
|           snprintf(fmtbuf, sizeof(fmtbuf), "%.*s", conv_len, fmt + i); | ||||
|           i += conv_len; | ||||
|           i += strspn(fmt + i, delims); | ||||
|           break; | ||||
|         } | ||||
|       } | ||||
|       json_walk(s, len, json_scanf_cb, &info); | ||||
|     } else if (is_alpha(fmt[i]) || get_utf8_char_len(fmt[i]) > 1) { | ||||
|       const char *delims = ": \r\n\t"; | ||||
|       int key_len = strcspn(&fmt[i], delims); | ||||
|       if ((p = strrchr(path, '.')) != NULL) p[1] = '\0'; | ||||
|       sprintf(path + strlen(path), "%.*s", key_len, &fmt[i]); | ||||
|       i += key_len + strspn(fmt + i + key_len, delims); | ||||
|     } else { | ||||
|       i++; | ||||
|     } | ||||
|   } | ||||
|   return info.num_conversions; | ||||
| } | ||||
|  | ||||
| int json_scanf(const char *str, int len, const char *fmt, ...) WEAK; | ||||
| int json_scanf(const char *str, int len, const char *fmt, ...) { | ||||
|   int result; | ||||
|   va_list ap; | ||||
|   va_start(ap, fmt); | ||||
|   result = json_vscanf(str, len, fmt, ap); | ||||
|   va_end(ap); | ||||
|   return result; | ||||
| } | ||||
							
								
								
									
										214
									
								
								src/frozen/frozen.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								src/frozen/frozen.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,214 @@ | ||||
| /* | ||||
|  * Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com> | ||||
|  * Copyright (c) 2013 Cesanta Software Limited | ||||
|  * All rights reserved | ||||
|  * | ||||
|  * This library is dual-licensed: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2 as | ||||
|  * published by the Free Software Foundation. For the terms of this | ||||
|  * license, see <http: *www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * You are free to use this library under the terms of the GNU General | ||||
|  * Public License, but WITHOUT ANY WARRANTY; without even the implied | ||||
|  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||||
|  * See the GNU General Public License for more details. | ||||
|  * | ||||
|  * Alternatively, you can license this library under a commercial | ||||
|  * license, as set out in <http://cesanta.com/products.html>. | ||||
|  */ | ||||
|  | ||||
| #ifndef CS_FROZEN_FROZEN_H_ | ||||
| #define CS_FROZEN_FROZEN_H_ | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif /* __cplusplus */ | ||||
|  | ||||
| #include <stdarg.h> | ||||
| #include <stddef.h> | ||||
| #include <stdio.h> | ||||
|  | ||||
| /* JSON token type */ | ||||
| enum json_token_type { | ||||
|   JSON_TYPE_INVALID = 0, /* memsetting to 0 should create INVALID value */ | ||||
|   JSON_TYPE_STRING, | ||||
|   JSON_TYPE_NUMBER, | ||||
|   JSON_TYPE_TRUE, | ||||
|   JSON_TYPE_FALSE, | ||||
|   JSON_TYPE_NULL, | ||||
|   JSON_TYPE_OBJECT_START, | ||||
|   JSON_TYPE_OBJECT_END, | ||||
|   JSON_TYPE_ARRAY_START, | ||||
|   JSON_TYPE_ARRAY_END, | ||||
|  | ||||
|   JSON_TYPES_CNT | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * Structure containing token type and value. Used in `json_walk()` and | ||||
|  * `json_scanf()` with the format specifier `%T`. | ||||
|  */ | ||||
| struct json_token { | ||||
|   const char *ptr;           /* Points to the beginning of the value */ | ||||
|   int len;                   /* Value length */ | ||||
|   enum json_token_type type; /* Type of the token, possible values are above */ | ||||
| }; | ||||
|  | ||||
| #define JSON_INVALID_TOKEN \ | ||||
|   { 0, 0, JSON_TYPE_INVALID } | ||||
|  | ||||
| /* Error codes */ | ||||
| #define JSON_STRING_INVALID -1 | ||||
| #define JSON_STRING_INCOMPLETE -2 | ||||
|  | ||||
| /* | ||||
|  * Callback-based SAX-like API. | ||||
|  * | ||||
|  * Property name and length is given only if it's available: i.e. if current | ||||
|  * event is an object's property. In other cases, `name` is `NULL`. For | ||||
|  * example, name is never given: | ||||
|  *   - For the first value in the JSON string; | ||||
|  *   - For events JSON_TYPE_OBJECT_END and JSON_TYPE_ARRAY_END | ||||
|  * | ||||
|  * E.g. for the input `{ "foo": 123, "bar": [ 1, 2, { "baz": true } ] }`, | ||||
|  * the sequence of callback invocations will be as follows: | ||||
|  * | ||||
|  * - type: JSON_TYPE_OBJECT_START, name: NULL, path: "", value: NULL | ||||
|  * - type: JSON_TYPE_NUMBER, name: "foo", path: ".foo", value: "123" | ||||
|  * - type: JSON_TYPE_ARRAY_START,  name: "bar", path: ".bar", value: NULL | ||||
|  * - type: JSON_TYPE_NUMBER, name: "0", path: ".bar[0]", value: "1" | ||||
|  * - type: JSON_TYPE_NUMBER, name: "1", path: ".bar[1]", value: "2" | ||||
|  * - type: JSON_TYPE_OBJECT_START, name: "2", path: ".bar[2]", value: NULL | ||||
|  * - type: JSON_TYPE_TRUE, name: "baz", path: ".bar[2].baz", value: "true" | ||||
|  * - type: JSON_TYPE_OBJECT_END, name: NULL, path: ".bar[2]", value: "{ \"baz\": | ||||
|  *true }" | ||||
|  * - type: JSON_TYPE_ARRAY_END, name: NULL, path: ".bar", value: "[ 1, 2, { | ||||
|  *\"baz\": true } ]" | ||||
|  * - type: JSON_TYPE_OBJECT_END, name: NULL, path: "", value: "{ \"foo\": 123, | ||||
|  *\"bar\": [ 1, 2, { \"baz\": true } ] }" | ||||
|  */ | ||||
| typedef void (*json_walk_callback_t)(void *callback_data, const char *name, | ||||
|                                      size_t name_len, const char *path, | ||||
|                                      const struct json_token *token); | ||||
|  | ||||
| /* | ||||
|  * Parse `json_string`, invoking `callback` in a way similar to SAX parsers; | ||||
|  * see `json_walk_callback_t`. | ||||
|  */ | ||||
| int json_walk(const char *json_string, int json_string_length, | ||||
|               json_walk_callback_t callback, void *callback_data); | ||||
|  | ||||
| /* | ||||
|  * JSON generation API. | ||||
|  * struct json_out abstracts output, allowing alternative printing plugins. | ||||
|  */ | ||||
| struct json_out { | ||||
|   int (*printer)(struct json_out *, const char *str, size_t len); | ||||
|   union { | ||||
|     struct { | ||||
|       char *buf; | ||||
|       size_t size; | ||||
|       size_t len; | ||||
|     } buf; | ||||
|     void *data; | ||||
|     FILE *fp; | ||||
|   } u; | ||||
| }; | ||||
|  | ||||
| extern int json_printer_buf(struct json_out *, const char *, size_t); | ||||
| extern int json_printer_file(struct json_out *, const char *, size_t); | ||||
|  | ||||
| #define JSON_OUT_BUF(buf, len) \ | ||||
|   {                            \ | ||||
|     json_printer_buf, {        \ | ||||
|       { buf, len, 0 }          \ | ||||
|     }                          \ | ||||
|   } | ||||
| #define JSON_OUT_FILE(fp)   \ | ||||
|   {                         \ | ||||
|     json_printer_file, {    \ | ||||
|       { (void *) fp, 0, 0 } \ | ||||
|     }                       \ | ||||
|   } | ||||
|  | ||||
| typedef int (*json_printf_callback_t)(struct json_out *, va_list *ap); | ||||
|  | ||||
| /* | ||||
|  * Generate formatted output into a given sting buffer. | ||||
|  * This is a superset of printf() function, with extra format specifiers: | ||||
|  *  - `%B` print json boolean, `true` or `false`. Accepts an `int`. | ||||
|  *  - `%Q` print quoted escaped string or `null`. Accepts a `const char *`. | ||||
|  *  - `%.*Q` same as `%Q`, but with length. Accepts `int`, `const char *` | ||||
|  *  - `%V` print quoted base64-encoded string. Accepts a `const char *`, `int`. | ||||
|  *  - `%H` print quoted hex-encoded string. Accepts a `int`, `const char *`. | ||||
|  *  - `%M` invokes a json_printf_callback_t function. That callback function | ||||
|  *  can consume more parameters. | ||||
|  * | ||||
|  * Return number of bytes printed. If the return value is bigger then the | ||||
|  * supplied buffer, that is an indicator of overflow. In the overflow case, | ||||
|  * overflown bytes are not printed. | ||||
|  */ | ||||
| int json_printf(struct json_out *, const char *fmt, ...); | ||||
| int json_vprintf(struct json_out *, const char *fmt, va_list ap); | ||||
|  | ||||
| /* | ||||
|  * Helper %M callback that prints contiguous C arrays. | ||||
|  * Consumes void *array_ptr, size_t array_size, size_t elem_size, char *fmt | ||||
|  * Return number of bytes printed. | ||||
|  */ | ||||
| int json_printf_array(struct json_out *, va_list *ap); | ||||
|  | ||||
| /* | ||||
|  * Scan JSON string `str`, performing scanf-like conversions according to `fmt`. | ||||
|  * This is a `scanf()` - like function, with following differences: | ||||
|  * | ||||
|  * 1. Object keys in the format string may be not quoted, e.g. "{key: %d}" | ||||
|  * 2. Order of keys in an object is irrelevant. | ||||
|  * 3. Several extra format specifiers are supported: | ||||
|  *    - %B: consumes `int *`, expects boolean `true` or `false`. | ||||
|  *    - %Q: consumes `char **`, expects quoted, JSON-encoded string. Scanned | ||||
|  *       string is malloc-ed, caller must free() the string. | ||||
|  *    - %V: consumes `char **`, `int *`. Expects base64-encoded string. | ||||
|  *       Result string is base64-decoded, malloced and NUL-terminated. | ||||
|  *       The length of result string is stored in `int *` placeholder. | ||||
|  *       Caller must free() the result. | ||||
|  *    - %H: consumes `int *`, `char **`. | ||||
|  *       Expects a hex-encoded string, e.g. "fa014f". | ||||
|  *       Result string is hex-decoded, malloced and NUL-terminated. | ||||
|  *       The length of the result string is stored in `int *` placeholder. | ||||
|  *       Caller must free() the result. | ||||
|  *    - %M: consumes custom scanning function pointer and | ||||
|  *       `void *user_data` parameter - see json_scanner_t definition. | ||||
|  *    - %T: consumes `struct json_token *`, fills it out with matched token. | ||||
|  * | ||||
|  * Return number of elements successfully scanned & converted. | ||||
|  * Negative number means scan error. | ||||
|  */ | ||||
| int json_scanf(const char *str, int str_len, const char *fmt, ...); | ||||
| int json_vscanf(const char *str, int str_len, const char *fmt, va_list ap); | ||||
|  | ||||
| /* json_scanf's %M handler  */ | ||||
| typedef void (*json_scanner_t)(const char *str, int len, void *user_data); | ||||
|  | ||||
| /* | ||||
|  * Helper function to scan array item with given path and index. | ||||
|  * Fills `token` with the matched JSON token. | ||||
|  * Return 0 if no array element found, otherwise non-0. | ||||
|  */ | ||||
| int json_scanf_array_elem(const char *s, int len, const char *path, int index, | ||||
|                           struct json_token *token); | ||||
|  | ||||
| /* | ||||
|  * Unescape JSON-encoded string src,slen into dst, dlen. | ||||
|  * src and dst may overlap. | ||||
|  * If destination buffer is too small (or zero-length), result string is not | ||||
|  * written but the length is counted nevertheless (similar to snprintf). | ||||
|  * Return the length of unescaped string in bytes. | ||||
|  */ | ||||
| int json_unescape(const char *src, int slen, char *dst, int dlen); | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif /* __cplusplus */ | ||||
|  | ||||
| #endif /* CS_FROZEN_FROZEN_H_ */ | ||||
		Reference in New Issue
	
	Block a user
	 jcorporation
					jcorporation