├── cjson ├── ext.manifest └── src │ ├── fpconv.h │ ├── strbuf.h │ ├── fpconv.c │ ├── strbuf.c │ └── cjson.c ├── docs └── logo.png ├── input └── game.input_binding ├── .gitignore ├── game.project ├── resources └── small.json ├── example ├── example.collection └── example.script └── README.md /cjson/ext.manifest: -------------------------------------------------------------------------------- 1 | name: "cjson" 2 | -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Melsoft-Games/defold-cjson/HEAD/docs/logo.png -------------------------------------------------------------------------------- /input/game.input_binding: -------------------------------------------------------------------------------- 1 | mouse_trigger { 2 | input: MOUSE_BUTTON_1 3 | action: "touch" 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.internal 2 | /build 3 | .externalToolBuilders 4 | .DS_Store 5 | Thumbs.db 6 | .lock-wscript 7 | *.pyc 8 | .project 9 | .cproject 10 | builtins -------------------------------------------------------------------------------- /game.project: -------------------------------------------------------------------------------- 1 | [bootstrap] 2 | main_collection = /example/example.collectionc 3 | 4 | [script] 5 | shared_state = 1 6 | 7 | [display] 8 | width = 960 9 | height = 640 10 | 11 | [project] 12 | title = CJson 13 | custom_resources = resources/ 14 | 15 | [library] 16 | include_dirs = cjson -------------------------------------------------------------------------------- /resources/small.json: -------------------------------------------------------------------------------- 1 | { 2 | "old": "step", 3 | "recently": { 4 | "human": [ 5 | 626552284, 6 | "daughter", 7 | "doubt", 8 | -1544724900, 9 | 1829042422, 10 | -1590987650 11 | ], 12 | "exciting": "all", 13 | "process": true, 14 | "bigger": "audience", 15 | "parts": false, 16 | "tax": -333744421 17 | }, 18 | "present": "simple", 19 | "joined": "sentence", 20 | "research": false, 21 | "hard": false 22 | } -------------------------------------------------------------------------------- /cjson/src/fpconv.h: -------------------------------------------------------------------------------- 1 | /* Lua CJSON floating point conversion routines */ 2 | 3 | /* Buffer required to store the largest string representation of a double. 4 | * 5 | * Longest double printed with %.14g is 21 characters long: 6 | * -1.7976931348623e+308 */ 7 | # define FPCONV_G_FMT_BUFSIZE 32 8 | 9 | #ifdef USE_INTERNAL_FPCONV 10 | static inline void fpconv_init() 11 | { 12 | /* Do nothing - not required */ 13 | } 14 | #else 15 | extern inline void fpconv_init(); 16 | #endif 17 | 18 | extern int fpconv_g_fmt(char*, double, int); 19 | extern double fpconv_strtod(const char*, char**); 20 | 21 | /* vi:ai et sw=4 ts=4: 22 | */ 23 | -------------------------------------------------------------------------------- /example/example.collection: -------------------------------------------------------------------------------- 1 | name: "default" 2 | scale_along_z: 0 3 | embedded_instances { 4 | id: "go" 5 | data: "components {\n" 6 | " id: \"example\"\n" 7 | " component: \"/example/example.script\"\n" 8 | " position {\n" 9 | " x: 0.0\n" 10 | " y: 0.0\n" 11 | " z: 0.0\n" 12 | " }\n" 13 | " rotation {\n" 14 | " x: 0.0\n" 15 | " y: 0.0\n" 16 | " z: 0.0\n" 17 | " w: 1.0\n" 18 | " }\n" 19 | "}\n" 20 | "" 21 | position { 22 | x: 0.0 23 | y: 0.0 24 | z: 0.0 25 | } 26 | rotation { 27 | x: 0.0 28 | y: 0.0 29 | z: 0.0 30 | w: 1.0 31 | } 32 | scale3 { 33 | x: 1.0 34 | y: 1.0 35 | z: 1.0 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /example/example.script: -------------------------------------------------------------------------------- 1 | function init(self) 2 | local simple_json = '{"5":2,"o":{"a":5,"b":"test"},"string_val":"some val"}' 3 | 4 | local parsed = cjson.decode(simple_json) 5 | print("Encoded back") 6 | pprint(cjson.encode(parsed)) 7 | 8 | local luat = { 9 | [5] = "value", 10 | text = "text value", 11 | ["btext"] = 5 12 | } 13 | local luat_string = cjson.encode(luat) 14 | print(luat_string) 15 | 16 | 17 | local small_json = sys.load_resource("/resources/small.json") 18 | local medium_json = sys.load_resource("/resources/medium.json") 19 | local big_json = sys.load_resource("/resources/big.json") 20 | 21 | local small_lua = cjson.decode(small_json) 22 | local medium_lua = cjson.decode(medium_json) 23 | local big_lua = cjson.decode(big_json) 24 | 25 | print("SMALL JSON") 26 | local t = socket.gettime() 27 | cjson.decode(small_json) 28 | print("CJSON decode", socket.gettime() - t) 29 | t = socket.gettime() 30 | cjson.encode(small_lua) 31 | print("CJSON encode", socket.gettime() - t) 32 | 33 | 34 | print("MEDIUM JSON") 35 | t = socket.gettime() 36 | cjson.decode(medium_json) 37 | print("CJSON decode", socket.gettime() - t) 38 | t = socket.gettime() 39 | cjson.encode(medium_lua) 40 | print("CJSON encode", socket.gettime() - t) 41 | 42 | print("BIG JSON") 43 | t = socket.gettime() 44 | cjson.decode(big_json) 45 | print("CJSON decode", socket.gettime() - t) 46 | t = socket.gettime() 47 | cjson.encode(big_lua) 48 | print("CJSON encode", socket.gettime() - t) 49 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](docs/logo.png) 2 | 3 | # Defold-CJSON 4 | 5 | Defold-CJSON [Native Extension](https://www.defold.com/manuals/extensions/) for the [Defold Game Engine](https://www.defold.com) 6 | 7 | This extension allow you encode lua table to JSON and decode JSON to lua table in native part 8 | 9 | 10 | ## Platforms 11 | 12 | * **iOS** 13 | * **Android** 14 | * **MacOS** 15 | * **Windows** 16 | 17 | ## Setup 18 | 19 | You can use the Defold-CJSON extension in your own project by adding this project as a [Defold library dependency](https://www.defold.com/manuals/libraries/). Open your game.project file and in the dependencies field under project add: 20 | 21 | > https://github.com/Melsoft-Games/defold-cjson/archive/master.zip 22 | 23 | Or point to the ZIP file of a [specific release](https://github.com/Melsoft-Games/defold-cjson/releases). 24 | 25 | ## API 26 | 27 | #### `cjson.encode(lua_table)` 28 | return encoded lua table to json string 29 | 30 | #### `cjson.decode(valid_json)` 31 | return parsed lua_table 32 | 33 | **Note**: *json must be valid, or cjson will throw error. You can wrap it with pcall, or change extension and call cjson safe_decode from lua-cjson lib* 34 | 35 | 36 | ## Benchmark 37 | **rxlua** - pure lua JSON encode/decode [implementation](https://github.com/rxi/json.lua) 38 | 39 | time in ms 40 | 41 | | Json: | 4KB | 971KB | 1.8MB | 42 | | -- | -- | -- | -- | 43 | | defold decode | 0 | 0.032 | 0.075 | 44 | | -- | -- | -- | -- | 45 | | rxlua encode | 0 | 0.0734 | 0.148 | 46 | | rxlua decode | 0.0003 | 0.0308 | 0.051 | 47 | | -- | -- | -- | -- | 48 | | cjson encode | 0 | 0.0057 | 0.0122 | 49 | | cjson decode | 0 | 0.0066 | 0.0117 | 50 | 51 | 52 | 53 | ## License, Authors 54 | *MIT license* 55 | This NE wrapped by [Denis Smirnov](https://github.com/trouble1337) 56 | 57 | Original module: [lua-cjson](https://github.com/mpx/lua-cjson) 58 | [License](https://github.com/mpx/lua-cjson/blob/master/LICENSE) 59 | 60 | ## Issues and suggestions 61 | 62 | If you have any issues, questions or suggestions please [create an issue](https://github.com/Melsoft-Games/defold-cjson/issues) or contact me: insality@gmail.com 63 | -------------------------------------------------------------------------------- /cjson/src/strbuf.h: -------------------------------------------------------------------------------- 1 | /* strbuf - String buffer routines 2 | * 3 | * Copyright (c) 2010-2012 Mark Pulford 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject to 11 | * the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be 14 | * included in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | /* Size: Total bytes allocated to *buf 29 | * Length: String length, excluding optional NULL terminator. 30 | * Increment: Allocation increments when resizing the string buffer. 31 | * Dynamic: True if created via strbuf_new() 32 | */ 33 | 34 | typedef struct { 35 | char *buf; 36 | int size; 37 | int length; 38 | int increment; 39 | int dynamic; 40 | int reallocs; 41 | int debug; 42 | } strbuf_t; 43 | 44 | #ifndef STRBUF_DEFAULT_SIZE 45 | #define STRBUF_DEFAULT_SIZE 1023 46 | #endif 47 | #ifndef STRBUF_DEFAULT_INCREMENT 48 | #define STRBUF_DEFAULT_INCREMENT -2 49 | #endif 50 | 51 | /* Initialise */ 52 | extern strbuf_t *strbuf_new(int len); 53 | extern void strbuf_init(strbuf_t *s, int len); 54 | extern void strbuf_set_increment(strbuf_t *s, int increment); 55 | 56 | /* Release */ 57 | extern void strbuf_free(strbuf_t *s); 58 | extern char *strbuf_free_to_string(strbuf_t *s, int *len); 59 | 60 | /* Management */ 61 | extern void strbuf_resize(strbuf_t *s, int len); 62 | static int strbuf_empty_length(strbuf_t *s); 63 | static int strbuf_length(strbuf_t *s); 64 | static char *strbuf_string(strbuf_t *s, int *len); 65 | static void strbuf_ensure_empty_length(strbuf_t *s, int len); 66 | static char *strbuf_empty_ptr(strbuf_t *s); 67 | static void strbuf_extend_length(strbuf_t *s, int len); 68 | 69 | /* Update */ 70 | extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...); 71 | extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...); 72 | static void strbuf_append_mem(strbuf_t *s, const char *c, int len); 73 | extern void strbuf_append_string(strbuf_t *s, const char *str); 74 | static void strbuf_append_char(strbuf_t *s, const char c); 75 | static void strbuf_ensure_null(strbuf_t *s); 76 | 77 | /* Reset string for before use */ 78 | static inline void strbuf_reset(strbuf_t *s) 79 | { 80 | s->length = 0; 81 | } 82 | 83 | static inline int strbuf_allocated(strbuf_t *s) 84 | { 85 | return s->buf != NULL; 86 | } 87 | 88 | /* Return bytes remaining in the string buffer 89 | * Ensure there is space for a NULL terminator. */ 90 | static inline int strbuf_empty_length(strbuf_t *s) 91 | { 92 | return s->size - s->length - 1; 93 | } 94 | 95 | static inline void strbuf_ensure_empty_length(strbuf_t *s, int len) 96 | { 97 | if (len > strbuf_empty_length(s)) 98 | strbuf_resize(s, s->length + len); 99 | } 100 | 101 | static inline char *strbuf_empty_ptr(strbuf_t *s) 102 | { 103 | return s->buf + s->length; 104 | } 105 | 106 | static inline void strbuf_extend_length(strbuf_t *s, int len) 107 | { 108 | s->length += len; 109 | } 110 | 111 | static inline int strbuf_length(strbuf_t *s) 112 | { 113 | return s->length; 114 | } 115 | 116 | static inline void strbuf_append_char(strbuf_t *s, const char c) 117 | { 118 | strbuf_ensure_empty_length(s, 1); 119 | s->buf[s->length++] = c; 120 | } 121 | 122 | static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c) 123 | { 124 | s->buf[s->length++] = c; 125 | } 126 | 127 | static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len) 128 | { 129 | strbuf_ensure_empty_length(s, len); 130 | memcpy(s->buf + s->length, c, len); 131 | s->length += len; 132 | } 133 | 134 | static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len) 135 | { 136 | memcpy(s->buf + s->length, c, len); 137 | s->length += len; 138 | } 139 | 140 | static inline void strbuf_ensure_null(strbuf_t *s) 141 | { 142 | s->buf[s->length] = 0; 143 | } 144 | 145 | // +++++ 146 | static inline char *strbuf_string(strbuf_t *s, int *len) 147 | { 148 | if (len) 149 | *len = s->length; 150 | 151 | return s->buf; 152 | } 153 | 154 | /* vi:ai et sw=4 ts=4: 155 | */ 156 | -------------------------------------------------------------------------------- /cjson/src/fpconv.c: -------------------------------------------------------------------------------- 1 | /* fpconv - Floating point conversion routines 2 | * 3 | * Copyright (c) 2011-2012 Mark Pulford 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject to 11 | * the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be 14 | * included in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | /* JSON uses a '.' decimal separator. strtod() / sprintf() under C libraries 26 | * with locale support will break when the decimal separator is a comma. 27 | * 28 | * fpconv_* will around these issues with a translation buffer if required. 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "fpconv.h" 37 | 38 | /* Lua CJSON assumes the locale is the same for all threads within a 39 | * process and doesn't change after initialisation. 40 | * 41 | * This avoids the need for per thread storage or expensive checks 42 | * for call. */ 43 | static char locale_decimal_point = '.'; 44 | 45 | /* In theory multibyte decimal_points are possible, but 46 | * Lua CJSON only supports UTF-8 and known locales only have 47 | * single byte decimal points ([.,]). 48 | * 49 | * localconv() may not be thread safe (=>crash), and nl_langinfo() is 50 | * not supported on some platforms. Use sprintf() instead - if the 51 | * locale does change, at least Lua CJSON won't crash. */ 52 | static void fpconv_update_locale() 53 | { 54 | char buf[8]; 55 | 56 | snprintf(buf, sizeof(buf), "%g", 0.5); 57 | 58 | /* Failing this test might imply the platform has a buggy dtoa 59 | * implementation or wide characters */ 60 | if (buf[0] != '0' || buf[2] != '5' || buf[3] != 0) { 61 | fprintf(stderr, "Error: wide characters found or printf() bug."); 62 | abort(); 63 | } 64 | 65 | locale_decimal_point = buf[1]; 66 | } 67 | 68 | /* Check for a valid number character: [-+0-9a-yA-Y.] 69 | * Eg: -0.6e+5, infinity, 0xF0.F0pF0 70 | * 71 | * Used to find the probable end of a number. It doesn't matter if 72 | * invalid characters are counted - strtod() will find the valid 73 | * number if it exists. The risk is that slightly more memory might 74 | * be allocated before a parse error occurs. */ 75 | static inline int valid_number_character(char ch) 76 | { 77 | char lower_ch; 78 | 79 | if ('0' <= ch && ch <= '9') 80 | return 1; 81 | if (ch == '-' || ch == '+' || ch == '.') 82 | return 1; 83 | 84 | /* Hex digits, exponent (e), base (p), "infinity",.. */ 85 | lower_ch = ch | 0x20; 86 | if ('a' <= lower_ch && lower_ch <= 'y') 87 | return 1; 88 | 89 | return 0; 90 | } 91 | 92 | /* Calculate the size of the buffer required for a strtod locale 93 | * conversion. */ 94 | static int strtod_buffer_size(const char *s) 95 | { 96 | const char *p = s; 97 | 98 | while (valid_number_character(*p)) 99 | p++; 100 | 101 | return p - s; 102 | } 103 | 104 | /* Similar to strtod(), but must be passed the current locale's decimal point 105 | * character. Guaranteed to be called at the start of any valid number in a string */ 106 | double fpconv_strtod(const char *nptr, char **endptr) 107 | { 108 | char localbuf[FPCONV_G_FMT_BUFSIZE]; 109 | char *buf, *endbuf, *dp; 110 | int buflen; 111 | double value; 112 | 113 | /* System strtod() is fine when decimal point is '.' */ 114 | if (locale_decimal_point == '.') 115 | return strtod(nptr, endptr); 116 | 117 | buflen = strtod_buffer_size(nptr); 118 | if (!buflen) { 119 | /* No valid characters found, standard strtod() return */ 120 | *endptr = (char *)nptr; 121 | return 0; 122 | } 123 | 124 | /* Duplicate number into buffer */ 125 | if (buflen >= FPCONV_G_FMT_BUFSIZE) { 126 | /* Handle unusually large numbers */ 127 | buf = (char *)malloc(buflen + 1); 128 | if (!buf) { 129 | fprintf(stderr, "Out of memory"); 130 | abort(); 131 | } 132 | } else { 133 | /* This is the common case.. */ 134 | buf = localbuf; 135 | } 136 | memcpy(buf, nptr, buflen); 137 | buf[buflen] = 0; 138 | 139 | /* Update decimal point character if found */ 140 | dp = strchr(buf, '.'); 141 | if (dp) 142 | *dp = locale_decimal_point; 143 | 144 | value = strtod(buf, &endbuf); 145 | *endptr = (char *)&nptr[endbuf - buf]; 146 | if (buflen >= FPCONV_G_FMT_BUFSIZE) 147 | free(buf); 148 | 149 | return value; 150 | } 151 | 152 | /* "fmt" must point to a buffer of at least 6 characters */ 153 | static void set_number_format(char *fmt, int precision) 154 | { 155 | int d1, d2, i; 156 | 157 | assert(1 <= precision && precision <= 14); 158 | 159 | /* Create printf format (%.14g) from precision */ 160 | d1 = precision / 10; 161 | d2 = precision % 10; 162 | fmt[0] = '%'; 163 | fmt[1] = '.'; 164 | i = 2; 165 | if (d1) { 166 | fmt[i++] = '0' + d1; 167 | } 168 | fmt[i++] = '0' + d2; 169 | fmt[i++] = 'g'; 170 | fmt[i] = 0; 171 | } 172 | 173 | /* Assumes there is always at least 32 characters available in the target buffer */ 174 | int fpconv_g_fmt(char *str, double num, int precision) 175 | { 176 | char buf[FPCONV_G_FMT_BUFSIZE]; 177 | char fmt[6]; 178 | int len; 179 | char *b; 180 | 181 | set_number_format(fmt, precision); 182 | 183 | /* Pass through when decimal point character is dot. */ 184 | if (locale_decimal_point == '.') 185 | return snprintf(str, FPCONV_G_FMT_BUFSIZE, fmt, num); 186 | 187 | /* snprintf() to a buffer then translate for other decimal point characters */ 188 | len = snprintf(buf, FPCONV_G_FMT_BUFSIZE, fmt, num); 189 | 190 | /* Copy into target location. Translate decimal point if required */ 191 | b = buf; 192 | do { 193 | *str++ = (*b == locale_decimal_point ? '.' : *b); 194 | } while(*b++); 195 | 196 | return len; 197 | } 198 | 199 | void fpconv_init() 200 | { 201 | fpconv_update_locale(); 202 | } 203 | 204 | /* vi:ai et sw=4 ts=4: 205 | */ 206 | -------------------------------------------------------------------------------- /cjson/src/strbuf.c: -------------------------------------------------------------------------------- 1 | /* strbuf - String buffer routines 2 | * 3 | * Copyright (c) 2010-2012 Mark Pulford 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject to 11 | * the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be 14 | * included in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "strbuf.h" 31 | 32 | static void die(const char *fmt, ...) 33 | { 34 | va_list arg; 35 | 36 | va_start(arg, fmt); 37 | vfprintf(stderr, fmt, arg); 38 | va_end(arg); 39 | fprintf(stderr, "\n"); 40 | 41 | exit(-1); 42 | } 43 | 44 | // ++++++ 45 | void strbuf_init(strbuf_t *s, int len) 46 | { 47 | int size; 48 | if (len <= 0) 49 | size = STRBUF_DEFAULT_SIZE; 50 | else 51 | size = len + 1; 52 | s->buf = NULL; 53 | s->size = size; 54 | s->length = 0; 55 | s->increment = STRBUF_DEFAULT_INCREMENT; 56 | s->dynamic = 0; 57 | s->reallocs = 0; 58 | s->debug = 0; 59 | s->buf = (char *)malloc(size); 60 | if (!s->buf) 61 | die("Out of memory"); 62 | strbuf_ensure_null(s); 63 | } 64 | 65 | strbuf_t *strbuf_new(int len) 66 | { 67 | strbuf_t *s; 68 | s = (strbuf_t *)malloc(sizeof(strbuf_t)); 69 | if (!s) 70 | die("Out of memory"); 71 | strbuf_init(s, len); 72 | s->dynamic = 1; 73 | return s; 74 | } 75 | 76 | void strbuf_set_increment(strbuf_t *s, int increment) 77 | { 78 | /* Increment > 0: Linear buffer growth rate 79 | * Increment < -1: Exponential buffer growth rate */ 80 | if (increment == 0 || increment == -1) 81 | die("BUG: Invalid string increment"); 82 | 83 | s->increment = increment; 84 | } 85 | 86 | static inline void debug_stats(strbuf_t *s) 87 | { 88 | if (s->debug) { 89 | fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %d, size: %d\n", 90 | (long)s, s->reallocs, s->length, s->size); 91 | } 92 | } 93 | 94 | // +++++ 95 | void strbuf_free(strbuf_t *s) 96 | { 97 | debug_stats(s); 98 | 99 | if (s->buf) { 100 | free(s->buf); 101 | s->buf = NULL; 102 | } 103 | if (s->dynamic) 104 | free(s); 105 | } 106 | 107 | char *strbuf_free_to_string(strbuf_t *s, int *len) 108 | { 109 | char *buf; 110 | 111 | debug_stats(s); 112 | 113 | strbuf_ensure_null(s); 114 | 115 | buf = s->buf; 116 | if (len) 117 | *len = s->length; 118 | 119 | if (s->dynamic) 120 | free(s); 121 | 122 | return buf; 123 | } 124 | 125 | static int calculate_new_size(strbuf_t *s, int len) 126 | { 127 | int reqsize, newsize; 128 | 129 | if (len <= 0) 130 | die("BUG: Invalid strbuf length requested"); 131 | 132 | /* Ensure there is room for optional NULL termination */ 133 | reqsize = len + 1; 134 | 135 | /* If the user has requested to shrink the buffer, do it exactly */ 136 | if (s->size > reqsize) 137 | return reqsize; 138 | 139 | newsize = s->size; 140 | if (s->increment < 0) { 141 | /* Exponential sizing */ 142 | while (newsize < reqsize) 143 | newsize *= -s->increment; 144 | } else { 145 | /* Linear sizing */ 146 | newsize = ((newsize + s->increment - 1) / s->increment) * s->increment; 147 | } 148 | 149 | return newsize; 150 | } 151 | 152 | 153 | /* Ensure strbuf can handle a string length bytes long (ignoring NULL 154 | * optional termination). */ 155 | void strbuf_resize(strbuf_t *s, int len) 156 | { 157 | int newsize; 158 | 159 | newsize = calculate_new_size(s, len); 160 | 161 | if (s->debug > 1) { 162 | fprintf(stderr, "strbuf(%lx) resize: %d => %d\n", 163 | (long)s, s->size, newsize); 164 | } 165 | 166 | s->size = newsize; 167 | s->buf = (char*)realloc(s->buf, s->size); 168 | if (!s->buf) 169 | die("Out of memory"); 170 | s->reallocs++; 171 | } 172 | 173 | void strbuf_append_string(strbuf_t *s, const char *str) 174 | { 175 | int space, i; 176 | 177 | space = strbuf_empty_length(s); 178 | 179 | for (i = 0; str[i]; i++) { 180 | if (space < 1) { 181 | strbuf_resize(s, s->length + 1); 182 | space = strbuf_empty_length(s); 183 | } 184 | 185 | s->buf[s->length] = str[i]; 186 | s->length++; 187 | space--; 188 | } 189 | } 190 | 191 | /* strbuf_append_fmt() should only be used when an upper bound 192 | * is known for the output string. */ 193 | void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...) 194 | { 195 | va_list arg; 196 | int fmt_len; 197 | 198 | strbuf_ensure_empty_length(s, len); 199 | 200 | va_start(arg, fmt); 201 | fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg); 202 | va_end(arg); 203 | 204 | if (fmt_len < 0) 205 | die("BUG: Unable to convert number"); /* This should never happen.. */ 206 | 207 | s->length += fmt_len; 208 | } 209 | 210 | /* strbuf_append_fmt_retry() can be used when the there is no known 211 | * upper bound for the output string. */ 212 | void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...) 213 | { 214 | va_list arg; 215 | int fmt_len; 216 | int tryy; 217 | int empty_len; 218 | 219 | /* If the first attempt to append fails, resize the buffer appropriately 220 | * and try again */ 221 | for (tryy = 0; ; tryy++) { 222 | va_start(arg, fmt); 223 | /* Append the new formatted string */ 224 | /* fmt_len is the length of the string required, excluding the 225 | * trailing NULL */ 226 | empty_len = strbuf_empty_length(s); 227 | /* Add 1 since there is also space to store the terminating NULL. */ 228 | fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg); 229 | va_end(arg); 230 | 231 | if (fmt_len <= empty_len) 232 | break; /* SUCCESS */ 233 | if (tryy > 0) 234 | die("BUG: length of formatted string changed"); 235 | 236 | strbuf_resize(s, s->length + fmt_len); 237 | } 238 | 239 | s->length += fmt_len; 240 | } 241 | 242 | /* vi:ai et sw=4 ts=4: 243 | */ 244 | -------------------------------------------------------------------------------- /cjson/src/cjson.c: -------------------------------------------------------------------------------- 1 | #define LIB_NAME "cjson" 2 | #define DLIB_LOG_DOMAIN "cjson" 3 | 4 | #ifdef _MSC_VER 5 | //not #if defined(_WIN32) || defined(_WIN64) because we have strncasecmp in mingw 6 | #define strncasecmp _strnicmp 7 | #define strcasecmp _stricmp 8 | #endif 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include "strbuf.h" 18 | #include "fpconv.h" 19 | 20 | /* Lua CJSON - JSON support for Lua 21 | * 22 | * Copyright (c) 2010-2012 Mark Pulford 23 | * 24 | * Permission is hereby granted, free of charge, to any person obtaining 25 | * a copy of this software and associated documentation files (the 26 | * "Software"), to deal in the Software without restriction, including 27 | * without limitation the rights to use, copy, modify, merge, publish, 28 | * distribute, sublicense, and/or sell copies of the Software, and to 29 | * permit persons to whom the Software is furnished to do so, subject to 30 | * the following conditions: 31 | * 32 | * The above copyright notice and this permission notice shall be 33 | * included in all copies or substantial portions of the Software. 34 | * 35 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 36 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 37 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 38 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 39 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 40 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 41 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 42 | */ 43 | 44 | /* Caveats: 45 | * - JSON "null" values are represented as lightuserdata since Lua 46 | * tables cannot contain "nil". Compare with cjson.null. 47 | * - Invalid UTF-8 characters are not detected and will be passed 48 | * untouched. If required, UTF-8 error checking should be done 49 | * outside this library. 50 | * - Javascript comments are not part of the JSON spec, and are not 51 | * currently supported. 52 | * 53 | * Note: Decoding is slower than encoding. Lua spends significant 54 | * time (30%) managing tables when parsing JSON since it is 55 | * difficult to know object/array sizes ahead of time. 56 | */ 57 | 58 | #ifndef CJSON_MODNAME 59 | #define CJSON_MODNAME "cjson" 60 | #endif 61 | 62 | #ifndef CJSON_VERSION 63 | #define CJSON_VERSION "2.1.0" 64 | #endif 65 | 66 | #if !defined(isinf) && (defined(USE_INTERNAL_ISINF) || defined(MISSING_ISINF)) 67 | #define isinf(x) (!isnan(x) && isnan((x) - (x))) 68 | #endif 69 | 70 | #define DEFAULT_SPARSE_CONVERT 0 71 | #define DEFAULT_SPARSE_RATIO 2 72 | #define DEFAULT_SPARSE_SAFE 10 73 | #define DEFAULT_ENCODE_MAX_DEPTH 1000 74 | #define DEFAULT_DECODE_MAX_DEPTH 1000 75 | #define DEFAULT_ENCODE_INVALID_NUMBERS 0 76 | #define DEFAULT_DECODE_INVALID_NUMBERS 1 77 | #define DEFAULT_ENCODE_KEEP_BUFFER 1 78 | #define DEFAULT_ENCODE_NUMBER_PRECISION 14 79 | 80 | #ifdef DISABLE_INVALID_NUMBERS 81 | #undef DEFAULT_DECODE_INVALID_NUMBERS 82 | #define DEFAULT_DECODE_INVALID_NUMBERS 0 83 | #endif 84 | 85 | typedef enum { 86 | T_OBJ_BEGIN, 87 | T_OBJ_END, 88 | T_ARR_BEGIN, 89 | T_ARR_END, 90 | T_STRING, 91 | T_NUMBER, 92 | T_BOOLEAN, 93 | T_NULL, 94 | T_COLON, 95 | T_COMMA, 96 | T_END, 97 | T_WHITESPACE, 98 | T_ERROR, 99 | T_UNKNOWN 100 | } json_token_type_t; 101 | 102 | static const char *json_token_type_name[] = 103 | { 104 | "T_OBJ_BEGIN", 105 | "T_OBJ_END", 106 | "T_ARR_BEGIN", 107 | "T_ARR_END", 108 | "T_STRING", 109 | "T_NUMBER", 110 | "T_BOOLEAN", 111 | "T_NULL", 112 | "T_COLON", 113 | "T_COMMA", 114 | "T_END", 115 | "T_WHITESPACE", 116 | "T_ERROR", 117 | "T_UNKNOWN", 118 | NULL 119 | }; 120 | 121 | typedef struct { 122 | json_token_type_t ch2token[256]; 123 | char escape2char[256]; 124 | strbuf_t encode_buf; 125 | int encode_sparse_convert; 126 | int encode_sparse_ratio; 127 | int encode_sparse_safe; 128 | int encode_max_depth; 129 | int encode_invalid_numbers; 130 | int encode_number_precision; 131 | int encode_keep_buffer; 132 | int decode_invalid_numbers; 133 | int decode_max_depth; 134 | } json_config_t; 135 | 136 | typedef struct { 137 | const char *data; 138 | const char *ptr; 139 | strbuf_t *tmp; 140 | json_config_t *cfg; 141 | int current_depth; 142 | } json_parse_t; 143 | 144 | typedef struct { 145 | json_token_type_t type; 146 | int index; 147 | union { 148 | const char *string; 149 | double number; 150 | int boolean; 151 | } value; 152 | int string_len; 153 | } json_token_t; 154 | 155 | static const char *char2escape[256] = { 156 | "\\u0000", "\\u0001", "\\u0002", "\\u0003", 157 | "\\u0004", "\\u0005", "\\u0006", "\\u0007", 158 | "\\b", "\\t", "\\n", "\\u000b", 159 | "\\f", "\\r", "\\u000e", "\\u000f", 160 | "\\u0010", "\\u0011", "\\u0012", "\\u0013", 161 | "\\u0014", "\\u0015", "\\u0016", "\\u0017", 162 | "\\u0018", "\\u0019", "\\u001a", "\\u001b", 163 | "\\u001c", "\\u001d", "\\u001e", "\\u001f", 164 | NULL, NULL, "\\\"", NULL, NULL, NULL, NULL, NULL, 165 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\/", 166 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 167 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 168 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 169 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 170 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 171 | NULL, NULL, NULL, NULL, "\\\\", NULL, NULL, NULL, 172 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 173 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 174 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 175 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\u007f", 176 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 177 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 178 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 179 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 180 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 181 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 182 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 183 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 184 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 185 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 186 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 187 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 188 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 189 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 190 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 191 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 192 | }; 193 | 194 | // ++++ 195 | static json_config_t *cfg; 196 | 197 | static json_config_t *json_fetch_config(lua_State *l) 198 | { 199 | // json_config_t *cfg; 200 | cfg = (json_config_t*)lua_touserdata(l, lua_upvalueindex(1)); 201 | if (!cfg) 202 | luaL_error(l, "BUG: Unable to fetch CJSON configuration"); 203 | return cfg; 204 | } 205 | 206 | static json_config_t *json_arg_init(lua_State *l, int args) 207 | { 208 | luaL_argcheck(l, lua_gettop(l) <= args, args + 1, 209 | "found too many arguments"); 210 | while (lua_gettop(l) < args) 211 | lua_pushnil(l); 212 | return json_fetch_config(l); 213 | } 214 | 215 | static int json_integer_option(lua_State *l, int optindex, int *setting, int min, int max) 216 | { 217 | char errmsg[64]; 218 | int value; 219 | if (!lua_isnil(l, optindex)) 220 | { 221 | value = luaL_checkinteger(l, optindex); 222 | snprintf(errmsg, sizeof(errmsg), "expected integer between %d and %d", min, max); 223 | luaL_argcheck(l, min <= value && value <= max, 1, errmsg); 224 | *setting = value; 225 | } 226 | lua_pushinteger(l, *setting); 227 | return 1; 228 | } 229 | 230 | static int json_enum_option(lua_State *l, int optindex, int *setting, const char **options, int bool_true) 231 | { 232 | static const char *bool_options[] = { "off", "on", NULL }; 233 | if (!options) 234 | { 235 | options = bool_options; 236 | bool_true = 1; 237 | } 238 | if (!lua_isnil(l, optindex)) 239 | { 240 | if (bool_true && lua_isboolean(l, optindex)) 241 | *setting = lua_toboolean(l, optindex) * bool_true; 242 | else 243 | *setting = luaL_checkoption(l, optindex, NULL, options); 244 | } 245 | if (bool_true && (*setting == 0 || *setting == bool_true)) 246 | lua_pushboolean(l, *setting); 247 | else 248 | lua_pushstring(l, options[*setting]); 249 | return 1; 250 | } 251 | 252 | static int json_cfg_encode_sparse_array(lua_State *l) 253 | { 254 | json_config_t *cfg = json_arg_init(l, 3); 255 | json_enum_option(l, 1, &cfg->encode_sparse_convert, NULL, 1); 256 | json_integer_option(l, 2, &cfg->encode_sparse_ratio, 0, INT_MAX); 257 | json_integer_option(l, 3, &cfg->encode_sparse_safe, 0, INT_MAX); 258 | return 3; 259 | } 260 | 261 | static int json_cfg_encode_max_depth(lua_State *l) 262 | { 263 | json_config_t *cfg = json_arg_init(l, 1); 264 | return json_integer_option(l, 1, &cfg->encode_max_depth, 1, INT_MAX); 265 | } 266 | 267 | static int json_cfg_decode_max_depth(lua_State *l) 268 | { 269 | json_config_t *cfg = json_arg_init(l, 1); 270 | return json_integer_option(l, 1, &cfg->decode_max_depth, 1, INT_MAX); 271 | } 272 | 273 | static int json_cfg_encode_number_precision(lua_State *l) 274 | { 275 | json_config_t *cfg = json_arg_init(l, 1); 276 | return json_integer_option(l, 1, &cfg->encode_number_precision, 1, 14); 277 | } 278 | 279 | static int json_cfg_encode_keep_buffer(lua_State *l) 280 | { 281 | json_config_t *cfg = json_arg_init(l, 1); 282 | int old_value; 283 | old_value = cfg->encode_keep_buffer; 284 | json_enum_option(l, 1, &cfg->encode_keep_buffer, NULL, 1); 285 | if (old_value ^ cfg->encode_keep_buffer) 286 | { 287 | if (cfg->encode_keep_buffer) 288 | strbuf_init(&cfg->encode_buf, 0); 289 | else 290 | strbuf_free(&cfg->encode_buf); 291 | } 292 | return 1; 293 | } 294 | 295 | #if defined(DISABLE_INVALID_NUMBERS) && !defined(USE_INTERNAL_FPCONV) 296 | void json_verify_invalid_number_setting(lua_State *l, int *setting) 297 | { 298 | if (*setting == 1) 299 | { 300 | *setting = 0; 301 | luaL_error(l, "Infinity, NaN, and/or hexadecimal numbers are not supported."); 302 | } 303 | } 304 | #else 305 | #define json_verify_invalid_number_setting(l, s) do { } while(0) 306 | #endif 307 | 308 | static int json_cfg_encode_invalid_numbers(lua_State *l) 309 | { 310 | static const char *options[] = { "off", "on", "null", NULL }; 311 | json_config_t *cfg = json_arg_init(l, 1); 312 | json_enum_option(l, 1, &cfg->encode_invalid_numbers, options, 1); 313 | json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers); 314 | return 1; 315 | } 316 | 317 | static int json_cfg_decode_invalid_numbers(lua_State *l) 318 | { 319 | json_config_t *cfg = json_arg_init(l, 1); 320 | json_enum_option(l, 1, &cfg->decode_invalid_numbers, NULL, 1); 321 | json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers); 322 | return 1; 323 | } 324 | 325 | // ++++ 326 | static int json_destroy_config(lua_State *l) 327 | { 328 | // json_config_t *cfg; 329 | // cfg = (json_config_t*)lua_touserdata(l, 1); 330 | if (cfg) 331 | strbuf_free(&cfg->encode_buf); 332 | cfg = NULL; 333 | return 0; 334 | } 335 | 336 | // ++++ 337 | static void json_create_config(lua_State *l) 338 | { 339 | // json_config_t *cfg; 340 | int i; 341 | cfg = (json_config_t*)lua_newuserdata(l, sizeof(*cfg)); 342 | 343 | lua_newtable(l); 344 | lua_pushcfunction(l, json_destroy_config); 345 | lua_setfield(l, -2, "__gc"); 346 | lua_setmetatable(l, -2); 347 | 348 | cfg->encode_sparse_convert = DEFAULT_SPARSE_CONVERT; 349 | cfg->encode_sparse_ratio = DEFAULT_SPARSE_RATIO; 350 | cfg->encode_sparse_safe = DEFAULT_SPARSE_SAFE; 351 | cfg->encode_max_depth = DEFAULT_ENCODE_MAX_DEPTH; 352 | cfg->decode_max_depth = DEFAULT_DECODE_MAX_DEPTH; 353 | cfg->encode_invalid_numbers = DEFAULT_ENCODE_INVALID_NUMBERS; 354 | cfg->decode_invalid_numbers = DEFAULT_DECODE_INVALID_NUMBERS; 355 | cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER; 356 | cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION; 357 | #if DEFAULT_ENCODE_KEEP_BUFFER > 0 358 | strbuf_init(&cfg->encode_buf, 0); 359 | #endif 360 | for (i = 0; i < 256; i++) 361 | cfg->ch2token[i] = T_ERROR; 362 | cfg->ch2token['{'] = T_OBJ_BEGIN; 363 | cfg->ch2token['}'] = T_OBJ_END; 364 | cfg->ch2token['['] = T_ARR_BEGIN; 365 | cfg->ch2token[']'] = T_ARR_END; 366 | cfg->ch2token[','] = T_COMMA; 367 | cfg->ch2token[':'] = T_COLON; 368 | cfg->ch2token['\0'] = T_END; 369 | cfg->ch2token[' '] = T_WHITESPACE; 370 | cfg->ch2token['\t'] = T_WHITESPACE; 371 | cfg->ch2token['\n'] = T_WHITESPACE; 372 | cfg->ch2token['\r'] = T_WHITESPACE; 373 | cfg->ch2token['f'] = T_UNKNOWN; 374 | cfg->ch2token['i'] = T_UNKNOWN; 375 | cfg->ch2token['I'] = T_UNKNOWN; 376 | cfg->ch2token['n'] = T_UNKNOWN; 377 | cfg->ch2token['N'] = T_UNKNOWN; 378 | cfg->ch2token['t'] = T_UNKNOWN; 379 | cfg->ch2token['"'] = T_UNKNOWN; 380 | cfg->ch2token['+'] = T_UNKNOWN; 381 | cfg->ch2token['-'] = T_UNKNOWN; 382 | for (i = 0; i < 10; i++) 383 | cfg->ch2token['0' + i] = T_UNKNOWN; 384 | for (i = 0; i < 256; i++) 385 | cfg->escape2char[i] = 0; 386 | cfg->escape2char['"'] = '"'; 387 | cfg->escape2char['\\'] = '\\'; 388 | cfg->escape2char['/'] = '/'; 389 | cfg->escape2char['b'] = '\b'; 390 | cfg->escape2char['t'] = '\t'; 391 | cfg->escape2char['n'] = '\n'; 392 | cfg->escape2char['f'] = '\f'; 393 | cfg->escape2char['r'] = '\r'; 394 | cfg->escape2char['u'] = 'u'; 395 | } 396 | 397 | static void json_encode_exception(lua_State *l, json_config_t *cfg, strbuf_t *json, int lindex, const char *reason) 398 | { 399 | if (!cfg->encode_keep_buffer) 400 | strbuf_free(json); 401 | luaL_error(l, "Cannot serialise %s: %s", lua_typename(l, lua_type(l, lindex)), reason); 402 | } 403 | 404 | static void json_append_string(lua_State *l, strbuf_t *json, int lindex) 405 | { 406 | const char *escstr; 407 | int i; 408 | const char *str; 409 | size_t len; 410 | str = lua_tolstring(l, lindex, &len); 411 | strbuf_ensure_empty_length(json, len * 6 + 2); 412 | strbuf_append_char_unsafe(json, '\"'); 413 | for (i = 0; i < len; i++) 414 | { 415 | escstr = char2escape[(unsigned char)str[i]]; 416 | if (escstr) 417 | strbuf_append_string(json, escstr); 418 | else 419 | strbuf_append_char_unsafe(json, str[i]); 420 | } 421 | strbuf_append_char_unsafe(json, '\"'); 422 | } 423 | 424 | static int lua_array_length(lua_State *l, json_config_t *cfg, strbuf_t *json) 425 | { 426 | double k; 427 | int max; 428 | int items; 429 | max = 0; 430 | items = 0; 431 | lua_pushnil(l); 432 | while (lua_next(l, -2) != 0) 433 | { 434 | if (lua_type(l, -2) == LUA_TNUMBER && (k = lua_tonumber(l, -2))) 435 | { 436 | if (floor(k) == k && k >= 1) 437 | { 438 | if (k > max) 439 | max = k; 440 | items++; 441 | lua_pop(l, 1); 442 | continue; 443 | } 444 | } 445 | lua_pop(l, 2); 446 | return -1; 447 | } 448 | if (cfg->encode_sparse_ratio > 0 && 449 | max > items * cfg->encode_sparse_ratio && 450 | max > cfg->encode_sparse_safe) 451 | { 452 | if (!cfg->encode_sparse_convert) 453 | json_encode_exception(l, cfg, json, -1, "excessively sparse array"); 454 | return -1; 455 | } 456 | return max; 457 | } 458 | 459 | static void json_check_encode_depth(lua_State *l, json_config_t *cfg, int current_depth, strbuf_t *json) 460 | { 461 | if (current_depth <= cfg->encode_max_depth && lua_checkstack(l, 3)) 462 | return; 463 | if (!cfg->encode_keep_buffer) 464 | strbuf_free(json); 465 | luaL_error(l, "Cannot serialise, excessive nesting (%d)", current_depth); 466 | } 467 | 468 | static void json_append_data(lua_State *l, json_config_t *cfg, int current_depth, strbuf_t *json); 469 | static void json_append_array(lua_State *l, json_config_t *cfg, int current_depth, strbuf_t *json, int array_length) 470 | { 471 | int comma, i; 472 | strbuf_append_char(json, '['); 473 | comma = 0; 474 | for (i = 1; i <= array_length; i++) 475 | { 476 | if (comma) 477 | strbuf_append_char(json, ','); 478 | else 479 | comma = 1; 480 | lua_rawgeti(l, -1, i); 481 | json_append_data(l, cfg, current_depth, json); 482 | lua_pop(l, 1); 483 | } 484 | strbuf_append_char(json, ']'); 485 | } 486 | 487 | static void json_append_number(lua_State *l, json_config_t *cfg, strbuf_t *json, int lindex) 488 | { 489 | double num = lua_tonumber(l, lindex); 490 | int len; 491 | if (cfg->encode_invalid_numbers == 0) 492 | { 493 | if (isinf(num) || isnan(num)) 494 | json_encode_exception(l, cfg, json, lindex, "must not be NaN or Inf"); 495 | } else if (cfg->encode_invalid_numbers == 1) 496 | { 497 | if (isnan(num)) 498 | { 499 | strbuf_append_mem(json, "nan", 3); 500 | return; 501 | } 502 | } else 503 | { 504 | if (isinf(num) || isnan(num)) 505 | { 506 | strbuf_append_mem(json, "null", 4); 507 | return; 508 | } 509 | } 510 | strbuf_ensure_empty_length(json, FPCONV_G_FMT_BUFSIZE); 511 | len = fpconv_g_fmt(strbuf_empty_ptr(json), num, cfg->encode_number_precision); 512 | strbuf_extend_length(json, len); 513 | } 514 | 515 | static void json_append_object(lua_State *l, json_config_t *cfg, int current_depth, strbuf_t *json) 516 | { 517 | int comma, keytype; 518 | strbuf_append_char(json, '{'); 519 | lua_pushnil(l); 520 | comma = 0; 521 | while (lua_next(l, -2) != 0) 522 | { 523 | if (comma) 524 | strbuf_append_char(json, ','); 525 | else 526 | comma = 1; 527 | keytype = lua_type(l, -2); 528 | if (keytype == LUA_TNUMBER) 529 | { 530 | strbuf_append_char(json, '"'); 531 | json_append_number(l, cfg, json, -2); 532 | strbuf_append_mem(json, "\":", 2); 533 | } else if (keytype == LUA_TSTRING) 534 | { 535 | json_append_string(l, json, -2); 536 | strbuf_append_char(json, ':'); 537 | } else 538 | { 539 | json_encode_exception(l, cfg, json, -2, "table key must be a number or string"); 540 | } 541 | json_append_data(l, cfg, current_depth, json); 542 | lua_pop(l, 1); 543 | } 544 | strbuf_append_char(json, '}'); 545 | } 546 | 547 | static void json_append_data(lua_State *l, json_config_t *cfg, int current_depth, strbuf_t *json) 548 | { 549 | int len; 550 | switch (lua_type(l, -1)) 551 | { 552 | case LUA_TSTRING: 553 | json_append_string(l, json, -1); 554 | break; 555 | case LUA_TNUMBER: 556 | json_append_number(l, cfg, json, -1); 557 | break; 558 | case LUA_TBOOLEAN: 559 | if (lua_toboolean(l, -1)) 560 | strbuf_append_mem(json, "true", 4); 561 | else 562 | strbuf_append_mem(json, "false", 5); 563 | break; 564 | case LUA_TTABLE: 565 | current_depth++; 566 | json_check_encode_depth(l, cfg, current_depth, json); 567 | len = lua_array_length(l, cfg, json); 568 | if (len > 0) 569 | json_append_array(l, cfg, current_depth, json, len); 570 | else 571 | json_append_object(l, cfg, current_depth, json); 572 | break; 573 | case LUA_TNIL: 574 | strbuf_append_mem(json, "null", 4); 575 | break; 576 | case LUA_TUSERDATA: 577 | case LUA_TFUNCTION: 578 | case LUA_TTHREAD: 579 | { 580 | unsigned long long pointer_addr = (unsigned long long)lua_topointer(l, -1); 581 | const int buff_size = 16; 582 | char str_buffer[buff_size]; 583 | int len = snprintf(str_buffer, buff_size, "%llx", pointer_addr); 584 | strbuf_append_mem(json, "\"object at 0x", 13); 585 | strbuf_append_mem(json, str_buffer, len); 586 | strbuf_append_mem(json, "\"", 1); 587 | break; 588 | } 589 | case LUA_TLIGHTUSERDATA: 590 | if (lua_touserdata(l, -1) == NULL) 591 | { 592 | strbuf_append_mem(json, "null", 4); 593 | break; 594 | } 595 | default: 596 | /* Remaining types (LUA_TLIGHTUSERDATA) cannot be serialised */ 597 | json_encode_exception(l, cfg, json, -1, "type not supported"); 598 | /* never returns */ 599 | } 600 | } 601 | 602 | static int json_encode(lua_State *l) 603 | { 604 | json_config_t *cfg = json_fetch_config(l); 605 | strbuf_t local_encode_buf; 606 | strbuf_t *encode_buf; 607 | char *json; 608 | int len; 609 | luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); 610 | if (!cfg->encode_keep_buffer) 611 | { 612 | encode_buf = &local_encode_buf; 613 | strbuf_init(encode_buf, 0); 614 | } else 615 | { 616 | encode_buf = &cfg->encode_buf; 617 | strbuf_reset(encode_buf); 618 | } 619 | json_append_data(l, cfg, 0, encode_buf); 620 | json = strbuf_string(encode_buf, &len); 621 | lua_pushlstring(l, json, len); 622 | if (!cfg->encode_keep_buffer) 623 | strbuf_free(encode_buf); 624 | return 1; 625 | } 626 | 627 | static void json_process_value(lua_State *l, json_parse_t *json, json_token_t *token); 628 | static int hexdigit2int(char hex) 629 | { 630 | if ('0' <= hex && hex <= '9') 631 | return hex - '0'; 632 | hex |= 0x20; 633 | if ('a' <= hex && hex <= 'f') 634 | return 10 + hex - 'a'; 635 | return -1; 636 | } 637 | 638 | static int decode_hex4(const char *hex) 639 | { 640 | int digit[4]; 641 | int i; 642 | for (i = 0; i < 4; i++) 643 | { 644 | digit[i] = hexdigit2int(hex[i]); 645 | if (digit[i] < 0) 646 | { 647 | return -1; 648 | } 649 | } 650 | return (digit[0] << 12) + 651 | (digit[1] << 8) + 652 | (digit[2] << 4) + 653 | digit[3]; 654 | } 655 | 656 | static int codepoint_to_utf8(char *utf8, int codepoint) 657 | { 658 | if (codepoint <= 0x7F) 659 | { 660 | utf8[0] = codepoint; 661 | return 1; 662 | } 663 | if (codepoint <= 0x7FF) 664 | { 665 | utf8[0] = (codepoint >> 6) | 0xC0; 666 | utf8[1] = (codepoint & 0x3F) | 0x80; 667 | return 2; 668 | } 669 | if (codepoint <= 0xFFFF) 670 | { 671 | utf8[0] = (codepoint >> 12) | 0xE0; 672 | utf8[1] = ((codepoint >> 6) & 0x3F) | 0x80; 673 | utf8[2] = (codepoint & 0x3F) | 0x80; 674 | return 3; 675 | } 676 | if (codepoint <= 0x1FFFFF) 677 | { 678 | utf8[0] = (codepoint >> 18) | 0xF0; 679 | utf8[1] = ((codepoint >> 12) & 0x3F) | 0x80; 680 | utf8[2] = ((codepoint >> 6) & 0x3F) | 0x80; 681 | utf8[3] = (codepoint & 0x3F) | 0x80; 682 | return 4; 683 | } 684 | return 0; 685 | } 686 | 687 | static int json_append_unicode_escape(json_parse_t *json) 688 | { 689 | char utf8[4]; 690 | int codepoint; 691 | int surrogate_low; 692 | int len; 693 | int escape_len = 6; 694 | codepoint = decode_hex4(json->ptr + 2); 695 | if (codepoint < 0) 696 | return -1; 697 | if ((codepoint & 0xF800) == 0xD800) 698 | { 699 | if (codepoint & 0x400) 700 | return -1; 701 | if (*(json->ptr + escape_len) != '\\' || 702 | *(json->ptr + escape_len + 1) != 'u') 703 | return -1; 704 | surrogate_low = decode_hex4(json->ptr + 2 + escape_len); 705 | if (surrogate_low < 0) 706 | return -1; 707 | if ((surrogate_low & 0xFC00) != 0xDC00) 708 | return -1; 709 | codepoint = (codepoint & 0x3FF) << 10; 710 | surrogate_low &= 0x3FF; 711 | codepoint = (codepoint | surrogate_low) + 0x10000; 712 | escape_len = 12; 713 | } 714 | len = codepoint_to_utf8(utf8, codepoint); 715 | if (!len) 716 | return -1; 717 | strbuf_append_mem_unsafe(json->tmp, utf8, len); 718 | json->ptr += escape_len; 719 | return 0; 720 | } 721 | 722 | static void json_set_token_error(json_token_t *token, json_parse_t *json, const char *errtype) 723 | { 724 | token->type = T_ERROR; 725 | token->index = json->ptr - json->data; 726 | token->value.string = errtype; 727 | } 728 | 729 | static void json_next_string_token(json_parse_t *json, json_token_t *token) 730 | { 731 | char *escape2char = json->cfg->escape2char; 732 | char ch; 733 | assert(*json->ptr == '"'); 734 | json->ptr++; 735 | strbuf_reset(json->tmp); 736 | while ((ch = *json->ptr) != '"') 737 | { 738 | if (!ch) 739 | { 740 | json_set_token_error(token, json, "unexpected end of string"); 741 | return; 742 | } 743 | if (ch == '\\') 744 | { 745 | ch = *(json->ptr + 1); 746 | ch = escape2char[(unsigned char)ch]; 747 | if (ch == 'u') 748 | { 749 | if (json_append_unicode_escape(json) == 0) 750 | continue; 751 | json_set_token_error(token, json, "invalid unicode escape code"); 752 | return; 753 | } 754 | if (!ch) 755 | { 756 | json_set_token_error(token, json, "invalid escape code"); 757 | return; 758 | } 759 | json->ptr++; 760 | } 761 | strbuf_append_char_unsafe(json->tmp, ch); 762 | json->ptr++; 763 | } 764 | json->ptr++; 765 | strbuf_ensure_null(json->tmp); 766 | token->type = T_STRING; 767 | token->value.string = strbuf_string(json->tmp, &token->string_len); 768 | } 769 | 770 | static int json_is_invalid_number(json_parse_t *json) 771 | { 772 | const char *p = json->ptr; 773 | if (*p == '+') 774 | return 1; 775 | if (*p == '-') 776 | p++; 777 | if (*p == '0') 778 | { 779 | int ch2 = *(p + 1); 780 | if ((ch2 | 0x20) == 'x' || ('0' <= ch2 && ch2 <= '9')) 781 | return 1; 782 | return 0; 783 | } else if (*p <= '9') 784 | { 785 | return 0; 786 | } 787 | if (!strncasecmp(p, "inf", 3)) 788 | return 1; 789 | if (!strncasecmp(p, "nan", 3)) 790 | return 1; 791 | return 0; 792 | } 793 | 794 | static void json_next_number_token(json_parse_t *json, json_token_t *token) 795 | { 796 | char *endptr; 797 | token->type = T_NUMBER; 798 | token->value.number = fpconv_strtod(json->ptr, &endptr); 799 | if (json->ptr == endptr) 800 | json_set_token_error(token, json, "invalid number"); 801 | else 802 | json->ptr = endptr; 803 | return; 804 | } 805 | 806 | static void json_next_token(json_parse_t *json, json_token_t *token) 807 | { 808 | const json_token_type_t *ch2token = json->cfg->ch2token; 809 | int ch; 810 | while (1) 811 | { 812 | ch = (unsigned char)*(json->ptr); 813 | token->type = ch2token[ch]; 814 | if (token->type != T_WHITESPACE) 815 | break; 816 | json->ptr++; 817 | } 818 | token->index = json->ptr - json->data; 819 | if (token->type == T_ERROR) 820 | { 821 | json_set_token_error(token, json, "invalid token"); 822 | return; 823 | } 824 | if (token->type == T_END) 825 | return; 826 | if (token->type != T_UNKNOWN) 827 | { 828 | json->ptr++; 829 | return; 830 | } 831 | if (ch == '"') 832 | { 833 | json_next_string_token(json, token); 834 | return; 835 | } else if (ch == '-' || ('0' <= ch && ch <= '9')) 836 | { 837 | if (!json->cfg->decode_invalid_numbers && json_is_invalid_number(json)) 838 | { 839 | json_set_token_error(token, json, "invalid number"); 840 | return; 841 | } 842 | json_next_number_token(json, token); 843 | return; 844 | } else if (!strncmp(json->ptr, "true", 4)) 845 | { 846 | token->type = T_BOOLEAN; 847 | token->value.boolean = 1; 848 | json->ptr += 4; 849 | return; 850 | } else if (!strncmp(json->ptr, "false", 5)) 851 | { 852 | token->type = T_BOOLEAN; 853 | token->value.boolean = 0; 854 | json->ptr += 5; 855 | return; 856 | } else if (!strncmp(json->ptr, "null", 4)) 857 | { 858 | token->type = T_NULL; 859 | json->ptr += 4; 860 | return; 861 | } else if (json->cfg->decode_invalid_numbers && json_is_invalid_number(json)) 862 | { 863 | json_next_number_token(json, token); 864 | return; 865 | } 866 | json_set_token_error(token, json, "invalid token"); 867 | } 868 | 869 | static void json_throw_parse_error(lua_State *l, json_parse_t *json, const char *exp, json_token_t *token) 870 | { 871 | const char *found; 872 | strbuf_free(json->tmp); 873 | if (token->type == T_ERROR) 874 | found = token->value.string; 875 | else 876 | found = json_token_type_name[token->type]; 877 | luaL_error(l, "Expected %s but found %s at character %d", exp, found, token->index + 1); 878 | } 879 | 880 | static inline void json_decode_ascend(json_parse_t *json) 881 | { 882 | json->current_depth--; 883 | } 884 | 885 | static void json_decode_descend(lua_State *l, json_parse_t *json, int slots) 886 | { 887 | json->current_depth++; 888 | if (json->current_depth <= json->cfg->decode_max_depth && 889 | lua_checkstack(l, slots)) { 890 | return; 891 | } 892 | strbuf_free(json->tmp); 893 | luaL_error(l, "Found too many nested data structures (%d) at character %ld", json->current_depth, json->ptr - json->data); 894 | } 895 | 896 | static void json_parse_object_context(lua_State *l, json_parse_t *json) 897 | { 898 | json_token_t token; 899 | json_decode_descend(l, json, 3); 900 | lua_newtable(l); 901 | json_next_token(json, &token); 902 | if (token.type == T_OBJ_END) 903 | { 904 | json_decode_ascend(json); 905 | return; 906 | } 907 | while (1) 908 | { 909 | if (token.type != T_STRING) 910 | json_throw_parse_error(l, json, "object key string", &token); 911 | lua_pushlstring(l, token.value.string, token.string_len); 912 | json_next_token(json, &token); 913 | if (token.type != T_COLON) 914 | json_throw_parse_error(l, json, "colon", &token); 915 | json_next_token(json, &token); 916 | json_process_value(l, json, &token); 917 | lua_rawset(l, -3); 918 | json_next_token(json, &token); 919 | if (token.type == T_OBJ_END) 920 | { 921 | json_decode_ascend(json); 922 | return; 923 | } 924 | if (token.type != T_COMMA) 925 | json_throw_parse_error(l, json, "comma or object end", &token); 926 | json_next_token(json, &token); 927 | } 928 | } 929 | 930 | static void json_parse_array_context(lua_State *l, json_parse_t *json) 931 | { 932 | json_token_t token; 933 | int i; 934 | json_decode_descend(l, json, 2); 935 | lua_newtable(l); 936 | json_next_token(json, &token); 937 | if (token.type == T_ARR_END) 938 | { 939 | json_decode_ascend(json); 940 | return; 941 | } 942 | for (i = 1; ; i++) 943 | { 944 | json_process_value(l, json, &token); 945 | lua_rawseti(l, -2, i); 946 | json_next_token(json, &token); 947 | if (token.type == T_ARR_END) 948 | { 949 | json_decode_ascend(json); 950 | return; 951 | } 952 | if (token.type != T_COMMA) 953 | json_throw_parse_error(l, json, "comma or array end", &token); 954 | json_next_token(json, &token); 955 | } 956 | } 957 | 958 | static void json_process_value(lua_State *l, json_parse_t *json, json_token_t *token) 959 | { 960 | switch (token->type) { 961 | case T_STRING: 962 | lua_pushlstring(l, token->value.string, token->string_len); 963 | break;; 964 | case T_NUMBER: 965 | lua_pushnumber(l, token->value.number); 966 | break;; 967 | case T_BOOLEAN: 968 | lua_pushboolean(l, token->value.boolean); 969 | break;; 970 | case T_OBJ_BEGIN: 971 | json_parse_object_context(l, json); 972 | break;; 973 | case T_ARR_BEGIN: 974 | json_parse_array_context(l, json); 975 | break;; 976 | case T_NULL: 977 | lua_pushlightuserdata(l, NULL); 978 | break;; 979 | default: 980 | json_throw_parse_error(l, json, "value", token); 981 | } 982 | } 983 | 984 | static int json_decode(lua_State *l) 985 | { 986 | json_parse_t json; 987 | json_token_t token; 988 | size_t json_len; 989 | luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); 990 | json.cfg = json_fetch_config(l); 991 | json.data = luaL_checklstring(l, 1, &json_len); 992 | json.current_depth = 0; 993 | json.ptr = json.data; 994 | if (json_len >= 2 && (!json.data[0] || !json.data[1])) 995 | luaL_error(l, "JSON parser does not support UTF-16 or UTF-32"); 996 | json.tmp = strbuf_new(json_len); 997 | json_next_token(&json, &token); 998 | json_process_value(l, &json, &token); 999 | json_next_token(&json, &token); 1000 | if (token.type != T_END) 1001 | json_throw_parse_error(l, &json, "the end", &token); 1002 | strbuf_free(json.tmp); 1003 | return 1; 1004 | } 1005 | 1006 | /* ===== INITIALISATION ===== */ 1007 | 1008 | #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 1009 | 1010 | static void luaL_setfuncs (lua_State *l, const luaL_Reg *reg, int nup) 1011 | { 1012 | int i; 1013 | 1014 | luaL_checkstack(l, nup, "too many upvalues"); 1015 | for (; reg->name != NULL; reg++) 1016 | { 1017 | for (i = 0; i < nup; i++) 1018 | lua_pushvalue(l, -nup); 1019 | lua_pushcclosure(l, reg->func, nup); 1020 | lua_setfield(l, -(nup + 2), reg->name); 1021 | } 1022 | lua_pop(l, nup); 1023 | } 1024 | #endif 1025 | 1026 | static int json_protect_conversion(lua_State *l) 1027 | { 1028 | int err; 1029 | luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); 1030 | lua_pushvalue(l, lua_upvalueindex(1)); 1031 | lua_insert(l, 1); 1032 | err = lua_pcall(l, 1, 1, 0); 1033 | if (!err) 1034 | return 1; 1035 | if (err == LUA_ERRRUN) 1036 | { 1037 | lua_pushnil(l); 1038 | lua_insert(l, -2); 1039 | return 2; 1040 | } 1041 | return luaL_error(l, "Memory allocation error in CJSON protected call"); 1042 | } 1043 | 1044 | 1045 | static int lua_cjson_new(lua_State *l) 1046 | { 1047 | int top = lua_gettop(l); 1048 | 1049 | luaL_Reg reg[] = 1050 | { 1051 | { "encode", json_encode }, 1052 | { "decode", json_decode }, 1053 | { NULL, NULL } 1054 | }; 1055 | // create table 1056 | lua_newtable(l); 1057 | 1058 | json_create_config(l); 1059 | 1060 | // fill with funcs 1061 | luaL_setfuncs(l, reg, 1); 1062 | lua_pushlightuserdata(l, NULL); 1063 | lua_setfield(l, -2, "null"); 1064 | 1065 | // pop the created table 1066 | lua_setglobal(l, CJSON_MODNAME); 1067 | 1068 | assert(top == lua_gettop(l)); 1069 | return 0; 1070 | } 1071 | 1072 | dmExtension::Result AppInitialize(dmExtension::AppParams* params) 1073 | { 1074 | return dmExtension::RESULT_OK; 1075 | } 1076 | 1077 | dmExtension::Result Initialize(dmExtension::Params* params) 1078 | { 1079 | lua_cjson_new(params->m_L); 1080 | return dmExtension::RESULT_OK; 1081 | } 1082 | 1083 | dmExtension::Result AppFinalize(dmExtension::AppParams* params) 1084 | { 1085 | return dmExtension::RESULT_OK; 1086 | } 1087 | 1088 | dmExtension::Result Finalize(dmExtension::Params* params) 1089 | { 1090 | return dmExtension::RESULT_OK; 1091 | } 1092 | 1093 | DM_DECLARE_EXTENSION(cjson, LIB_NAME, AppInitialize, AppFinalize, Initialize, 0, 0, Finalize) 1094 | 1095 | --------------------------------------------------------------------------------