diff options
| author | Mattias EngdegÄrd | 2024-03-26 16:44:09 +0100 |
|---|---|---|
| committer | Mattias EngdegÄrd | 2024-03-30 14:45:53 +0100 |
| commit | 890edfd2bb8fd79730919972cc82811b73c7f572 (patch) | |
| tree | 3860e8c46a0f44f88eb93c07a2e0faab99556351 /src | |
| parent | ab016657e7b1bd32c775da271ffb7127f86d5a23 (diff) | |
| download | emacs-890edfd2bb8fd79730919972cc82811b73c7f572.tar.gz emacs-890edfd2bb8fd79730919972cc82811b73c7f572.zip | |
New JSON encoder (bug#70007)
It is in general at least 2x faster than the old encoder and does not
depend on any external library. Using our own code also gives us
control over translation details: for example, we now have full
bignum support and tighter float formatting.
* src/json.c (json_delete, json_initialized, init_json_functions)
(json_malloc, json_free, init_json, json_out_of_memory)
(json_releae_object, check_string_without_embedded_nulls, json_check)
(json_check_utf8, lisp_to_json_nonscalar_1, lisp_to_json_nonscalar)
(lisp_to_json, json_available_p, ensure_json_available, json_insert)
(json_handle_nonlocal_exit, json_insert_callback):
Remove. Remaining uses updated.
* src/json.c (json_out_t, symset_t, struct symset_tbl)
(symset_size, make_symset_table, push_symset, pop_symset)
(cleanup_symset_tables, symset_hash, symset_expand, symset_add)
(json_out_grow_buf, cleanup_json_out, json_make_room, JSON_OUT_STR)
(json_out_str, json_out_byte, json_out_fixnum, string_not_unicode)
(json_plain_char, json_out_string, json_out_nest, json_out_unnest)
(json_out_object_cons, json_out_object_hash), json_out_array)
(json_out_float, json_out_bignum, json_out_something)
(json_out_to_string, json_serialize): New.
(Fjson_serialize, Fjson_insert):
New JSON encoder implementation.
* test/src/json-tests.el (json-serialize/object-with-duplicate-keys)
(json-serialize/string): Update tests.
Diffstat (limited to 'src')
| -rw-r--r-- | src/emacs.c | 4 | ||||
| -rw-r--r-- | src/json.c | 1071 | ||||
| -rw-r--r-- | src/lisp.h | 1 | ||||
| -rw-r--r-- | src/print.c | 1 |
4 files changed, 547 insertions, 530 deletions
diff --git a/src/emacs.c b/src/emacs.c index 87f12d3fa86..4a34bb06425 100644 --- a/src/emacs.c +++ b/src/emacs.c | |||
| @@ -2013,10 +2013,6 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem | |||
| 2013 | init_random (); | 2013 | init_random (); |
| 2014 | init_xfaces (); | 2014 | init_xfaces (); |
| 2015 | 2015 | ||
| 2016 | #if defined HAVE_JSON && !defined WINDOWSNT | ||
| 2017 | init_json (); | ||
| 2018 | #endif | ||
| 2019 | |||
| 2020 | if (!initialized) | 2016 | if (!initialized) |
| 2021 | syms_of_comp (); | 2017 | syms_of_comp (); |
| 2022 | 2018 | ||
diff --git a/src/json.c b/src/json.c index afc48c59d5a..5bc63069624 100644 --- a/src/json.c +++ b/src/json.c | |||
| @@ -25,189 +25,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 25 | #include <stdlib.h> | 25 | #include <stdlib.h> |
| 26 | #include <math.h> | 26 | #include <math.h> |
| 27 | 27 | ||
| 28 | #include <jansson.h> | ||
| 29 | |||
| 30 | #include "lisp.h" | 28 | #include "lisp.h" |
| 31 | #include "buffer.h" | 29 | #include "buffer.h" |
| 32 | #include "coding.h" | 30 | #include "coding.h" |
| 33 | 31 | ||
| 34 | #ifdef WINDOWSNT | ||
| 35 | # include <windows.h> | ||
| 36 | # include "w32common.h" | ||
| 37 | # include "w32.h" | ||
| 38 | |||
| 39 | DEF_DLL_FN (void, json_set_alloc_funcs, | ||
| 40 | (json_malloc_t malloc_fn, json_free_t free_fn)); | ||
| 41 | DEF_DLL_FN (void, json_delete, (json_t *json)); | ||
| 42 | DEF_DLL_FN (json_t *, json_array, (void)); | ||
| 43 | DEF_DLL_FN (int, json_array_append_new, (json_t *array, json_t *value)); | ||
| 44 | DEF_DLL_FN (size_t, json_array_size, (const json_t *array)); | ||
| 45 | DEF_DLL_FN (json_t *, json_object, (void)); | ||
| 46 | DEF_DLL_FN (int, json_object_set_new, | ||
| 47 | (json_t *object, const char *key, json_t *value)); | ||
| 48 | DEF_DLL_FN (json_t *, json_null, (void)); | ||
| 49 | DEF_DLL_FN (json_t *, json_true, (void)); | ||
| 50 | DEF_DLL_FN (json_t *, json_false, (void)); | ||
| 51 | DEF_DLL_FN (json_t *, json_integer, (json_int_t value)); | ||
| 52 | DEF_DLL_FN (json_t *, json_real, (double value)); | ||
| 53 | DEF_DLL_FN (json_t *, json_stringn, (const char *value, size_t len)); | ||
| 54 | DEF_DLL_FN (char *, json_dumps, (const json_t *json, size_t flags)); | ||
| 55 | DEF_DLL_FN (int, json_dump_callback, | ||
| 56 | (const json_t *json, json_dump_callback_t callback, void *data, | ||
| 57 | size_t flags)); | ||
| 58 | DEF_DLL_FN (json_t *, json_object_get, (const json_t *object, const char *key)); | ||
| 59 | |||
| 60 | /* This is called by json_decref, which is an inline function. */ | ||
| 61 | void json_delete(json_t *json) | ||
| 62 | { | ||
| 63 | fn_json_delete (json); | ||
| 64 | } | ||
| 65 | |||
| 66 | static bool json_initialized; | ||
| 67 | |||
| 68 | static bool | ||
| 69 | init_json_functions (void) | ||
| 70 | { | ||
| 71 | HMODULE library = w32_delayed_load (Qjson); | ||
| 72 | |||
| 73 | if (!library) | ||
| 74 | return false; | ||
| 75 | |||
| 76 | LOAD_DLL_FN (library, json_set_alloc_funcs); | ||
| 77 | LOAD_DLL_FN (library, json_delete); | ||
| 78 | LOAD_DLL_FN (library, json_array); | ||
| 79 | LOAD_DLL_FN (library, json_array_append_new); | ||
| 80 | LOAD_DLL_FN (library, json_array_size); | ||
| 81 | LOAD_DLL_FN (library, json_object); | ||
| 82 | LOAD_DLL_FN (library, json_object_set_new); | ||
| 83 | LOAD_DLL_FN (library, json_null); | ||
| 84 | LOAD_DLL_FN (library, json_true); | ||
| 85 | LOAD_DLL_FN (library, json_false); | ||
| 86 | LOAD_DLL_FN (library, json_integer); | ||
| 87 | LOAD_DLL_FN (library, json_real); | ||
| 88 | LOAD_DLL_FN (library, json_stringn); | ||
| 89 | LOAD_DLL_FN (library, json_dumps); | ||
| 90 | LOAD_DLL_FN (library, json_dump_callback); | ||
| 91 | LOAD_DLL_FN (library, json_object_get); | ||
| 92 | |||
| 93 | init_json (); | ||
| 94 | |||
| 95 | return true; | ||
| 96 | } | ||
| 97 | |||
| 98 | #define json_set_alloc_funcs fn_json_set_alloc_funcs | ||
| 99 | #define json_array fn_json_array | ||
| 100 | #define json_array_append_new fn_json_array_append_new | ||
| 101 | #define json_array_size fn_json_array_size | ||
| 102 | #define json_object fn_json_object | ||
| 103 | #define json_object_set_new fn_json_object_set_new | ||
| 104 | #define json_null fn_json_null | ||
| 105 | #define json_true fn_json_true | ||
| 106 | #define json_false fn_json_false | ||
| 107 | #define json_integer fn_json_integer | ||
| 108 | #define json_real fn_json_real | ||
| 109 | #define json_stringn fn_json_stringn | ||
| 110 | #define json_dumps fn_json_dumps | ||
| 111 | #define json_dump_callback fn_json_dump_callback | ||
| 112 | #define json_object_get fn_json_object_get | ||
| 113 | |||
| 114 | #endif /* WINDOWSNT */ | ||
| 115 | |||
| 116 | /* We install a custom allocator so that we can avoid objects larger | ||
| 117 | than PTRDIFF_MAX. Such objects wouldn't play well with the rest of | ||
| 118 | Emacs's codebase, which generally uses ptrdiff_t for sizes and | ||
| 119 | indices. The other functions in this file also generally assume | ||
| 120 | that size_t values never exceed PTRDIFF_MAX. | ||
| 121 | |||
| 122 | In addition, we need to use a custom allocator because on | ||
| 123 | MS-Windows we replace malloc/free with our own functions, see | ||
| 124 | w32heap.c, so we must force the library to use our allocator, or | ||
| 125 | else we won't be able to free storage allocated by the library. */ | ||
| 126 | |||
| 127 | static void * | ||
| 128 | json_malloc (size_t size) | ||
| 129 | { | ||
| 130 | if (size > PTRDIFF_MAX) | ||
| 131 | { | ||
| 132 | errno = ENOMEM; | ||
| 133 | return NULL; | ||
| 134 | } | ||
| 135 | return malloc (size); | ||
| 136 | } | ||
| 137 | |||
| 138 | static void | ||
| 139 | json_free (void *ptr) | ||
| 140 | { | ||
| 141 | free (ptr); | ||
| 142 | } | ||
| 143 | |||
| 144 | void | ||
| 145 | init_json (void) | ||
| 146 | { | ||
| 147 | json_set_alloc_funcs (json_malloc, json_free); | ||
| 148 | } | ||
| 149 | |||
| 150 | /* Note that all callers of make_string_from_utf8 and build_string_from_utf8 | ||
| 151 | below either pass only value UTF-8 strings or use the functionf for | ||
| 152 | formatting error messages; in the latter case correctness isn't | ||
| 153 | critical. */ | ||
| 154 | |||
| 155 | /* Return a unibyte string containing the sequence of UTF-8 encoding | ||
| 156 | units of the UTF-8 representation of STRING. If STRING does not | ||
| 157 | represent a sequence of Unicode scalar values, return a string with | ||
| 158 | unspecified contents. */ | ||
| 159 | |||
| 160 | static Lisp_Object | ||
| 161 | json_encode (Lisp_Object string) | ||
| 162 | { | ||
| 163 | /* FIXME: Raise an error if STRING is not a scalar value | ||
| 164 | sequence. */ | ||
| 165 | return encode_string_utf_8 (string, Qnil, false, Qt, Qt); | ||
| 166 | } | ||
| 167 | |||
| 168 | static AVOID | ||
| 169 | json_out_of_memory (void) | ||
| 170 | { | ||
| 171 | xsignal0 (Qjson_out_of_memory); | ||
| 172 | } | ||
| 173 | |||
| 174 | static void | ||
| 175 | json_release_object (void *object) | ||
| 176 | { | ||
| 177 | json_decref (object); | ||
| 178 | } | ||
| 179 | |||
| 180 | /* Signal an error if OBJECT is not a string, or if OBJECT contains | ||
| 181 | embedded null characters. */ | ||
| 182 | |||
| 183 | static void | ||
| 184 | check_string_without_embedded_nulls (Lisp_Object object) | ||
| 185 | { | ||
| 186 | CHECK_STRING (object); | ||
| 187 | CHECK_TYPE (memchr (SDATA (object), '\0', SBYTES (object)) == NULL, | ||
| 188 | Qstring_without_embedded_nulls_p, object); | ||
| 189 | } | ||
| 190 | |||
| 191 | /* Signal an error of type `json-out-of-memory' if OBJECT is | ||
| 192 | NULL. */ | ||
| 193 | |||
| 194 | static json_t * | ||
| 195 | json_check (json_t *object) | ||
| 196 | { | ||
| 197 | if (object == NULL) | ||
| 198 | json_out_of_memory (); | ||
| 199 | return object; | ||
| 200 | } | ||
| 201 | |||
| 202 | /* If STRING is not a valid UTF-8 string, signal an error of type | ||
| 203 | `wrong-type-argument'. STRING must be a unibyte string. */ | ||
| 204 | |||
| 205 | static void | ||
| 206 | json_check_utf8 (Lisp_Object string) | ||
| 207 | { | ||
| 208 | CHECK_TYPE (utf8_string_p (string), Qutf_8_string_p, string); | ||
| 209 | } | ||
| 210 | |||
| 211 | enum json_object_type { | 32 | enum json_object_type { |
| 212 | json_object_hashtable, | 33 | json_object_hashtable, |
| 213 | json_object_alist, | 34 | json_object_alist, |
| @@ -226,179 +47,6 @@ struct json_configuration { | |||
| 226 | Lisp_Object false_object; | 47 | Lisp_Object false_object; |
| 227 | }; | 48 | }; |
| 228 | 49 | ||
| 229 | static json_t *lisp_to_json (Lisp_Object, | ||
| 230 | const struct json_configuration *conf); | ||
| 231 | |||
| 232 | /* Convert a Lisp object to a nonscalar JSON object (array or object). */ | ||
| 233 | |||
| 234 | static json_t * | ||
| 235 | lisp_to_json_nonscalar_1 (Lisp_Object lisp, | ||
| 236 | const struct json_configuration *conf) | ||
| 237 | { | ||
| 238 | json_t *json; | ||
| 239 | specpdl_ref count; | ||
| 240 | |||
| 241 | if (VECTORP (lisp)) | ||
| 242 | { | ||
| 243 | ptrdiff_t size = ASIZE (lisp); | ||
| 244 | json = json_check (json_array ()); | ||
| 245 | count = SPECPDL_INDEX (); | ||
| 246 | record_unwind_protect_ptr (json_release_object, json); | ||
| 247 | for (ptrdiff_t i = 0; i < size; ++i) | ||
| 248 | { | ||
| 249 | int status | ||
| 250 | = json_array_append_new (json, lisp_to_json (AREF (lisp, i), | ||
| 251 | conf)); | ||
| 252 | if (status == -1) | ||
| 253 | json_out_of_memory (); | ||
| 254 | } | ||
| 255 | eassert (json_array_size (json) == size); | ||
| 256 | } | ||
| 257 | else if (HASH_TABLE_P (lisp)) | ||
| 258 | { | ||
| 259 | struct Lisp_Hash_Table *h = XHASH_TABLE (lisp); | ||
| 260 | json = json_check (json_object ()); | ||
| 261 | count = SPECPDL_INDEX (); | ||
| 262 | record_unwind_protect_ptr (json_release_object, json); | ||
| 263 | DOHASH (h, key, v) | ||
| 264 | { | ||
| 265 | CHECK_STRING (key); | ||
| 266 | Lisp_Object ekey = json_encode (key); | ||
| 267 | /* We can't specify the length, so the string must be | ||
| 268 | null-terminated. */ | ||
| 269 | check_string_without_embedded_nulls (ekey); | ||
| 270 | const char *key_str = SSDATA (ekey); | ||
| 271 | /* Reject duplicate keys. These are possible if the hash | ||
| 272 | table test is not `equal'. */ | ||
| 273 | if (json_object_get (json, key_str) != NULL) | ||
| 274 | wrong_type_argument (Qjson_value_p, lisp); | ||
| 275 | int status | ||
| 276 | = json_object_set_new (json, key_str, | ||
| 277 | lisp_to_json (v, conf)); | ||
| 278 | if (status == -1) | ||
| 279 | { | ||
| 280 | /* A failure can be caused either by an invalid key or | ||
| 281 | by low memory. */ | ||
| 282 | json_check_utf8 (ekey); | ||
| 283 | json_out_of_memory (); | ||
| 284 | } | ||
| 285 | } | ||
| 286 | } | ||
| 287 | else if (NILP (lisp)) | ||
| 288 | return json_check (json_object ()); | ||
| 289 | else if (CONSP (lisp)) | ||
| 290 | { | ||
| 291 | Lisp_Object tail = lisp; | ||
| 292 | json = json_check (json_object ()); | ||
| 293 | count = SPECPDL_INDEX (); | ||
| 294 | record_unwind_protect_ptr (json_release_object, json); | ||
| 295 | bool is_plist = !CONSP (XCAR (tail)); | ||
| 296 | FOR_EACH_TAIL (tail) | ||
| 297 | { | ||
| 298 | const char *key_str; | ||
| 299 | Lisp_Object value; | ||
| 300 | Lisp_Object key_symbol; | ||
| 301 | if (is_plist) | ||
| 302 | { | ||
| 303 | key_symbol = XCAR (tail); | ||
| 304 | tail = XCDR (tail); | ||
| 305 | CHECK_CONS (tail); | ||
| 306 | value = XCAR (tail); | ||
| 307 | } | ||
| 308 | else | ||
| 309 | { | ||
| 310 | Lisp_Object pair = XCAR (tail); | ||
| 311 | CHECK_CONS (pair); | ||
| 312 | key_symbol = XCAR (pair); | ||
| 313 | value = XCDR (pair); | ||
| 314 | } | ||
| 315 | CHECK_SYMBOL (key_symbol); | ||
| 316 | Lisp_Object key = SYMBOL_NAME (key_symbol); | ||
| 317 | /* We can't specify the length, so the string must be | ||
| 318 | null-terminated. */ | ||
| 319 | check_string_without_embedded_nulls (key); | ||
| 320 | key_str = SSDATA (key); | ||
| 321 | /* In plists, ensure leading ":" in keys is stripped. It | ||
| 322 | will be reconstructed later in `json_to_lisp'.*/ | ||
| 323 | if (is_plist && ':' == key_str[0] && key_str[1]) | ||
| 324 | { | ||
| 325 | key_str = &key_str[1]; | ||
| 326 | } | ||
| 327 | /* Only add element if key is not already present. */ | ||
| 328 | if (json_object_get (json, key_str) == NULL) | ||
| 329 | { | ||
| 330 | int status | ||
| 331 | = json_object_set_new (json, key_str, lisp_to_json (value, | ||
| 332 | conf)); | ||
| 333 | if (status == -1) | ||
| 334 | json_out_of_memory (); | ||
| 335 | } | ||
| 336 | } | ||
| 337 | CHECK_LIST_END (tail, lisp); | ||
| 338 | } | ||
| 339 | else | ||
| 340 | wrong_type_argument (Qjson_value_p, lisp); | ||
| 341 | |||
| 342 | clear_unwind_protect (count); | ||
| 343 | unbind_to (count, Qnil); | ||
| 344 | return json; | ||
| 345 | } | ||
| 346 | |||
| 347 | /* Convert LISP to a nonscalar JSON object (array or object). Signal | ||
| 348 | an error of type `wrong-type-argument' if LISP is not a vector, | ||
| 349 | hashtable, alist, or plist. */ | ||
| 350 | |||
| 351 | static json_t * | ||
| 352 | lisp_to_json_nonscalar (Lisp_Object lisp, | ||
| 353 | const struct json_configuration *conf) | ||
| 354 | { | ||
| 355 | if (++lisp_eval_depth > max_lisp_eval_depth) | ||
| 356 | xsignal0 (Qjson_object_too_deep); | ||
| 357 | json_t *json = lisp_to_json_nonscalar_1 (lisp, conf); | ||
| 358 | --lisp_eval_depth; | ||
| 359 | return json; | ||
| 360 | } | ||
| 361 | |||
| 362 | /* Convert LISP to any JSON object. Signal an error of type | ||
| 363 | `wrong-type-argument' if the type of LISP can't be converted to a | ||
| 364 | JSON object. */ | ||
| 365 | |||
| 366 | static json_t * | ||
| 367 | lisp_to_json (Lisp_Object lisp, const struct json_configuration *conf) | ||
| 368 | { | ||
| 369 | if (EQ (lisp, conf->null_object)) | ||
| 370 | return json_check (json_null ()); | ||
| 371 | else if (EQ (lisp, conf->false_object)) | ||
| 372 | return json_check (json_false ()); | ||
| 373 | else if (EQ (lisp, Qt)) | ||
| 374 | return json_check (json_true ()); | ||
| 375 | else if (INTEGERP (lisp)) | ||
| 376 | { | ||
| 377 | intmax_t low = TYPE_MINIMUM (json_int_t); | ||
| 378 | intmax_t high = TYPE_MAXIMUM (json_int_t); | ||
| 379 | intmax_t value = check_integer_range (lisp, low, high); | ||
| 380 | return json_check (json_integer (value)); | ||
| 381 | } | ||
| 382 | else if (FLOATP (lisp)) | ||
| 383 | return json_check (json_real (XFLOAT_DATA (lisp))); | ||
| 384 | else if (STRINGP (lisp)) | ||
| 385 | { | ||
| 386 | Lisp_Object encoded = json_encode (lisp); | ||
| 387 | json_t *json = json_stringn (SSDATA (encoded), SBYTES (encoded)); | ||
| 388 | if (json == NULL) | ||
| 389 | { | ||
| 390 | /* A failure can be caused either by an invalid string or by | ||
| 391 | low memory. */ | ||
| 392 | json_check_utf8 (encoded); | ||
| 393 | json_out_of_memory (); | ||
| 394 | } | ||
| 395 | return json; | ||
| 396 | } | ||
| 397 | |||
| 398 | /* LISP now must be a vector, hashtable, alist, or plist. */ | ||
| 399 | return lisp_to_json_nonscalar (lisp, conf); | ||
| 400 | } | ||
| 401 | |||
| 402 | static void | 50 | static void |
| 403 | json_parse_args (ptrdiff_t nargs, | 51 | json_parse_args (ptrdiff_t nargs, |
| 404 | Lisp_Object *args, | 52 | Lisp_Object *args, |
| @@ -450,158 +98,533 @@ json_parse_args (ptrdiff_t nargs, | |||
| 450 | } | 98 | } |
| 451 | } | 99 | } |
| 452 | 100 | ||
| 453 | static bool | 101 | /* FIXME: Remove completely. */ |
| 454 | json_available_p (void) | 102 | DEFUN ("json--available-p", Fjson__available_p, Sjson__available_p, 0, 0, NULL, |
| 103 | doc: /* Return non-nil if libjansson is available (internal use only). */) | ||
| 104 | (void) | ||
| 455 | { | 105 | { |
| 456 | #ifdef WINDOWSNT | 106 | return Qt; |
| 457 | if (!json_initialized) | ||
| 458 | { | ||
| 459 | Lisp_Object status; | ||
| 460 | json_initialized = init_json_functions (); | ||
| 461 | status = json_initialized ? Qt : Qnil; | ||
| 462 | Vlibrary_cache = Fcons (Fcons (Qjson, status), Vlibrary_cache); | ||
| 463 | } | ||
| 464 | return json_initialized; | ||
| 465 | #else /* !WINDOWSNT */ | ||
| 466 | return true; | ||
| 467 | #endif | ||
| 468 | } | 107 | } |
| 469 | 108 | ||
| 470 | #ifdef WINDOWSNT | 109 | /* JSON encoding context. */ |
| 110 | typedef struct { | ||
| 111 | char *buf; | ||
| 112 | ptrdiff_t size; /* number of bytes in buf */ | ||
| 113 | ptrdiff_t capacity; /* allocated size of buf */ | ||
| 114 | ptrdiff_t chars_delta; /* size - {number of characters in buf} */ | ||
| 115 | |||
| 116 | int maxdepth; | ||
| 117 | struct symset_tbl *ss_table; /* table used by containing object */ | ||
| 118 | struct json_configuration conf; | ||
| 119 | } json_out_t; | ||
| 120 | |||
| 121 | /* Set of symbols. */ | ||
| 122 | typedef struct { | ||
| 123 | ptrdiff_t count; /* symbols in table */ | ||
| 124 | int bits; /* log2(table size) */ | ||
| 125 | struct symset_tbl *table; /* heap-allocated table */ | ||
| 126 | } symset_t; | ||
| 127 | |||
| 128 | struct symset_tbl | ||
| 129 | { | ||
| 130 | /* Table used by the containing object if any, so that we can free all | ||
| 131 | tables if an error occurs. */ | ||
| 132 | struct symset_tbl *up; | ||
| 133 | /* Table of symbols (2**bits elements), Qunbound where unused. */ | ||
| 134 | Lisp_Object entries[]; | ||
| 135 | }; | ||
| 136 | |||
| 137 | static inline ptrdiff_t | ||
| 138 | symset_size (int bits) | ||
| 139 | { | ||
| 140 | return (ptrdiff_t)1 << bits; | ||
| 141 | } | ||
| 142 | |||
| 143 | static struct symset_tbl * | ||
| 144 | make_symset_table (int bits, struct symset_tbl *up) | ||
| 145 | { | ||
| 146 | int maxbits = min (SIZE_WIDTH - 2 - (word_size < 8 ? 2 : 3), 32); | ||
| 147 | if (bits > maxbits) | ||
| 148 | memory_full (PTRDIFF_MAX); /* Will never happen in practice. */ | ||
| 149 | struct symset_tbl *st = xnmalloc (sizeof *st->entries << bits, sizeof *st); | ||
| 150 | st->up = up; | ||
| 151 | ptrdiff_t size = symset_size (bits); | ||
| 152 | for (ptrdiff_t i = 0; i < size; i++) | ||
| 153 | st->entries[i] = Qunbound; | ||
| 154 | return st; | ||
| 155 | } | ||
| 156 | |||
| 157 | /* Create a new symset to use for a new object. */ | ||
| 158 | static symset_t | ||
| 159 | push_symset (json_out_t *jo) | ||
| 160 | { | ||
| 161 | int bits = 4; | ||
| 162 | struct symset_tbl *tbl = make_symset_table (bits, jo->ss_table); | ||
| 163 | jo->ss_table = tbl; | ||
| 164 | return (symset_t){ .count = 0, .bits = bits, .table = tbl }; | ||
| 165 | } | ||
| 166 | |||
| 167 | /* Destroy the current symset. */ | ||
| 471 | static void | 168 | static void |
| 472 | ensure_json_available (void) | 169 | pop_symset (json_out_t *jo, symset_t *ss) |
| 473 | { | 170 | { |
| 474 | if (!json_available_p ()) | 171 | jo->ss_table = ss->table->up; |
| 475 | Fsignal (Qjson_unavailable, | 172 | xfree (ss->table); |
| 476 | list1 (build_unibyte_string ("jansson library not found"))); | ||
| 477 | } | 173 | } |
| 478 | #endif | ||
| 479 | 174 | ||
| 480 | DEFUN ("json--available-p", Fjson__available_p, Sjson__available_p, 0, 0, NULL, | 175 | /* Remove all heap-allocated symset tables, in case an error occurred. */ |
| 481 | doc: /* Return non-nil if libjansson is available (internal use only). */) | 176 | static void |
| 482 | (void) | 177 | cleanup_symset_tables (struct symset_tbl *st) |
| 483 | { | 178 | { |
| 484 | return json_available_p () ? Qt : Qnil; | 179 | while (st) |
| 180 | { | ||
| 181 | struct symset_tbl *up = st->up; | ||
| 182 | xfree (st); | ||
| 183 | st = up; | ||
| 184 | } | ||
| 485 | } | 185 | } |
| 486 | 186 | ||
| 487 | DEFUN ("json-serialize", Fjson_serialize, Sjson_serialize, 1, MANY, | 187 | static inline uint32_t |
| 488 | NULL, | 188 | symset_hash (Lisp_Object sym, int bits) |
| 489 | doc: /* Return the JSON representation of OBJECT as a string. | 189 | { |
| 190 | return knuth_hash (reduce_emacs_uint_to_hash_hash (XHASH (sym)), bits); | ||
| 191 | } | ||
| 490 | 192 | ||
| 491 | OBJECT must be t, a number, string, vector, hashtable, alist, plist, | 193 | /* Enlarge the table used by a symset. */ |
| 492 | or the Lisp equivalents to the JSON null and false values, and its | 194 | static NO_INLINE void |
| 493 | elements must recursively consist of the same kinds of values. t will | 195 | symset_expand (symset_t *ss) |
| 494 | be converted to the JSON true value. Vectors will be converted to | 196 | { |
| 495 | JSON arrays, whereas hashtables, alists and plists are converted to | 197 | struct symset_tbl *old_table = ss->table; |
| 496 | JSON objects. Hashtable keys must be strings without embedded null | 198 | int oldbits = ss->bits; |
| 497 | characters and must be unique within each object. Alist and plist | 199 | ptrdiff_t oldsize = symset_size (oldbits); |
| 498 | keys must be symbols; if a key is duplicate, the first instance is | 200 | int bits = oldbits + 1; |
| 499 | used. | 201 | ss->bits = bits; |
| 202 | ss->table = make_symset_table (bits, old_table->up); | ||
| 203 | /* Move all entries from the old table to the new one. */ | ||
| 204 | ptrdiff_t mask = symset_size (bits) - 1; | ||
| 205 | struct symset_tbl *tbl = ss->table; | ||
| 206 | for (ptrdiff_t i = 0; i < oldsize; i++) | ||
| 207 | { | ||
| 208 | Lisp_Object sym = old_table->entries[i]; | ||
| 209 | if (!BASE_EQ (sym, Qunbound)) | ||
| 210 | { | ||
| 211 | ptrdiff_t j = symset_hash (sym, bits); | ||
| 212 | while (!BASE_EQ (tbl->entries[j], Qunbound)) | ||
| 213 | j = (j + 1) & mask; | ||
| 214 | tbl->entries[j] = sym; | ||
| 215 | } | ||
| 216 | } | ||
| 217 | xfree (old_table); | ||
| 218 | } | ||
| 500 | 219 | ||
| 501 | The Lisp equivalents to the JSON null and false values are | 220 | /* If sym is in ss, return false; otherwise add it and return true. |
| 502 | configurable in the arguments ARGS, a list of keyword/argument pairs: | 221 | Comparison is done by strict identity. */ |
| 222 | static inline bool | ||
| 223 | symset_add (json_out_t *jo, symset_t *ss, Lisp_Object sym) | ||
| 224 | { | ||
| 225 | /* Make sure we don't fill more than half of the table. */ | ||
| 226 | if (ss->count >= (symset_size (ss->bits) >> 1)) | ||
| 227 | { | ||
| 228 | symset_expand (ss); | ||
| 229 | jo->ss_table = ss->table; | ||
| 230 | } | ||
| 503 | 231 | ||
| 504 | The keyword argument `:null-object' specifies which object to use | 232 | struct symset_tbl *tbl = ss->table; |
| 505 | to represent a JSON null value. It defaults to `:null'. | 233 | ptrdiff_t mask = symset_size (ss->bits) - 1; |
| 234 | for (ptrdiff_t i = symset_hash (sym, ss->bits); ; i = (i + 1) & mask) | ||
| 235 | { | ||
| 236 | Lisp_Object s = tbl->entries[i]; | ||
| 237 | if (BASE_EQ (s, sym)) | ||
| 238 | return false; /* Previous occurrence found. */ | ||
| 239 | if (BASE_EQ (s, Qunbound)) | ||
| 240 | { | ||
| 241 | /* Not in set, add it. */ | ||
| 242 | tbl->entries[i] = sym; | ||
| 243 | ss->count++; | ||
| 244 | return true; | ||
| 245 | } | ||
| 246 | } | ||
| 247 | } | ||
| 506 | 248 | ||
| 507 | The keyword argument `:false-object' specifies which object to use to | 249 | static NO_INLINE void |
| 508 | represent a JSON false value. It defaults to `:false'. | 250 | json_out_grow_buf (json_out_t *jo, ptrdiff_t bytes) |
| 251 | { | ||
| 252 | ptrdiff_t need = jo->size + bytes; | ||
| 253 | ptrdiff_t new_size = max (jo->capacity, 512); | ||
| 254 | while (new_size < need) | ||
| 255 | new_size <<= 1; | ||
| 256 | jo->buf = xrealloc (jo->buf, new_size); | ||
| 257 | jo->capacity = new_size; | ||
| 258 | } | ||
| 509 | 259 | ||
| 510 | In you specify the same value for `:null-object' and `:false-object', | 260 | static void |
| 511 | a potentially ambiguous situation, the JSON output will not contain | 261 | cleanup_json_out (void *arg) |
| 512 | any JSON false values. | ||
| 513 | usage: (json-serialize OBJECT &rest ARGS) */) | ||
| 514 | (ptrdiff_t nargs, Lisp_Object *args) | ||
| 515 | { | 262 | { |
| 516 | specpdl_ref count = SPECPDL_INDEX (); | 263 | json_out_t *jo = arg; |
| 264 | xfree (jo->buf); | ||
| 265 | jo->buf = NULL; | ||
| 266 | cleanup_symset_tables (jo->ss_table); | ||
| 267 | } | ||
| 517 | 268 | ||
| 518 | #ifdef WINDOWSNT | 269 | /* Make room for `bytes` more bytes in buffer. */ |
| 519 | ensure_json_available (); | 270 | static void |
| 520 | #endif | 271 | json_make_room (json_out_t *jo, ptrdiff_t bytes) |
| 272 | { | ||
| 273 | if (bytes > jo->capacity - jo->size) | ||
| 274 | json_out_grow_buf (jo, bytes); | ||
| 275 | } | ||
| 521 | 276 | ||
| 522 | struct json_configuration conf = | 277 | #define JSON_OUT_STR(jo, str) (json_out_str (jo, str, sizeof (str) - 1)) |
| 523 | {json_object_hashtable, json_array_array, QCnull, QCfalse}; | ||
| 524 | json_parse_args (nargs - 1, args + 1, &conf, false); | ||
| 525 | 278 | ||
| 526 | json_t *json = lisp_to_json (args[0], &conf); | 279 | /* Add `bytes` bytes from `str` to the buffer. */ |
| 527 | record_unwind_protect_ptr (json_release_object, json); | 280 | static void |
| 281 | json_out_str (json_out_t *jo, const char *str, size_t bytes) | ||
| 282 | { | ||
| 283 | json_make_room (jo, bytes); | ||
| 284 | memcpy (jo->buf + jo->size, str, bytes); | ||
| 285 | jo->size += bytes; | ||
| 286 | } | ||
| 528 | 287 | ||
| 529 | char *string = json_dumps (json, JSON_COMPACT | JSON_ENCODE_ANY); | 288 | static void |
| 530 | if (string == NULL) | 289 | json_out_byte (json_out_t *jo, unsigned char c) |
| 531 | json_out_of_memory (); | 290 | { |
| 532 | record_unwind_protect_ptr (json_free, string); | 291 | json_make_room (jo, 1); |
| 292 | jo->buf[jo->size++] = c; | ||
| 293 | } | ||
| 533 | 294 | ||
| 534 | return unbind_to (count, build_string_from_utf8 (string)); | 295 | static void |
| 296 | json_out_fixnum (json_out_t *jo, EMACS_INT x) | ||
| 297 | { | ||
| 298 | char buf[INT_BUFSIZE_BOUND (EMACS_INT)]; | ||
| 299 | char *end = buf + sizeof buf; | ||
| 300 | char *p = fixnum_to_string (x, buf, end); | ||
| 301 | json_out_str (jo, p, end - p); | ||
| 535 | } | 302 | } |
| 536 | 303 | ||
| 537 | struct json_buffer_and_size | 304 | static AVOID |
| 305 | string_not_unicode (Lisp_Object obj) | ||
| 538 | { | 306 | { |
| 539 | const char *buffer; | 307 | /* FIXME: this is just for compatibility with existing tests, it's not |
| 540 | ptrdiff_t size; | 308 | a very descriptive error. */ |
| 541 | /* This tracks how many bytes were inserted by the callback since | 309 | wrong_type_argument (Qjson_value_p, obj); |
| 542 | json_dump_callback was called. */ | 310 | } |
| 543 | ptrdiff_t inserted_bytes; | 311 | |
| 312 | static const unsigned char json_plain_char[256] = { | ||
| 313 | /* 32 chars/line: 1 for printable ASCII + DEL except " and \, 0 elsewhere */ | ||
| 314 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 00-1f */ | ||
| 315 | 1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 20-3f */ | ||
| 316 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1, /* 40-5f */ | ||
| 317 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 60-7f */ | ||
| 318 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 80-9f */ | ||
| 319 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* a0-bf */ | ||
| 320 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* c0-df */ | ||
| 321 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* e0-ff */ | ||
| 544 | }; | 322 | }; |
| 545 | 323 | ||
| 546 | static Lisp_Object | 324 | static void |
| 547 | json_insert (void *data) | 325 | json_out_string (json_out_t *jo, Lisp_Object str, int skip) |
| 326 | { | ||
| 327 | /* FIXME: this code is slow, make faster! */ | ||
| 328 | |||
| 329 | static const char hexchar[16] = "0123456789ABCDEF"; | ||
| 330 | ptrdiff_t len = SBYTES (str); | ||
| 331 | json_make_room (jo, len + 2); | ||
| 332 | json_out_byte (jo, '"'); | ||
| 333 | unsigned char *p = SDATA (str); | ||
| 334 | unsigned char *end = p + len; | ||
| 335 | p += skip; | ||
| 336 | while (p < end) | ||
| 337 | { | ||
| 338 | unsigned char c = *p; | ||
| 339 | if (json_plain_char[c]) | ||
| 340 | { | ||
| 341 | json_out_byte (jo, c); | ||
| 342 | p++; | ||
| 343 | } | ||
| 344 | else if (c > 0x7f) | ||
| 345 | { | ||
| 346 | if (STRING_MULTIBYTE (str)) | ||
| 347 | { | ||
| 348 | int n; | ||
| 349 | if (c <= 0xc1) | ||
| 350 | string_not_unicode (str); | ||
| 351 | if (c <= 0xdf) | ||
| 352 | n = 2; | ||
| 353 | else if (c <= 0xef) | ||
| 354 | { | ||
| 355 | int v = (((c & 0x0f) << 12) | ||
| 356 | + ((p[1] & 0x3f) << 6) + (p[2] & 0x3f)); | ||
| 357 | if (char_surrogate_p (v)) | ||
| 358 | string_not_unicode (str); | ||
| 359 | n = 3; | ||
| 360 | } | ||
| 361 | else if (c <= 0xf7) | ||
| 362 | { | ||
| 363 | int v = (((c & 0x07) << 18) | ||
| 364 | + ((p[1] & 0x3f) << 12) | ||
| 365 | + ((p[2] & 0x3f) << 6) | ||
| 366 | + (p[3] & 0x3f)); | ||
| 367 | if (v > MAX_UNICODE_CHAR) | ||
| 368 | string_not_unicode (str); | ||
| 369 | n = 4; | ||
| 370 | } | ||
| 371 | else | ||
| 372 | string_not_unicode (str); | ||
| 373 | json_out_str (jo, (const char *)p, n); | ||
| 374 | jo->chars_delta += n - 1; | ||
| 375 | p += n; | ||
| 376 | } | ||
| 377 | else | ||
| 378 | string_not_unicode (str); | ||
| 379 | } | ||
| 380 | else | ||
| 381 | { | ||
| 382 | json_out_byte (jo, '\\'); | ||
| 383 | switch (c) | ||
| 384 | { | ||
| 385 | case '"': | ||
| 386 | case '\\': json_out_byte (jo, c); break; | ||
| 387 | case '\b': json_out_byte (jo, 'b'); break; | ||
| 388 | case '\t': json_out_byte (jo, 't'); break; | ||
| 389 | case '\n': json_out_byte (jo, 'n'); break; | ||
| 390 | case '\f': json_out_byte (jo, 'f'); break; | ||
| 391 | case '\r': json_out_byte (jo, 'r'); break; | ||
| 392 | default: | ||
| 393 | { | ||
| 394 | char hex[5] = { 'u', '0', '0', | ||
| 395 | hexchar[c >> 4], hexchar[c & 0xf] }; | ||
| 396 | json_out_str (jo, hex, 5); | ||
| 397 | break; | ||
| 398 | } | ||
| 399 | } | ||
| 400 | p++; | ||
| 401 | } | ||
| 402 | } | ||
| 403 | json_out_byte (jo, '"'); | ||
| 404 | } | ||
| 405 | |||
| 406 | static void | ||
| 407 | json_out_nest (json_out_t *jo) | ||
| 408 | { | ||
| 409 | --jo->maxdepth; | ||
| 410 | if (jo->maxdepth < 0) | ||
| 411 | error ("Maximum JSON serialisation depth exceeded"); | ||
| 412 | } | ||
| 413 | |||
| 414 | static void | ||
| 415 | json_out_unnest (json_out_t *jo) | ||
| 548 | { | 416 | { |
| 549 | struct json_buffer_and_size *buffer_and_size = data; | 417 | ++jo->maxdepth; |
| 550 | ptrdiff_t len = buffer_and_size->size; | 418 | } |
| 551 | ptrdiff_t inserted_bytes = buffer_and_size->inserted_bytes; | ||
| 552 | ptrdiff_t gap_size = GAP_SIZE - inserted_bytes; | ||
| 553 | 419 | ||
| 554 | /* Enlarge the gap if necessary. */ | 420 | static void json_out_something (json_out_t *jo, Lisp_Object obj); |
| 555 | if (gap_size < len) | ||
| 556 | make_gap (len - gap_size); | ||
| 557 | 421 | ||
| 558 | /* Copy this chunk of data into the gap. */ | 422 | static void |
| 559 | memcpy ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE + inserted_bytes, | 423 | json_out_object_cons (json_out_t *jo, Lisp_Object obj) |
| 560 | buffer_and_size->buffer, len); | 424 | { |
| 561 | buffer_and_size->inserted_bytes += len; | 425 | json_out_nest (jo); |
| 562 | return Qnil; | 426 | symset_t ss = push_symset (jo); |
| 427 | json_out_byte (jo, '{'); | ||
| 428 | bool is_alist = CONSP (XCAR (obj)); | ||
| 429 | bool first = true; | ||
| 430 | Lisp_Object tail = obj; | ||
| 431 | FOR_EACH_TAIL (tail) | ||
| 432 | { | ||
| 433 | Lisp_Object key; | ||
| 434 | Lisp_Object value; | ||
| 435 | if (is_alist) | ||
| 436 | { | ||
| 437 | Lisp_Object pair = XCAR (tail); | ||
| 438 | CHECK_CONS (pair); | ||
| 439 | key = XCAR (pair); | ||
| 440 | value = XCDR (pair); | ||
| 441 | } | ||
| 442 | else | ||
| 443 | { | ||
| 444 | key = XCAR (tail); | ||
| 445 | tail = XCDR (tail); | ||
| 446 | CHECK_CONS (tail); | ||
| 447 | value = XCAR (tail); | ||
| 448 | } | ||
| 449 | key = maybe_remove_pos_from_symbol (key); | ||
| 450 | CHECK_TYPE (BARE_SYMBOL_P (key), Qsymbolp, key); | ||
| 451 | |||
| 452 | if (symset_add (jo, &ss, key)) | ||
| 453 | { | ||
| 454 | if (!first) | ||
| 455 | json_out_byte (jo, ','); | ||
| 456 | first = false; | ||
| 457 | |||
| 458 | Lisp_Object key_str = SYMBOL_NAME (key); | ||
| 459 | const char *str = SSDATA (key_str); | ||
| 460 | /* Skip leading ':' in plist keys. */ | ||
| 461 | int skip = !is_alist && str[0] == ':' && str[1] ? 1 : 0; | ||
| 462 | json_out_string (jo, key_str, skip); | ||
| 463 | json_out_byte (jo, ':'); | ||
| 464 | json_out_something (jo, value); | ||
| 465 | } | ||
| 466 | } | ||
| 467 | CHECK_LIST_END (tail, obj); | ||
| 468 | json_out_byte (jo, '}'); | ||
| 469 | pop_symset (jo, &ss); | ||
| 470 | json_out_unnest (jo); | ||
| 563 | } | 471 | } |
| 564 | 472 | ||
| 565 | static Lisp_Object | 473 | static void |
| 566 | json_handle_nonlocal_exit (enum nonlocal_exit type, Lisp_Object data) | 474 | json_out_object_hash (json_out_t *jo, Lisp_Object obj) |
| 567 | { | 475 | { |
| 568 | switch (type) | 476 | json_out_nest (jo); |
| 477 | json_out_byte (jo, '{'); | ||
| 478 | struct Lisp_Hash_Table *h = XHASH_TABLE (obj); | ||
| 479 | bool first = true; | ||
| 480 | DOHASH (h, k, v) | ||
| 569 | { | 481 | { |
| 570 | case NONLOCAL_EXIT_SIGNAL: | 482 | if (!first) |
| 571 | return data; | 483 | json_out_byte (jo, ','); |
| 572 | case NONLOCAL_EXIT_THROW: | 484 | first = false; |
| 573 | return Fcons (Qno_catch, data); | 485 | CHECK_STRING (k); |
| 574 | default: | 486 | /* It's the user's responsibility to ensure that hash keys are |
| 575 | eassume (false); | 487 | unique; we don't check for it. */ |
| 488 | json_out_string (jo, k, 0); | ||
| 489 | json_out_byte (jo, ':'); | ||
| 490 | json_out_something (jo, v); | ||
| 576 | } | 491 | } |
| 492 | json_out_byte (jo, '}'); | ||
| 493 | json_out_unnest (jo); | ||
| 494 | |||
| 577 | } | 495 | } |
| 578 | 496 | ||
| 579 | struct json_insert_data | 497 | static void |
| 498 | json_out_array (json_out_t *jo, Lisp_Object obj) | ||
| 580 | { | 499 | { |
| 581 | /* This tracks how many bytes were inserted by the callback since | 500 | json_out_nest (jo); |
| 582 | json_dump_callback was called. */ | 501 | json_out_byte (jo, '['); |
| 583 | ptrdiff_t inserted_bytes; | 502 | ptrdiff_t n = ASIZE (obj); |
| 584 | /* nil if json_insert succeeded, otherwise the symbol | 503 | for (ptrdiff_t i = 0; i < n; i++) |
| 585 | Qcatch_all_memory_full or a cons (ERROR-SYMBOL . ERROR-DATA). */ | 504 | { |
| 586 | Lisp_Object error; | 505 | if (i > 0) |
| 587 | }; | 506 | json_out_byte (jo, ','); |
| 507 | json_out_something (jo, AREF (obj, i)); | ||
| 508 | } | ||
| 509 | json_out_byte (jo, ']'); | ||
| 510 | json_out_unnest (jo); | ||
| 511 | } | ||
| 588 | 512 | ||
| 589 | /* Callback for json_dump_callback that inserts a JSON representation | 513 | static void |
| 590 | as a unibyte string into the gap. DATA must point to a structure | 514 | json_out_float (json_out_t *jo, Lisp_Object f) |
| 591 | of type json_insert_data. This function may not exit nonlocally. | 515 | { |
| 592 | It catches all nonlocal exits and stores them in data->error for | 516 | double x = XFLOAT_DATA (f); |
| 593 | reraising. */ | 517 | if (!isfinite (x)) |
| 518 | signal_error ("JSON does not allow Inf or NaN", f); | ||
| 519 | /* As luck has it, float_to_string emits correct JSON float syntax for | ||
| 520 | all numbers (because Vfloat_output_format is Qnil). */ | ||
| 521 | json_make_room (jo, FLOAT_TO_STRING_BUFSIZE); | ||
| 522 | int n = float_to_string (jo->buf + jo->size, x); | ||
| 523 | jo->size += n; | ||
| 524 | } | ||
| 594 | 525 | ||
| 595 | static int | 526 | static void |
| 596 | json_insert_callback (const char *buffer, size_t size, void *data) | 527 | json_out_bignum (json_out_t *jo, Lisp_Object x) |
| 597 | { | 528 | { |
| 598 | struct json_insert_data *d = data; | 529 | int base = 10; |
| 599 | struct json_buffer_and_size buffer_and_size | 530 | ptrdiff_t size = bignum_bufsize (x, base); |
| 600 | = {.buffer = buffer, .size = size, .inserted_bytes = d->inserted_bytes}; | 531 | json_make_room (jo, size); |
| 601 | d->error = internal_catch_all (json_insert, &buffer_and_size, | 532 | int n = bignum_to_c_string (jo->buf + jo->size, size, x, base); |
| 602 | json_handle_nonlocal_exit); | 533 | jo->size += n; |
| 603 | d->inserted_bytes = buffer_and_size.inserted_bytes; | 534 | } |
| 604 | return NILP (d->error) ? 0 : -1; | 535 | |
| 536 | static void | ||
| 537 | json_out_something (json_out_t *jo, Lisp_Object obj) | ||
| 538 | { | ||
| 539 | if (EQ (obj, jo->conf.null_object)) | ||
| 540 | JSON_OUT_STR (jo, "null"); | ||
| 541 | else if (EQ (obj, jo->conf.false_object)) | ||
| 542 | JSON_OUT_STR (jo, "false"); | ||
| 543 | else if (EQ (obj, Qt)) | ||
| 544 | JSON_OUT_STR (jo, "true"); | ||
| 545 | else if (NILP (obj)) | ||
| 546 | JSON_OUT_STR (jo, "{}"); | ||
| 547 | else if (FIXNUMP (obj)) | ||
| 548 | json_out_fixnum (jo, XFIXNUM (obj)); | ||
| 549 | else if (STRINGP (obj)) | ||
| 550 | json_out_string (jo, obj, 0); | ||
| 551 | else if (CONSP (obj)) | ||
| 552 | json_out_object_cons (jo, obj); | ||
| 553 | else if (FLOATP (obj)) | ||
| 554 | json_out_float (jo, obj); | ||
| 555 | else if (HASH_TABLE_P (obj)) | ||
| 556 | json_out_object_hash (jo, obj); | ||
| 557 | else if (VECTORP (obj)) | ||
| 558 | json_out_array (jo, obj); | ||
| 559 | else if (BIGNUMP (obj)) | ||
| 560 | json_out_bignum (jo, obj); | ||
| 561 | else | ||
| 562 | wrong_type_argument (Qjson_value_p, obj); | ||
| 563 | } | ||
| 564 | |||
| 565 | static Lisp_Object | ||
| 566 | json_out_to_string (json_out_t *jo) | ||
| 567 | { | ||
| 568 | /* FIXME: should this be a unibyte or multibyte string? | ||
| 569 | Right now we make a multibyte string for test compatibility, | ||
| 570 | but we are really encoding so unibyte would make more sense. */ | ||
| 571 | ptrdiff_t nchars = jo->size - jo->chars_delta; | ||
| 572 | return make_multibyte_string (jo->buf, nchars, jo->size); | ||
| 573 | } | ||
| 574 | |||
| 575 | static void | ||
| 576 | json_serialize (json_out_t *jo, Lisp_Object object, | ||
| 577 | ptrdiff_t nargs, Lisp_Object *args) | ||
| 578 | { | ||
| 579 | *jo = (json_out_t) { | ||
| 580 | /* The maximum nesting depth allowed should be sufficient for most | ||
| 581 | uses but could be raised if necessary. (The default maximum | ||
| 582 | depth for JSON_checker is 20.) */ | ||
| 583 | .maxdepth = 50, | ||
| 584 | .conf = {json_object_hashtable, json_array_array, QCnull, QCfalse} | ||
| 585 | }; | ||
| 586 | json_parse_args (nargs, args, &jo->conf, false); | ||
| 587 | record_unwind_protect_ptr (cleanup_json_out, jo); | ||
| 588 | |||
| 589 | /* Make float conversion independent of float-output-format. */ | ||
| 590 | if (!NILP (Vfloat_output_format)) | ||
| 591 | specbind (Qfloat_output_format, Qnil); | ||
| 592 | |||
| 593 | json_out_something (jo, object); | ||
| 594 | } | ||
| 595 | |||
| 596 | DEFUN ("json-serialize", Fjson_serialize, Sjson_serialize, 1, MANY, | ||
| 597 | NULL, | ||
| 598 | doc: /* Return the JSON representation of OBJECT as a string. | ||
| 599 | |||
| 600 | OBJECT must be t, a number, string, vector, hashtable, alist, plist, | ||
| 601 | or the Lisp equivalents to the JSON null and false values, and its | ||
| 602 | elements must recursively consist of the same kinds of values. t will | ||
| 603 | be converted to the JSON true value. Vectors will be converted to | ||
| 604 | JSON arrays, whereas hashtables, alists and plists are converted to | ||
| 605 | JSON objects. Hashtable keys must be strings, unique within each object. | ||
| 606 | Alist and plist keys must be symbols; if a key is duplicate, the first | ||
| 607 | instance is used. A leading colon in plist keys is elided. | ||
| 608 | |||
| 609 | The Lisp equivalents to the JSON null and false values are | ||
| 610 | configurable in the arguments ARGS, a list of keyword/argument pairs: | ||
| 611 | |||
| 612 | The keyword argument `:null-object' specifies which object to use | ||
| 613 | to represent a JSON null value. It defaults to `:null'. | ||
| 614 | |||
| 615 | The keyword argument `:false-object' specifies which object to use to | ||
| 616 | represent a JSON false value. It defaults to `:false'. | ||
| 617 | |||
| 618 | In you specify the same value for `:null-object' and `:false-object', | ||
| 619 | a potentially ambiguous situation, the JSON output will not contain | ||
| 620 | any JSON false values. | ||
| 621 | usage: (json-serialize OBJECT &rest ARGS) */) | ||
| 622 | (ptrdiff_t nargs, Lisp_Object *args) | ||
| 623 | { | ||
| 624 | specpdl_ref count = SPECPDL_INDEX (); | ||
| 625 | json_out_t jo; | ||
| 626 | json_serialize (&jo, args[0], nargs - 1, args + 1); | ||
| 627 | return unbind_to (count, json_out_to_string (&jo)); | ||
| 605 | } | 628 | } |
| 606 | 629 | ||
| 607 | DEFUN ("json-insert", Fjson_insert, Sjson_insert, 1, MANY, | 630 | DEFUN ("json-insert", Fjson_insert, Sjson_insert, 1, MANY, |
| @@ -614,71 +637,52 @@ usage: (json-insert OBJECT &rest ARGS) */) | |||
| 614 | (ptrdiff_t nargs, Lisp_Object *args) | 637 | (ptrdiff_t nargs, Lisp_Object *args) |
| 615 | { | 638 | { |
| 616 | specpdl_ref count = SPECPDL_INDEX (); | 639 | specpdl_ref count = SPECPDL_INDEX (); |
| 640 | json_out_t jo; | ||
| 641 | json_serialize (&jo, args[0], nargs - 1, args + 1); | ||
| 617 | 642 | ||
| 618 | #ifdef WINDOWSNT | 643 | /* FIXME: All the work below just to insert a string into a buffer? */ |
| 619 | ensure_json_available (); | ||
| 620 | #endif | ||
| 621 | |||
| 622 | struct json_configuration conf = | ||
| 623 | {json_object_hashtable, json_array_array, QCnull, QCfalse}; | ||
| 624 | json_parse_args (nargs - 1, args + 1, &conf, false); | ||
| 625 | |||
| 626 | json_t *json = lisp_to_json (args[0], &conf); | ||
| 627 | record_unwind_protect_ptr (json_release_object, json); | ||
| 628 | 644 | ||
| 629 | prepare_to_modify_buffer (PT, PT, NULL); | 645 | prepare_to_modify_buffer (PT, PT, NULL); |
| 630 | move_gap_both (PT, PT_BYTE); | 646 | move_gap_both (PT, PT_BYTE); |
| 631 | struct json_insert_data data; | 647 | if (GAP_SIZE < jo.size) |
| 632 | data.inserted_bytes = 0; | 648 | make_gap (jo.size - GAP_SIZE); |
| 633 | /* Could have used json_dumpb, but that became available only in | 649 | memcpy ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE, jo.buf, jo.size); |
| 634 | Jansson 2.10, whereas we want to support 2.7 and upward. */ | 650 | |
| 635 | int status = json_dump_callback (json, json_insert_callback, &data, | 651 | /* No need to keep allocation beyond this point. */ |
| 636 | JSON_COMPACT | JSON_ENCODE_ANY); | 652 | unbind_to (count, Qnil); |
| 637 | if (status == -1) | ||
| 638 | { | ||
| 639 | if (CONSP (data.error)) | ||
| 640 | xsignal (XCAR (data.error), XCDR (data.error)); | ||
| 641 | else | ||
| 642 | json_out_of_memory (); | ||
| 643 | } | ||
| 644 | 653 | ||
| 645 | ptrdiff_t inserted = 0; | 654 | ptrdiff_t inserted = 0; |
| 646 | ptrdiff_t inserted_bytes = data.inserted_bytes; | 655 | ptrdiff_t inserted_bytes = jo.size; |
| 647 | if (inserted_bytes > 0) | 656 | |
| 657 | /* If required, decode the stuff we've read into the gap. */ | ||
| 658 | struct coding_system coding; | ||
| 659 | /* JSON strings are UTF-8 encoded strings. */ | ||
| 660 | setup_coding_system (Qutf_8_unix, &coding); | ||
| 661 | coding.dst_multibyte = !NILP (BVAR (current_buffer, | ||
| 662 | enable_multibyte_characters)); | ||
| 663 | if (CODING_MAY_REQUIRE_DECODING (&coding)) | ||
| 648 | { | 664 | { |
| 649 | /* If required, decode the stuff we've read into the gap. */ | 665 | /* Now we have all the new bytes at the beginning of the gap, |
| 650 | struct coding_system coding; | 666 | but `decode_coding_gap` needs them at the end of the gap, so |
| 651 | /* JSON strings are UTF-8 encoded strings. If for some reason | 667 | we need to move them. */ |
| 652 | the text returned by the Jansson library includes invalid | 668 | memmove (GAP_END_ADDR - inserted_bytes, GPT_ADDR, inserted_bytes); |
| 653 | byte sequences, they will be represented by raw bytes in the | 669 | decode_coding_gap (&coding, inserted_bytes); |
| 654 | buffer text. */ | 670 | inserted = coding.produced_char; |
| 655 | setup_coding_system (Qutf_8_unix, &coding); | 671 | } |
| 656 | coding.dst_multibyte = | 672 | else |
| 657 | !NILP (BVAR (current_buffer, enable_multibyte_characters)); | 673 | { |
| 658 | if (CODING_MAY_REQUIRE_DECODING (&coding)) | 674 | /* Make the inserted text part of the buffer, as unibyte text. */ |
| 659 | { | 675 | eassert (NILP (BVAR (current_buffer, enable_multibyte_characters))); |
| 660 | /* Now we have all the new bytes at the beginning of the gap, | 676 | insert_from_gap_1 (inserted_bytes, inserted_bytes, false); |
| 661 | but `decode_coding_gap` needs them at the end of the gap, so | 677 | |
| 662 | we need to move them. */ | 678 | /* The target buffer is unibyte, so we don't need to decode. */ |
| 663 | memmove (GAP_END_ADDR - inserted_bytes, GPT_ADDR, inserted_bytes); | 679 | invalidate_buffer_caches (current_buffer, |
| 664 | decode_coding_gap (&coding, inserted_bytes); | 680 | PT, PT + inserted_bytes); |
| 665 | inserted = coding.produced_char; | 681 | adjust_after_insert (PT, PT_BYTE, |
| 666 | } | 682 | PT + inserted_bytes, |
| 667 | else | 683 | PT_BYTE + inserted_bytes, |
| 668 | { | 684 | inserted_bytes); |
| 669 | /* Make the inserted text part of the buffer, as unibyte text. */ | 685 | inserted = inserted_bytes; |
| 670 | eassert (NILP (BVAR (current_buffer, enable_multibyte_characters))); | ||
| 671 | insert_from_gap_1 (inserted_bytes, inserted_bytes, false); | ||
| 672 | |||
| 673 | /* The target buffer is unibyte, so we don't need to decode. */ | ||
| 674 | invalidate_buffer_caches (current_buffer, | ||
| 675 | PT, PT + inserted_bytes); | ||
| 676 | adjust_after_insert (PT, PT_BYTE, | ||
| 677 | PT + inserted_bytes, | ||
| 678 | PT_BYTE + inserted_bytes, | ||
| 679 | inserted_bytes); | ||
| 680 | inserted = inserted_bytes; | ||
| 681 | } | ||
| 682 | } | 686 | } |
| 683 | 687 | ||
| 684 | /* Call after-change hooks. */ | 688 | /* Call after-change hooks. */ |
| @@ -690,7 +694,26 @@ usage: (json-insert OBJECT &rest ARGS) */) | |||
| 690 | SET_PT_BOTH (PT + inserted, PT_BYTE + inserted_bytes); | 694 | SET_PT_BOTH (PT + inserted, PT_BYTE + inserted_bytes); |
| 691 | } | 695 | } |
| 692 | 696 | ||
| 693 | return unbind_to (count, Qnil); | 697 | return Qnil; |
| 698 | } | ||
| 699 | |||
| 700 | |||
| 701 | /* Note that all callers of make_string_from_utf8 and build_string_from_utf8 | ||
| 702 | below either pass only value UTF-8 strings or use the function for | ||
| 703 | formatting error messages; in the latter case correctness isn't | ||
| 704 | critical. */ | ||
| 705 | |||
| 706 | /* Return a unibyte string containing the sequence of UTF-8 encoding | ||
| 707 | units of the UTF-8 representation of STRING. If STRING does not | ||
| 708 | represent a sequence of Unicode scalar values, return a string with | ||
| 709 | unspecified contents. */ | ||
| 710 | |||
| 711 | static Lisp_Object | ||
| 712 | json_encode (Lisp_Object string) | ||
| 713 | { | ||
| 714 | /* FIXME: Raise an error if STRING is not a scalar value | ||
| 715 | sequence. */ | ||
| 716 | return encode_string_utf_8 (string, Qnil, false, Qt, Qt); | ||
| 694 | } | 717 | } |
| 695 | 718 | ||
| 696 | #define JSON_PARSER_INTERNAL_OBJECT_WORKSPACE_SIZE 64 | 719 | #define JSON_PARSER_INTERNAL_OBJECT_WORKSPACE_SIZE 64 |
| @@ -1894,7 +1917,6 @@ syms_of_json (void) | |||
| 1894 | DEFSYM (QCnull, ":null"); | 1917 | DEFSYM (QCnull, ":null"); |
| 1895 | DEFSYM (QCfalse, ":false"); | 1918 | DEFSYM (QCfalse, ":false"); |
| 1896 | 1919 | ||
| 1897 | DEFSYM (Qstring_without_embedded_nulls_p, "string-without-embedded-nulls-p"); | ||
| 1898 | DEFSYM (Qjson_value_p, "json-value-p"); | 1920 | DEFSYM (Qjson_value_p, "json-value-p"); |
| 1899 | 1921 | ||
| 1900 | DEFSYM (Qjson_error, "json-error"); | 1922 | DEFSYM (Qjson_error, "json-error"); |
| @@ -1907,7 +1929,6 @@ syms_of_json (void) | |||
| 1907 | DEFSYM (Qjson_invalid_surrogate_error, "json-invalid-surrogate-error") | 1929 | DEFSYM (Qjson_invalid_surrogate_error, "json-invalid-surrogate-error") |
| 1908 | DEFSYM (Qjson_number_out_of_range, "json-number-out-of-range-error") | 1930 | DEFSYM (Qjson_number_out_of_range, "json-number-out-of-range-error") |
| 1909 | DEFSYM (Qjson_escape_sequence_error, "json-escape-sequence-error") | 1931 | DEFSYM (Qjson_escape_sequence_error, "json-escape-sequence-error") |
| 1910 | DEFSYM (Qjson_unavailable, "json-unavailable"); | ||
| 1911 | define_error (Qjson_error, "generic JSON error", Qerror); | 1932 | define_error (Qjson_error, "generic JSON error", Qerror); |
| 1912 | define_error (Qjson_out_of_memory, | 1933 | define_error (Qjson_out_of_memory, |
| 1913 | "not enough memory for creating JSON object", Qjson_error); | 1934 | "not enough memory for creating JSON object", Qjson_error); |
diff --git a/src/lisp.h b/src/lisp.h index f066c876619..7c4bd435cd8 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -4327,7 +4327,6 @@ extern void syms_of_image (void); | |||
| 4327 | 4327 | ||
| 4328 | #ifdef HAVE_JSON | 4328 | #ifdef HAVE_JSON |
| 4329 | /* Defined in json.c. */ | 4329 | /* Defined in json.c. */ |
| 4330 | extern void init_json (void); | ||
| 4331 | extern void syms_of_json (void); | 4330 | extern void syms_of_json (void); |
| 4332 | #endif | 4331 | #endif |
| 4333 | 4332 | ||
diff --git a/src/print.c b/src/print.c index 76c577ec800..0d867b89395 100644 --- a/src/print.c +++ b/src/print.c | |||
| @@ -2859,6 +2859,7 @@ decimal point. 0 is not allowed with `e' or `g'. | |||
| 2859 | A value of nil means to use the shortest notation | 2859 | A value of nil means to use the shortest notation |
| 2860 | that represents the number without losing information. */); | 2860 | that represents the number without losing information. */); |
| 2861 | Vfloat_output_format = Qnil; | 2861 | Vfloat_output_format = Qnil; |
| 2862 | DEFSYM (Qfloat_output_format, "float-output-format"); | ||
| 2862 | 2863 | ||
| 2863 | DEFVAR_BOOL ("print-integers-as-characters", print_integers_as_characters, | 2864 | DEFVAR_BOOL ("print-integers-as-characters", print_integers_as_characters, |
| 2864 | doc: /* Non-nil means integers are printed using characters syntax. | 2865 | doc: /* Non-nil means integers are printed using characters syntax. |