aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMattias EngdegÄrd2024-03-26 16:44:09 +0100
committerMattias EngdegÄrd2024-03-30 14:45:53 +0100
commit890edfd2bb8fd79730919972cc82811b73c7f572 (patch)
tree3860e8c46a0f44f88eb93c07a2e0faab99556351 /src
parentab016657e7b1bd32c775da271ffb7127f86d5a23 (diff)
downloademacs-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.c4
-rw-r--r--src/json.c1071
-rw-r--r--src/lisp.h1
-rw-r--r--src/print.c1
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
39DEF_DLL_FN (void, json_set_alloc_funcs,
40 (json_malloc_t malloc_fn, json_free_t free_fn));
41DEF_DLL_FN (void, json_delete, (json_t *json));
42DEF_DLL_FN (json_t *, json_array, (void));
43DEF_DLL_FN (int, json_array_append_new, (json_t *array, json_t *value));
44DEF_DLL_FN (size_t, json_array_size, (const json_t *array));
45DEF_DLL_FN (json_t *, json_object, (void));
46DEF_DLL_FN (int, json_object_set_new,
47 (json_t *object, const char *key, json_t *value));
48DEF_DLL_FN (json_t *, json_null, (void));
49DEF_DLL_FN (json_t *, json_true, (void));
50DEF_DLL_FN (json_t *, json_false, (void));
51DEF_DLL_FN (json_t *, json_integer, (json_int_t value));
52DEF_DLL_FN (json_t *, json_real, (double value));
53DEF_DLL_FN (json_t *, json_stringn, (const char *value, size_t len));
54DEF_DLL_FN (char *, json_dumps, (const json_t *json, size_t flags));
55DEF_DLL_FN (int, json_dump_callback,
56 (const json_t *json, json_dump_callback_t callback, void *data,
57 size_t flags));
58DEF_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. */
61void json_delete(json_t *json)
62{
63 fn_json_delete (json);
64}
65
66static bool json_initialized;
67
68static bool
69init_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
127static void *
128json_malloc (size_t size)
129{
130 if (size > PTRDIFF_MAX)
131 {
132 errno = ENOMEM;
133 return NULL;
134 }
135 return malloc (size);
136}
137
138static void
139json_free (void *ptr)
140{
141 free (ptr);
142}
143
144void
145init_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
160static Lisp_Object
161json_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
168static AVOID
169json_out_of_memory (void)
170{
171 xsignal0 (Qjson_out_of_memory);
172}
173
174static void
175json_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
183static void
184check_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
194static json_t *
195json_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
205static void
206json_check_utf8 (Lisp_Object string)
207{
208 CHECK_TYPE (utf8_string_p (string), Qutf_8_string_p, string);
209}
210
211enum json_object_type { 32enum 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
229static 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
234static json_t *
235lisp_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
351static json_t *
352lisp_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
366static json_t *
367lisp_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
402static void 50static void
403json_parse_args (ptrdiff_t nargs, 51json_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
453static bool 101/* FIXME: Remove completely. */
454json_available_p (void) 102DEFUN ("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. */
110typedef 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. */
122typedef 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
128struct 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
137static inline ptrdiff_t
138symset_size (int bits)
139{
140 return (ptrdiff_t)1 << bits;
141}
142
143static struct symset_tbl *
144make_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. */
158static symset_t
159push_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. */
471static void 168static void
472ensure_json_available (void) 169pop_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
480DEFUN ("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). */) 176static void
482 (void) 177cleanup_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
487DEFUN ("json-serialize", Fjson_serialize, Sjson_serialize, 1, MANY, 187static inline uint32_t
488 NULL, 188symset_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
491OBJECT must be t, a number, string, vector, hashtable, alist, plist, 193/* Enlarge the table used by a symset. */
492or the Lisp equivalents to the JSON null and false values, and its 194static NO_INLINE void
493elements must recursively consist of the same kinds of values. t will 195symset_expand (symset_t *ss)
494be converted to the JSON true value. Vectors will be converted to 196{
495JSON arrays, whereas hashtables, alists and plists are converted to 197 struct symset_tbl *old_table = ss->table;
496JSON objects. Hashtable keys must be strings without embedded null 198 int oldbits = ss->bits;
497characters and must be unique within each object. Alist and plist 199 ptrdiff_t oldsize = symset_size (oldbits);
498keys must be symbols; if a key is duplicate, the first instance is 200 int bits = oldbits + 1;
499used. 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
501The Lisp equivalents to the JSON null and false values are 220/* If sym is in ss, return false; otherwise add it and return true.
502configurable in the arguments ARGS, a list of keyword/argument pairs: 221 Comparison is done by strict identity. */
222static inline bool
223symset_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
504The keyword argument `:null-object' specifies which object to use 232 struct symset_tbl *tbl = ss->table;
505to 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
507The keyword argument `:false-object' specifies which object to use to 249static NO_INLINE void
508represent a JSON false value. It defaults to `:false'. 250json_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
510In you specify the same value for `:null-object' and `:false-object', 260static void
511a potentially ambiguous situation, the JSON output will not contain 261cleanup_json_out (void *arg)
512any JSON false values.
513usage: (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 (); 270static void
520#endif 271json_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); 280static void
281json_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); 288static void
530 if (string == NULL) 289json_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)); 295static void
296json_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
537struct json_buffer_and_size 304static AVOID
305string_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
312static 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
546static Lisp_Object 324static void
547json_insert (void *data) 325json_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
406static void
407json_out_nest (json_out_t *jo)
408{
409 --jo->maxdepth;
410 if (jo->maxdepth < 0)
411 error ("Maximum JSON serialisation depth exceeded");
412}
413
414static void
415json_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. */ 420static 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. */ 422static void
559 memcpy ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE + inserted_bytes, 423json_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
565static Lisp_Object 473static void
566json_handle_nonlocal_exit (enum nonlocal_exit type, Lisp_Object data) 474json_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
579struct json_insert_data 497static void
498json_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 513static void
590 as a unibyte string into the gap. DATA must point to a structure 514json_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
595static int 526static void
596json_insert_callback (const char *buffer, size_t size, void *data) 527json_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
536static void
537json_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
565static Lisp_Object
566json_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
575static void
576json_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
596DEFUN ("json-serialize", Fjson_serialize, Sjson_serialize, 1, MANY,
597 NULL,
598 doc: /* Return the JSON representation of OBJECT as a string.
599
600OBJECT must be t, a number, string, vector, hashtable, alist, plist,
601or the Lisp equivalents to the JSON null and false values, and its
602elements must recursively consist of the same kinds of values. t will
603be converted to the JSON true value. Vectors will be converted to
604JSON arrays, whereas hashtables, alists and plists are converted to
605JSON objects. Hashtable keys must be strings, unique within each object.
606Alist and plist keys must be symbols; if a key is duplicate, the first
607instance is used. A leading colon in plist keys is elided.
608
609The Lisp equivalents to the JSON null and false values are
610configurable in the arguments ARGS, a list of keyword/argument pairs:
611
612The keyword argument `:null-object' specifies which object to use
613to represent a JSON null value. It defaults to `:null'.
614
615The keyword argument `:false-object' specifies which object to use to
616represent a JSON false value. It defaults to `:false'.
617
618In you specify the same value for `:null-object' and `:false-object',
619a potentially ambiguous situation, the JSON output will not contain
620any JSON false values.
621usage: (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
607DEFUN ("json-insert", Fjson_insert, Sjson_insert, 1, MANY, 630DEFUN ("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
711static Lisp_Object
712json_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. */
4330extern void init_json (void);
4331extern void syms_of_json (void); 4330extern 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'.
2859A value of nil means to use the shortest notation 2859A value of nil means to use the shortest notation
2860that represents the number without losing information. */); 2860that 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.