From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Mattias =?UTF-8?Q?Engdeg=C3=A5rd?= Newsgroups: gmane.emacs.bugs Subject: bug#70007: [PATCH] native JSON encoder Date: Sat, 30 Mar 2024 12:41:31 +0100 Message-ID: <3139C8FE-5C67-4FE3-B940-F449DA73E76C@gmail.com> References: <1BF559D1-DB9F-4FEB-90ED-72E0EFD76424@gmail.com> <86wmpphrg7.fsf@gnu.org> <4589243D-C11A-45C1-AF3E-6F4A5BADEB54@gmail.com> <864jcrindg.fsf@gnu.org> <291DD5F1-85B8-4647-A40A-EBBD4C51E253@gmail.com> <8634sbijfx.fsf@gnu.org> <2CF47DA5-A65B-47C4-A28A-6FEE1469BD13@gmail.com> <86cyrdfuai.fsf@gnu.org> Mime-Version: 1.0 (Mac OS X Mail 14.0 \(3654.120.0.1.15\)) Content-Type: multipart/mixed; boundary="Apple-Mail=_558A8A9C-18B2-4CE8-A0C1-865EBAE3B503" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="23620"; mail-complaints-to="usenet@ciao.gmane.io" Cc: casouri@gmail.com, 70007@debbugs.gnu.org To: Eli Zaretskii Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Sat Mar 30 12:42:17 2024 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1rqX6G-0005wl-Vk for geb-bug-gnu-emacs@m.gmane-mx.org; Sat, 30 Mar 2024 12:42:17 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rqX63-0003Ss-To; Sat, 30 Mar 2024 07:42:05 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rqX60-0003Se-Q8 for bug-gnu-emacs@gnu.org; Sat, 30 Mar 2024 07:42:00 -0400 Original-Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1rqX60-0005yM-Hl for bug-gnu-emacs@gnu.org; Sat, 30 Mar 2024 07:42:00 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1rqX62-0002al-3q for bug-gnu-emacs@gnu.org; Sat, 30 Mar 2024 07:42:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Mattias =?UTF-8?Q?Engdeg=C3=A5rd?= Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sat, 30 Mar 2024 11:42:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 70007 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch Original-Received: via spool by 70007-submit@debbugs.gnu.org id=B70007.17117989059907 (code B ref 70007); Sat, 30 Mar 2024 11:42:02 +0000 Original-Received: (at 70007) by debbugs.gnu.org; 30 Mar 2024 11:41:45 +0000 Original-Received: from localhost ([127.0.0.1]:44093 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rqX5l-0002Zj-27 for submit@debbugs.gnu.org; Sat, 30 Mar 2024 07:41:45 -0400 Original-Received: from mail-lf1-x12c.google.com ([2a00:1450:4864:20::12c]:45304) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rqX5h-0002ZO-N2 for 70007@debbugs.gnu.org; Sat, 30 Mar 2024 07:41:43 -0400 Original-Received: by mail-lf1-x12c.google.com with SMTP id 2adb3069b0e04-515d55ab035so436967e87.2 for <70007@debbugs.gnu.org>; Sat, 30 Mar 2024 04:41:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1711798894; x=1712403694; darn=debbugs.gnu.org; h=references:to:cc:in-reply-to:date:subject:mime-version:message-id :from:sender:from:to:cc:subject:date:message-id:reply-to; bh=lhlkbrTrtEy4QIRhwP4fVRhgXE+g2kJyYlI/Eum8JFo=; b=TlmIoGdaCDpPMC+i390ku4TXGnG48yVpzH5fIEBlIcHe3LtpgMQkuCRnTSZav/hHzg wHqWkWwT/aUYGDaNb2lrfOSPpwCWhp4lo3WjBdWgHsNEhO/lmYQyWqJVyLMiizLpSdxe TIPpS/kF96XezGJXsdVd4ANb4x7fKoKF0InpVua8gw9k+VuEs9F1ebjDOoUoLSmiFCww 6sW1uMlCS6T6dMiRO7XNXWWjSSnT6knjbp1ZwaR88LvHP41RCpYEBWB5YEDrK7h4c/rD FPd+tKTOFPMyoMbyLrWN5umbQdfhO0G/6EzYTJS5VTFpC0XSORpaezPxk/2549NXn/Wm AtdA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711798894; x=1712403694; h=references:to:cc:in-reply-to:date:subject:mime-version:message-id :from:sender:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=lhlkbrTrtEy4QIRhwP4fVRhgXE+g2kJyYlI/Eum8JFo=; b=PvOp8gwXopoc/bAHf06J6wnzYHinmsuufEDC7nkYyw979HhEYZHtDn2b6K2wc7A3Nf sedRfGmSXyz2WXr8Znl1bVn2afQBM1IYwaFmIh15vVpiUNDAWZzyt6oniWnohxvDCeyg MYj3Opcc15IJfhyVO0cCfYE/syohQ1GWRSzilwNlADtuAFMfeLpmUzRjQoTyuJ76chS0 3PfJaS6pcn0YcfiBe5HS+aVZGnDNVJSuiQhhWP4UpFvmZBhdQWhXWaFM6CJU6mljD6CL AtzyPy5toD3dOFX3mP0FsSALesqdYf7DDVklgmT3S35Dp+NUPrWO092e056BBI8VEFcx ZBXw== X-Forwarded-Encrypted: i=1; AJvYcCVPGBbVMfpx7Rd31f3wxB4TrpxGk4n4lxJkwLtjpEqd1VtqbNaz07SQHvvPrrgJEpeqAHfIO1ttEPyjHSziP1UXqKsGE5M= X-Gm-Message-State: AOJu0Yyg8LvqFKzO4gXzj7XlWKMwVYaYahE5q9nCb1Y3Nwo6epAwuuS+ WKMrSipQ5nlbZhYQniGNVPRuuhhr1oMMtofY14LMMcMTOpdOM+Pd X-Google-Smtp-Source: AGHT+IGbJuAQ29ZS+E+efEMOzv7jspHD8pS23dmSetiCWlrWHREYWSecAuvXGh/c5CsIySYTzY5rfg== X-Received: by 2002:ac2:5fb1:0:b0:516:a010:f1c0 with SMTP id s17-20020ac25fb1000000b00516a010f1c0mr467048lfe.63.1711798893264; Sat, 30 Mar 2024 04:41:33 -0700 (PDT) Original-Received: from smtpclient.apple (c80-217-1-132.bredband.tele2.se. [80.217.1.132]) by smtp.gmail.com with ESMTPSA id t9-20020a195f09000000b00515bad4cd0asm898443lfb.155.2024.03.30.04.41.32 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sat, 30 Mar 2024 04:41:32 -0700 (PDT) In-Reply-To: <86cyrdfuai.fsf@gnu.org> X-Mailer: Apple Mail (2.3654.120.0.1.15) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.bugs:282351 Archived-At: --Apple-Mail=_558A8A9C-18B2-4CE8-A0C1-865EBAE3B503 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii 29 mars 2024 kl. 07.04 skrev Eli Zaretskii : > OK. FTR, I'm not in favor of validation of unibyte strings, I just > suggest that we treat them as plain-ASCII: pass them through without > any validation, leaving the validation to the callers. Actually we are more or less forced to validate unibyte strings as long = as the serialiser returns multibyte. Which we agree that it probably = shouldn't, but I'd first like to take some time to ensure that returning = unibyte won't break anything. Thank you for pushing the new JSON parser to master. I've rebased my = patch and cleaned it up a bit, and it now removes all uses of Jansson = from json.c. Since the change involves removing some Windows-specific = code, perhaps you would like to check that it still compiles on that = platform, if you have the time? Otherwise I'll push it to master, and will remain ready to make any = further adjustments necessary. We can then remove all remaining Jansson = references (configuration, installation notes, etc), and make the = required NEWS and manual changes. --Apple-Mail=_558A8A9C-18B2-4CE8-A0C1-865EBAE3B503 Content-Disposition: attachment; filename=0001-New-JSON-encoder-bug-70007.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="0001-New-JSON-encoder-bug-70007.patch" Content-Transfer-Encoding: quoted-printable =46rom=204b2f5d8b55946c55838793046944b5680a19ba01=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20=3D?UTF-8?q?Mattias=3D20Engdeg=3DC3=3DA5rd?=3D=20= =0ADate:=20Tue,=2026=20Mar=202024=2016:44:09=20+0100=0A= Subject:=20[PATCH]=20New=20JSON=20encoder=20(bug#70007)=0A=0AIt=20is=20= in=20general=20at=20least=202x=20faster=20than=20the=20old=20encoder=20= and=20does=20not=0Adepend=20on=20any=20external=20library.=20=20Using=20= our=20own=20code=20also=20gives=20us=0Acontrol=20over=20translation=20= details:=20for=20example,=20we=20now=20have=20full=0Abignum=20support=20= and=20tighter=20float=20formatting.=0A=0A*=20src/json.c=20(json_delete,=20= json_initialized,=20init_json_functions)=0A(json_malloc,=20json_free,=20= init_json,=20json_out_of_memory)=0A(json_releae_object,=20= check_string_without_embedded_nulls,=20json_check)=0A(json_check_utf8,=20= lisp_to_json_nonscalar_1,=20lisp_to_json_nonscalar)=0A(lisp_to_json,=20= json_available_p,=20ensure_json_available,=20json_insert)=0A= (json_handle_nonlocal_exit,=20json_insert_callback):=0ARemove.=20=20= Remaining=20uses=20updated.=0A*=20src/json.c=20(json_out_t,=20symset_t,=20= struct=20symset_tbl)=0A(symset_size,=20make_symset_table,=20push_symset,=20= pop_symset)=0A(cleanup_symset_tables,=20symset_hash,=20symset_expand,=20= symset_add)=0A(json_out_grow_buf,=20cleanup_json_out,=20json_make_room,=20= JSON_OUT_STR)=0A(json_out_str,=20json_out_byte,=20json_out_fixnum,=20= string_not_unicode)=0A(json_plain_char,=20json_out_string,=20= json_out_nest,=20json_out_unnest)=0A(json_out_object_cons,=20= json_out_object_hash),=20json_out_array)=0A(json_out_float,=20= json_out_bignum,=20json_out_something)=0A(json_out_to_string,=20= json_serialize):=20New.=0A(Fjson_serialize,=20Fjson_insert):=0ANew=20= JSON=20encoder=20implementation.=0A*=20test/src/json-tests.el=20= (json-serialize/object-with-duplicate-keys)=0A(json-serialize/string):=20= Update=20tests.=0A---=0A=20src/emacs.c=20=20=20=20=20=20=20=20=20=20=20=20= |=20=20=20=204=20-=0A=20src/json.c=20=20=20=20=20=20=20=20=20=20=20=20=20= |=201071=20++++++++++++++++++++--------------------=0A=20src/lisp.h=20=20= =20=20=20=20=20=20=20=20=20=20=20|=20=20=20=201=20-=0A=20src/print.c=20=20= =20=20=20=20=20=20=20=20=20=20|=20=20=20=201=20+=0A=20= test/src/json-tests.el=20|=20=20=2041=20+-=0A=205=20files=20changed,=20= 581=20insertions(+),=20537=20deletions(-)=0A=0Adiff=20--git=20= a/src/emacs.c=20b/src/emacs.c=0Aindex=2087f12d3fa86..4a34bb06425=20= 100644=0A---=20a/src/emacs.c=0A+++=20b/src/emacs.c=0A@@=20-2013,10=20= +2013,6=20@@=20main=20(int=20argc,=20char=20**argv)=0A=20=20=20= init_random=20();=0A=20=20=20init_xfaces=20();=0A=20=0A-#if=20defined=20= HAVE_JSON=20&&=20!defined=20WINDOWSNT=0A-=20=20init_json=20();=0A-#endif=0A= -=0A=20=20=20if=20(!initialized)=0A=20=20=20=20=20syms_of_comp=20();=0A=20= =0Adiff=20--git=20a/src/json.c=20b/src/json.c=0Aindex=20= afc48c59d5a..711744138b8=20100644=0A---=20a/src/json.c=0A+++=20= b/src/json.c=0A@@=20-25,189=20+25,10=20@@=20Copyright=20(C)=202017-2024=20= Free=20Software=20Foundation,=20Inc.=0A=20#include=20=0A=20= #include=20=0A=20=0A-#include=20=0A-=0A=20#include=20= "lisp.h"=0A=20#include=20"buffer.h"=0A=20#include=20"coding.h"=0A=20=0A= -#ifdef=20WINDOWSNT=0A-#=20include=20=0A-#=20include=20= "w32common.h"=0A-#=20include=20"w32.h"=0A-=0A-DEF_DLL_FN=20(void,=20= json_set_alloc_funcs,=0A-=09=20=20=20=20(json_malloc_t=20malloc_fn,=20= json_free_t=20free_fn));=0A-DEF_DLL_FN=20(void,=20json_delete,=20(json_t=20= *json));=0A-DEF_DLL_FN=20(json_t=20*,=20json_array,=20(void));=0A= -DEF_DLL_FN=20(int,=20json_array_append_new,=20(json_t=20*array,=20= json_t=20*value));=0A-DEF_DLL_FN=20(size_t,=20json_array_size,=20(const=20= json_t=20*array));=0A-DEF_DLL_FN=20(json_t=20*,=20json_object,=20= (void));=0A-DEF_DLL_FN=20(int,=20json_object_set_new,=0A-=09=20=20=20=20= (json_t=20*object,=20const=20char=20*key,=20json_t=20*value));=0A= -DEF_DLL_FN=20(json_t=20*,=20json_null,=20(void));=0A-DEF_DLL_FN=20= (json_t=20*,=20json_true,=20(void));=0A-DEF_DLL_FN=20(json_t=20*,=20= json_false,=20(void));=0A-DEF_DLL_FN=20(json_t=20*,=20json_integer,=20= (json_int_t=20value));=0A-DEF_DLL_FN=20(json_t=20*,=20json_real,=20= (double=20value));=0A-DEF_DLL_FN=20(json_t=20*,=20json_stringn,=20(const=20= char=20*value,=20size_t=20len));=0A-DEF_DLL_FN=20(char=20*,=20= json_dumps,=20(const=20json_t=20*json,=20size_t=20flags));=0A-DEF_DLL_FN=20= (int,=20json_dump_callback,=0A-=09=20=20=20=20(const=20json_t=20*json,=20= json_dump_callback_t=20callback,=20void=20*data,=0A-=09=20=20=20=20=20= size_t=20flags));=0A-DEF_DLL_FN=20(json_t=20*,=20json_object_get,=20= (const=20json_t=20*object,=20const=20char=20*key));=0A-=0A-/*=20This=20= is=20called=20by=20json_decref,=20which=20is=20an=20inline=20function.=20= =20*/=0A-void=20json_delete(json_t=20*json)=0A-{=0A-=20=20fn_json_delete=20= (json);=0A-}=0A-=0A-static=20bool=20json_initialized;=0A-=0A-static=20= bool=0A-init_json_functions=20(void)=0A-{=0A-=20=20HMODULE=20library=20=3D= =20w32_delayed_load=20(Qjson);=0A-=0A-=20=20if=20(!library)=0A-=20=20=20=20= return=20false;=0A-=0A-=20=20LOAD_DLL_FN=20(library,=20= json_set_alloc_funcs);=0A-=20=20LOAD_DLL_FN=20(library,=20json_delete);=0A= -=20=20LOAD_DLL_FN=20(library,=20json_array);=0A-=20=20LOAD_DLL_FN=20= (library,=20json_array_append_new);=0A-=20=20LOAD_DLL_FN=20(library,=20= json_array_size);=0A-=20=20LOAD_DLL_FN=20(library,=20json_object);=0A-=20= =20LOAD_DLL_FN=20(library,=20json_object_set_new);=0A-=20=20LOAD_DLL_FN=20= (library,=20json_null);=0A-=20=20LOAD_DLL_FN=20(library,=20json_true);=0A= -=20=20LOAD_DLL_FN=20(library,=20json_false);=0A-=20=20LOAD_DLL_FN=20= (library,=20json_integer);=0A-=20=20LOAD_DLL_FN=20(library,=20= json_real);=0A-=20=20LOAD_DLL_FN=20(library,=20json_stringn);=0A-=20=20= LOAD_DLL_FN=20(library,=20json_dumps);=0A-=20=20LOAD_DLL_FN=20(library,=20= json_dump_callback);=0A-=20=20LOAD_DLL_FN=20(library,=20= json_object_get);=0A-=0A-=20=20init_json=20();=0A-=0A-=20=20return=20= true;=0A-}=0A-=0A-#define=20json_set_alloc_funcs=20= fn_json_set_alloc_funcs=0A-#define=20json_array=20fn_json_array=0A= -#define=20json_array_append_new=20fn_json_array_append_new=0A-#define=20= json_array_size=20fn_json_array_size=0A-#define=20json_object=20= fn_json_object=0A-#define=20json_object_set_new=20fn_json_object_set_new=0A= -#define=20json_null=20fn_json_null=0A-#define=20json_true=20= fn_json_true=0A-#define=20json_false=20fn_json_false=0A-#define=20= json_integer=20fn_json_integer=0A-#define=20json_real=20fn_json_real=0A= -#define=20json_stringn=20fn_json_stringn=0A-#define=20json_dumps=20= fn_json_dumps=0A-#define=20json_dump_callback=20fn_json_dump_callback=0A= -#define=20json_object_get=20fn_json_object_get=0A-=0A-#endif=09/*=20= WINDOWSNT=20*/=0A-=0A-/*=20We=20install=20a=20custom=20allocator=20so=20= that=20we=20can=20avoid=20objects=20larger=0A-=20=20=20than=20= PTRDIFF_MAX.=20=20Such=20objects=20wouldn't=20play=20well=20with=20the=20= rest=20of=0A-=20=20=20Emacs's=20codebase,=20which=20generally=20uses=20= ptrdiff_t=20for=20sizes=20and=0A-=20=20=20indices.=20=20The=20other=20= functions=20in=20this=20file=20also=20generally=20assume=0A-=20=20=20= that=20size_t=20values=20never=20exceed=20PTRDIFF_MAX.=0A-=0A-=20=20=20= In=20addition,=20we=20need=20to=20use=20a=20custom=20allocator=20because=20= on=0A-=20=20=20MS-Windows=20we=20replace=20malloc/free=20with=20our=20= own=20functions,=20see=0A-=20=20=20w32heap.c,=20so=20we=20must=20force=20= the=20library=20to=20use=20our=20allocator,=20or=0A-=20=20=20else=20we=20= won't=20be=20able=20to=20free=20storage=20allocated=20by=20the=20= library.=20=20*/=0A-=0A-static=20void=20*=0A-json_malloc=20(size_t=20= size)=0A-{=0A-=20=20if=20(size=20>=20PTRDIFF_MAX)=0A-=20=20=20=20{=0A-=20= =20=20=20=20=20errno=20=3D=20ENOMEM;=0A-=20=20=20=20=20=20return=20NULL;=0A= -=20=20=20=20}=0A-=20=20return=20malloc=20(size);=0A-}=0A-=0A-static=20= void=0A-json_free=20(void=20*ptr)=0A-{=0A-=20=20free=20(ptr);=0A-}=0A-=0A= -void=0A-init_json=20(void)=0A-{=0A-=20=20json_set_alloc_funcs=20= (json_malloc,=20json_free);=0A-}=0A-=0A-/*=20Note=20that=20all=20callers=20= of=20make_string_from_utf8=20and=20build_string_from_utf8=0A-=20=20=20= below=20either=20pass=20only=20value=20UTF-8=20strings=20or=20use=20the=20= functionf=20for=0A-=20=20=20formatting=20error=20messages;=20in=20the=20= latter=20case=20correctness=20isn't=0A-=20=20=20critical.=20=20*/=0A-=0A= -/*=20Return=20a=20unibyte=20string=20containing=20the=20sequence=20of=20= UTF-8=20encoding=0A-=20=20=20units=20of=20the=20UTF-8=20representation=20= of=20STRING.=20=20If=20STRING=20does=20not=0A-=20=20=20represent=20a=20= sequence=20of=20Unicode=20scalar=20values,=20return=20a=20string=20with=0A= -=20=20=20unspecified=20contents.=20=20*/=0A-=0A-static=20Lisp_Object=0A= -json_encode=20(Lisp_Object=20string)=0A-{=0A-=20=20/*=20FIXME:=20Raise=20= an=20error=20if=20STRING=20is=20not=20a=20scalar=20value=0A-=20=20=20=20=20= sequence.=20=20*/=0A-=20=20return=20encode_string_utf_8=20(string,=20= Qnil,=20false,=20Qt,=20Qt);=0A-}=0A-=0A-static=20AVOID=0A= -json_out_of_memory=20(void)=0A-{=0A-=20=20xsignal0=20= (Qjson_out_of_memory);=0A-}=0A-=0A-static=20void=0A-json_release_object=20= (void=20*object)=0A-{=0A-=20=20json_decref=20(object);=0A-}=0A-=0A-/*=20= Signal=20an=20error=20if=20OBJECT=20is=20not=20a=20string,=20or=20if=20= OBJECT=20contains=0A-=20=20=20embedded=20null=20characters.=20=20*/=0A-=0A= -static=20void=0A-check_string_without_embedded_nulls=20(Lisp_Object=20= object)=0A-{=0A-=20=20CHECK_STRING=20(object);=0A-=20=20CHECK_TYPE=20= (memchr=20(SDATA=20(object),=20'\0',=20SBYTES=20(object))=20=3D=3D=20= NULL,=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20= Qstring_without_embedded_nulls_p,=20object);=0A-}=0A-=0A-/*=20Signal=20= an=20error=20of=20type=20`json-out-of-memory'=20if=20OBJECT=20is=0A-=20=20= =20NULL.=20=20*/=0A-=0A-static=20json_t=20*=0A-json_check=20(json_t=20= *object)=0A-{=0A-=20=20if=20(object=20=3D=3D=20NULL)=0A-=20=20=20=20= json_out_of_memory=20();=0A-=20=20return=20object;=0A-}=0A-=0A-/*=20If=20= STRING=20is=20not=20a=20valid=20UTF-8=20string,=20signal=20an=20error=20= of=20type=0A-=20=20=20`wrong-type-argument'.=20=20STRING=20must=20be=20a=20= unibyte=20string.=20=20*/=0A-=0A-static=20void=0A-json_check_utf8=20= (Lisp_Object=20string)=0A-{=0A-=20=20CHECK_TYPE=20(utf8_string_p=20= (string),=20Qutf_8_string_p,=20string);=0A-}=0A-=0A=20enum=20= json_object_type=20{=0A=20=20=20json_object_hashtable,=0A=20=20=20= json_object_alist,=0A@@=20-226,179=20+47,6=20@@=20json_check_utf8=20= (Lisp_Object=20string)=0A=20=20=20Lisp_Object=20false_object;=0A=20};=0A=20= =0A-static=20json_t=20*lisp_to_json=20(Lisp_Object,=0A-=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= const=20struct=20json_configuration=20*conf);=0A-=0A-/*=20Convert=20a=20= Lisp=20object=20to=20a=20nonscalar=20JSON=20object=20(array=20or=20= object).=20=20*/=0A-=0A-static=20json_t=20*=0A-lisp_to_json_nonscalar_1=20= (Lisp_Object=20lisp,=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20const=20struct=20json_configuration=20*conf)=0A= -{=0A-=20=20json_t=20*json;=0A-=20=20specpdl_ref=20count;=0A-=0A-=20=20= if=20(VECTORP=20(lisp))=0A-=20=20=20=20{=0A-=20=20=20=20=20=20ptrdiff_t=20= size=20=3D=20ASIZE=20(lisp);=0A-=20=20=20=20=20=20json=20=3D=20= json_check=20(json_array=20());=0A-=20=20=20=20=20=20count=20=3D=20= SPECPDL_INDEX=20();=0A-=20=20=20=20=20=20record_unwind_protect_ptr=20= (json_release_object,=20json);=0A-=20=20=20=20=20=20for=20(ptrdiff_t=20i=20= =3D=200;=20i=20<=20size;=20++i)=0A-=20=20=20=20=20=20=20=20{=0A-=20=20=20= =20=20=20=20=20=20=20int=20status=0A-=20=20=20=20=20=20=20=20=20=20=20=20= =3D=20json_array_append_new=20(json,=20lisp_to_json=20(AREF=20(lisp,=20= i),=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20conf));=0A-=20=20=20=20=20=20=20=20=20=20= if=20(status=20=3D=3D=20-1)=0A-=20=20=20=20=20=20=20=20=20=20=20=20= json_out_of_memory=20();=0A-=20=20=20=20=20=20=20=20}=0A-=20=20=20=20=20=20= eassert=20(json_array_size=20(json)=20=3D=3D=20size);=0A-=20=20=20=20}=0A= -=20=20else=20if=20(HASH_TABLE_P=20(lisp))=0A-=20=20=20=20{=0A-=20=20=20=20= =20=20struct=20Lisp_Hash_Table=20*h=20=3D=20XHASH_TABLE=20(lisp);=0A-=20=20= =20=20=20=20json=20=3D=20json_check=20(json_object=20());=0A-=20=20=20=20= =20=20count=20=3D=20SPECPDL_INDEX=20();=0A-=20=20=20=20=20=20= record_unwind_protect_ptr=20(json_release_object,=20json);=0A-=20=20=20=20= =20=20DOHASH=20(h,=20key,=20v)=0A-=20=20=20=20=20=20=20=20{=0A-=09=20=20= CHECK_STRING=20(key);=0A-=09=20=20Lisp_Object=20ekey=20=3D=20json_encode=20= (key);=0A-=09=20=20/*=20We=20can't=20specify=20the=20length,=20so=20the=20= string=20must=20be=0A-=09=20=20=20=20=20null-terminated.=20=20*/=0A-=09=20= =20check_string_without_embedded_nulls=20(ekey);=0A-=09=20=20const=20= char=20*key_str=20=3D=20SSDATA=20(ekey);=0A-=09=20=20/*=20Reject=20= duplicate=20keys.=20=20These=20are=20possible=20if=20the=20hash=0A-=09=20= =20=20=20=20table=20test=20is=20not=20`equal'.=20=20*/=0A-=09=20=20if=20= (json_object_get=20(json,=20key_str)=20!=3D=20NULL)=0A-=09=20=20=20=20= wrong_type_argument=20(Qjson_value_p,=20lisp);=0A-=09=20=20int=20status=0A= -=09=20=20=20=20=3D=20json_object_set_new=20(json,=20key_str,=0A-=09=09=09= =09=20=20=20lisp_to_json=20(v,=20conf));=0A-=09=20=20if=20(status=20=3D=3D= =20-1)=0A-=09=20=20=20=20{=0A-=09=20=20=20=20=20=20/*=20A=20failure=20= can=20be=20caused=20either=20by=20an=20invalid=20key=20or=0A-=09=09=20by=20= low=20memory.=20=20*/=0A-=09=20=20=20=20=20=20json_check_utf8=20(ekey);=0A= -=09=20=20=20=20=20=20json_out_of_memory=20();=0A-=09=20=20=20=20}=0A-=09= }=0A-=20=20=20=20}=0A-=20=20else=20if=20(NILP=20(lisp))=0A-=20=20=20=20= return=20json_check=20(json_object=20());=0A-=20=20else=20if=20(CONSP=20= (lisp))=0A-=20=20=20=20{=0A-=20=20=20=20=20=20Lisp_Object=20tail=20=3D=20= lisp;=0A-=20=20=20=20=20=20json=20=3D=20json_check=20(json_object=20());=0A= -=20=20=20=20=20=20count=20=3D=20SPECPDL_INDEX=20();=0A-=20=20=20=20=20=20= record_unwind_protect_ptr=20(json_release_object,=20json);=0A-=20=20=20=20= =20=20bool=20is_plist=20=3D=20!CONSP=20(XCAR=20(tail));=0A-=20=20=20=20=20= =20FOR_EACH_TAIL=20(tail)=0A-=20=20=20=20=20=20=20=20{=0A-=20=20=20=20=20= =20=20=20=20=20const=20char=20*key_str;=0A-=20=20=20=20=20=20=20=20=20=20= Lisp_Object=20value;=0A-=20=20=20=20=20=20=20=20=20=20Lisp_Object=20= key_symbol;=0A-=20=20=20=20=20=20=20=20=20=20if=20(is_plist)=0A-=20=20=20= =20=20=20=20=20=20=20=20=20{=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20= key_symbol=20=3D=20XCAR=20(tail);=0A-=20=20=20=20=20=20=20=20=20=20=20=20= =20=20tail=20=3D=20XCDR=20(tail);=0A-=20=20=20=20=20=20=20=20=20=20=20=20= =20=20CHECK_CONS=20(tail);=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20= value=20=3D=20XCAR=20(tail);=0A-=20=20=20=20=20=20=20=20=20=20=20=20}=0A= -=20=20=20=20=20=20=20=20=20=20else=0A-=20=20=20=20=20=20=20=20=20=20=20=20= {=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20Lisp_Object=20pair=20=3D=20= XCAR=20(tail);=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20CHECK_CONS=20= (pair);=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20key_symbol=20=3D=20= XCAR=20(pair);=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20value=20=3D=20= XCDR=20(pair);=0A-=20=20=20=20=20=20=20=20=20=20=20=20}=0A-=20=20=20=20=20= =20=20=20=20=20CHECK_SYMBOL=20(key_symbol);=0A-=20=20=20=20=20=20=20=20=20= =20Lisp_Object=20key=20=3D=20SYMBOL_NAME=20(key_symbol);=0A-=20=20=20=20=20= =20=20=20=20=20/*=20We=20can't=20specify=20the=20length,=20so=20the=20= string=20must=20be=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20= null-terminated.=20=20*/=0A-=20=20=20=20=20=20=20=20=20=20= check_string_without_embedded_nulls=20(key);=0A-=20=20=20=20=20=20=20=20=20= =20key_str=20=3D=20SSDATA=20(key);=0A-=20=20=20=20=20=20=20=20=20=20/*=20= In=20plists,=20ensure=20leading=20":"=20in=20keys=20is=20stripped.=20=20= It=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20will=20be=20reconstructed=20= later=20in=20`json_to_lisp'.*/=0A-=20=20=20=20=20=20=20=20=20=20if=20= (is_plist=20&&=20':'=20=3D=3D=20key_str[0]=20&&=20key_str[1])=0A-=20=20=20= =20=20=20=20=20=20=20=20=20{=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20= key_str=20=3D=20&key_str[1];=0A-=20=20=20=20=20=20=20=20=20=20=20=20}=0A= -=20=20=20=20=20=20=20=20=20=20/*=20Only=20add=20element=20if=20key=20is=20= not=20already=20present.=20=20*/=0A-=20=20=20=20=20=20=20=20=20=20if=20= (json_object_get=20(json,=20key_str)=20=3D=3D=20NULL)=0A-=20=20=20=20=20=20= =20=20=20=20=20=20{=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20int=20= status=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=3D=20= json_object_set_new=20(json,=20key_str,=20lisp_to_json=20(value,=0A-=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20conf));=0A-=20=20=20=20=20= =20=20=20=20=20=20=20=20=20if=20(status=20=3D=3D=20-1)=0A-=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20json_out_of_memory=20();=0A-=20=20=20=20= =20=20=20=20=20=20=20=20}=0A-=20=20=20=20=20=20=20=20}=0A-=20=20=20=20=20= =20CHECK_LIST_END=20(tail,=20lisp);=0A-=20=20=20=20}=0A-=20=20else=0A-=20= =20=20=20wrong_type_argument=20(Qjson_value_p,=20lisp);=0A-=0A-=20=20= clear_unwind_protect=20(count);=0A-=20=20unbind_to=20(count,=20Qnil);=0A= -=20=20return=20json;=0A-}=0A-=0A-/*=20Convert=20LISP=20to=20a=20= nonscalar=20JSON=20object=20(array=20or=20object).=20=20Signal=0A-=20=20=20= an=20error=20of=20type=20`wrong-type-argument'=20if=20LISP=20is=20not=20= a=20vector,=0A-=20=20=20hashtable,=20alist,=20or=20plist.=20=20*/=0A-=0A= -static=20json_t=20*=0A-lisp_to_json_nonscalar=20(Lisp_Object=20lisp,=0A= -=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= const=20struct=20json_configuration=20*conf)=0A-{=0A-=20=20if=20= (++lisp_eval_depth=20>=20max_lisp_eval_depth)=0A-=20=20=20=20xsignal0=20= (Qjson_object_too_deep);=0A-=20=20json_t=20*json=20=3D=20= lisp_to_json_nonscalar_1=20(lisp,=20conf);=0A-=20=20--lisp_eval_depth;=0A= -=20=20return=20json;=0A-}=0A-=0A-/*=20Convert=20LISP=20to=20any=20JSON=20= object.=20=20Signal=20an=20error=20of=20type=0A-=20=20=20= `wrong-type-argument'=20if=20the=20type=20of=20LISP=20can't=20be=20= converted=20to=20a=0A-=20=20=20JSON=20object.=20=20*/=0A-=0A-static=20= json_t=20*=0A-lisp_to_json=20(Lisp_Object=20lisp,=20const=20struct=20= json_configuration=20*conf)=0A-{=0A-=20=20if=20(EQ=20(lisp,=20= conf->null_object))=0A-=20=20=20=20return=20json_check=20(json_null=20= ());=0A-=20=20else=20if=20(EQ=20(lisp,=20conf->false_object))=0A-=20=20=20= =20return=20json_check=20(json_false=20());=0A-=20=20else=20if=20(EQ=20= (lisp,=20Qt))=0A-=20=20=20=20return=20json_check=20(json_true=20());=0A-=20= =20else=20if=20(INTEGERP=20(lisp))=0A-=20=20=20=20{=0A-=20=20=20=20=20=20= intmax_t=20low=20=3D=20TYPE_MINIMUM=20(json_int_t);=0A-=20=20=20=20=20=20= intmax_t=20high=20=3D=20TYPE_MAXIMUM=20(json_int_t);=0A-=20=20=20=20=20=20= intmax_t=20value=20=3D=20check_integer_range=20(lisp,=20low,=20high);=0A= -=20=20=20=20=20=20return=20json_check=20(json_integer=20(value));=0A-=20= =20=20=20}=0A-=20=20else=20if=20(FLOATP=20(lisp))=0A-=20=20=20=20return=20= json_check=20(json_real=20(XFLOAT_DATA=20(lisp)));=0A-=20=20else=20if=20= (STRINGP=20(lisp))=0A-=20=20=20=20{=0A-=20=20=20=20=20=20Lisp_Object=20= encoded=20=3D=20json_encode=20(lisp);=0A-=20=20=20=20=20=20json_t=20= *json=20=3D=20json_stringn=20(SSDATA=20(encoded),=20SBYTES=20(encoded));=0A= -=20=20=20=20=20=20if=20(json=20=3D=3D=20NULL)=0A-=20=20=20=20=20=20=20=20= {=0A-=20=20=20=20=20=20=20=20=20=20/*=20A=20failure=20can=20be=20caused=20= either=20by=20an=20invalid=20string=20or=20by=0A-=20=20=20=20=20=20=20=20= =20=20=20=20=20low=20memory.=20=20*/=0A-=20=20=20=20=20=20=20=20=20=20= json_check_utf8=20(encoded);=0A-=20=20=20=20=20=20=20=20=20=20= json_out_of_memory=20();=0A-=20=20=20=20=20=20=20=20}=0A-=20=20=20=20=20=20= return=20json;=0A-=20=20=20=20}=0A-=0A-=20=20/*=20LISP=20now=20must=20be=20= a=20vector,=20hashtable,=20alist,=20or=20plist.=20=20*/=0A-=20=20return=20= lisp_to_json_nonscalar=20(lisp,=20conf);=0A-}=0A-=0A=20static=20void=0A=20= json_parse_args=20(ptrdiff_t=20nargs,=0A=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20Lisp_Object=20*args,=0A@@=20-450,158=20+98,533=20@@=20= json_parse_args=20(ptrdiff_t=20nargs,=0A=20=20=20}=0A=20}=0A=20=0A= -static=20bool=0A-json_available_p=20(void)=0A+/*=20FIXME:=20Remove=20= completely.=20=20*/=0A+DEFUN=20("json--available-p",=20= Fjson__available_p,=20Sjson__available_p,=200,=200,=20NULL,=0A+=20=20=20=20= =20=20=20doc:=20/*=20Return=20non-nil=20if=20libjansson=20is=20available=20= (internal=20use=20only).=20=20*/)=0A+=20=20(void)=0A=20{=0A-#ifdef=20= WINDOWSNT=0A-=20=20if=20(!json_initialized)=0A-=20=20=20=20{=0A-=20=20=20= =20=20=20Lisp_Object=20status;=0A-=20=20=20=20=20=20json_initialized=20=3D= =20init_json_functions=20();=0A-=20=20=20=20=20=20status=20=3D=20= json_initialized=20?=20Qt=20:=20Qnil;=0A-=20=20=20=20=20=20= Vlibrary_cache=20=3D=20Fcons=20(Fcons=20(Qjson,=20status),=20= Vlibrary_cache);=0A-=20=20=20=20}=0A-=20=20return=20json_initialized;=0A= -#else=20=20/*=20!WINDOWSNT=20*/=0A-=20=20return=20true;=0A-#endif=0A+=20= =20return=20Qt;=0A=20}=0A=20=0A-#ifdef=20WINDOWSNT=0A+/*=20JSON=20= encoding=20context.=20=20*/=0A+typedef=20struct=20{=0A+=20=20char=20= *buf;=0A+=20=20ptrdiff_t=20size;=09=20=20=20=20=20=20/*=20number=20of=20= bytes=20in=20buf=20*/=0A+=20=20ptrdiff_t=20capacity;=09=20=20=20=20=20=20= /*=20allocated=20size=20of=20buf=20*/=0A+=20=20ptrdiff_t=20chars_delta;=20= =20=20=20=20=20/*=20size=20-=20{number=20of=20characters=20in=20buf}=20= */=0A+=0A+=20=20int=20maxdepth;=0A+=20=20struct=20symset_tbl=20= *ss_table;=09/*=20table=20used=20by=20containing=20object=20*/=0A+=20=20= struct=20json_configuration=20conf;=0A+}=20json_out_t;=0A+=0A+/*=20Set=20= of=20symbols.=20=20*/=0A+typedef=20struct=20{=0A+=20=20ptrdiff_t=20= count;=09=09/*=20symbols=20in=20table=20*/=0A+=20=20int=20bits;=09=09=09= /*=20log2(table=20size)=20*/=0A+=20=20struct=20symset_tbl=20*table;=09/*=20= heap-allocated=20table=20*/=0A+}=20symset_t;=0A+=0A+struct=20symset_tbl=0A= +{=0A+=20=20/*=20Table=20used=20by=20the=20containing=20object=20if=20= any,=20so=20that=20we=20can=20free=20all=0A+=20=20=20=20=20tables=20if=20= an=20error=20occurs.=20=20*/=0A+=20=20struct=20symset_tbl=20*up;=0A+=20=20= /*=20Table=20of=20symbols=20(2**bits=20elements),=20Qunbound=20where=20= unused.=20=20*/=0A+=20=20Lisp_Object=20entries[];=0A+};=0A+=0A+static=20= inline=20ptrdiff_t=0A+symset_size=20(int=20bits)=0A+{=0A+=20=20return=20= (ptrdiff_t)1=20<<=20bits;=0A+}=0A+=0A+static=20struct=20symset_tbl=20*=0A= +make_symset_table=20(int=20bits,=20struct=20symset_tbl=20*up)=0A+{=0A+=20= =20int=20maxbits=20=3D=20min=20(SIZE_WIDTH=20-=202=20-=20(word_size=20<=20= 8=20?=202=20:=203),=2032);=0A+=20=20if=20(bits=20>=20maxbits)=0A+=20=20=20= =20error=20("out=20of=20memory");=09/*=20Will=20never=20happen=20in=20= practice.=20=20*/=0A+=20=20struct=20symset_tbl=20*st=20=3D=20xnmalloc=20= (sizeof=20*st->entries=20<<=20bits,=20sizeof=20*st);=0A+=20=20st->up=20=3D= =20up;=0A+=20=20ptrdiff_t=20size=20=3D=20symset_size=20(bits);=0A+=20=20= for=20(ptrdiff_t=20i=20=3D=200;=20i=20<=20size;=20i++)=0A+=20=20=20=20= st->entries[i]=20=3D=20Qunbound;=0A+=20=20return=20st;=0A+}=0A+=0A+/*=20= Create=20a=20new=20symset=20to=20use=20for=20a=20new=20object.=20=20*/=0A= +static=20symset_t=0A+push_symset=20(json_out_t=20*jo)=0A+{=0A+=20=20int=20= bits=20=3D=204;=0A+=20=20struct=20symset_tbl=20*tbl=20=3D=20= make_symset_table=20(bits,=20jo->ss_table);=0A+=20=20jo->ss_table=20=3D=20= tbl;=0A+=20=20return=20(symset_t){=20.count=20=3D=200,=20.bits=20=3D=20= bits,=20.table=20=3D=20tbl=20};=0A+}=0A+=0A+/*=20Destroy=20the=20current=20= symset.=20=20*/=0A=20static=20void=0A-ensure_json_available=20(void)=0A= +pop_symset=20(json_out_t=20*jo,=20symset_t=20*ss)=0A=20{=0A-=20=20if=20= (!json_available_p=20())=0A-=20=20=20=20Fsignal=20(Qjson_unavailable,=0A= -=09=20=20=20=20=20list1=20(build_unibyte_string=20("jansson=20library=20= not=20found")));=0A+=20=20jo->ss_table=20=3D=20ss->table->up;=0A+=20=20= xfree=20(ss->table);=0A=20}=0A-#endif=0A=20=0A-DEFUN=20= ("json--available-p",=20Fjson__available_p,=20Sjson__available_p,=200,=20= 0,=20NULL,=0A-=20=20=20=20=20=20=20doc:=20/*=20Return=20non-nil=20if=20= libjansson=20is=20available=20(internal=20use=20only).=20=20*/)=0A-=20=20= (void)=0A+/*=20Remove=20all=20heap-allocated=20symset=20tables,=20in=20= case=20an=20error=20occurred.=20=20*/=0A+static=20void=0A= +cleanup_symset_tables=20(struct=20symset_tbl=20*st)=0A=20{=0A-=20=20= return=20json_available_p=20()=20?=20Qt=20:=20Qnil;=0A+=20=20while=20= (st)=0A+=20=20=20=20{=0A+=20=20=20=20=20=20struct=20symset_tbl=20*up=20=3D= =20st->up;=0A+=20=20=20=20=20=20xfree=20(st);=0A+=20=20=20=20=20=20st=20= =3D=20up;=0A+=20=20=20=20}=0A=20}=0A=20=0A-DEFUN=20("json-serialize",=20= Fjson_serialize,=20Sjson_serialize,=201,=20MANY,=0A-=20=20=20=20=20=20=20= NULL,=0A-=20=20=20=20=20=20=20doc:=20/*=20Return=20the=20JSON=20= representation=20of=20OBJECT=20as=20a=20string.=0A+static=20inline=20= uint32_t=0A+symset_hash=20(Lisp_Object=20sym,=20int=20bits)=0A+{=0A+=20=20= return=20knuth_hash=20(reduce_emacs_uint_to_hash_hash=20(XHASH=20(sym)),=20= bits);=0A+}=0A=20=0A-OBJECT=20must=20be=20t,=20a=20number,=20string,=20= vector,=20hashtable,=20alist,=20plist,=0A-or=20the=20Lisp=20equivalents=20= to=20the=20JSON=20null=20and=20false=20values,=20and=20its=0A-elements=20= must=20recursively=20consist=20of=20the=20same=20kinds=20of=20values.=20=20= t=20will=0A-be=20converted=20to=20the=20JSON=20true=20value.=20=20= Vectors=20will=20be=20converted=20to=0A-JSON=20arrays,=20whereas=20= hashtables,=20alists=20and=20plists=20are=20converted=20to=0A-JSON=20= objects.=20=20Hashtable=20keys=20must=20be=20strings=20without=20= embedded=20null=0A-characters=20and=20must=20be=20unique=20within=20each=20= object.=20=20Alist=20and=20plist=0A-keys=20must=20be=20symbols;=20if=20a=20= key=20is=20duplicate,=20the=20first=20instance=20is=0A-used.=0A+/*=20= Enlarge=20the=20table=20used=20by=20a=20symset.=20=20*/=0A+static=20= NO_INLINE=20void=0A+symset_expand=20(symset_t=20*ss)=0A+{=0A+=20=20= struct=20symset_tbl=20*old_table=20=3D=20ss->table;=0A+=20=20int=20= oldbits=20=3D=20ss->bits;=0A+=20=20ptrdiff_t=20oldsize=20=3D=20= symset_size=20(oldbits);=0A+=20=20int=20bits=20=3D=20oldbits=20+=201;=0A= +=20=20ss->bits=20=3D=20bits;=0A+=20=20ss->table=20=3D=20= make_symset_table=20(bits,=20old_table->up);=0A+=20=20/*=20Move=20all=20= entries=20from=20the=20old=20table=20to=20the=20new=20one.=20=20*/=0A+=20= =20ptrdiff_t=20mask=20=3D=20symset_size=20(bits)=20-=201;=0A+=20=20= struct=20symset_tbl=20*tbl=20=3D=20ss->table;=0A+=20=20for=20(ptrdiff_t=20= i=20=3D=200;=20i=20<=20oldsize;=20i++)=0A+=20=20=20=20{=0A+=20=20=20=20=20= =20Lisp_Object=20sym=20=3D=20old_table->entries[i];=0A+=20=20=20=20=20=20= if=20(!BASE_EQ=20(sym,=20Qunbound))=0A+=09{=0A+=09=20=20ptrdiff_t=20j=20= =3D=20symset_hash=20(sym,=20bits);=0A+=09=20=20while=20(!BASE_EQ=20= (tbl->entries[j],=20Qunbound))=0A+=09=20=20=20=20j=20=3D=20(j=20+=201)=20= &=20mask;=0A+=09=20=20tbl->entries[j]=20=3D=20sym;=0A+=09}=0A+=20=20=20=20= }=0A+=20=20xfree=20(old_table);=0A+}=0A=20=0A-The=20Lisp=20equivalents=20= to=20the=20JSON=20null=20and=20false=20values=20are=0A-configurable=20in=20= the=20arguments=20ARGS,=20a=20list=20of=20keyword/argument=20pairs:=0A= +/*=20If=20sym=20is=20in=20ss,=20return=20false;=20otherwise=20add=20it=20= and=20return=20true.=0A+=20=20=20Comparison=20is=20done=20by=20strict=20= identity.=20=20*/=0A+static=20inline=20bool=0A+symset_add=20(json_out_t=20= *jo,=20symset_t=20*ss,=20Lisp_Object=20sym)=0A+{=0A+=20=20/*=20Make=20= sure=20we=20don't=20fill=20more=20than=20half=20of=20the=20table.=20=20= */=0A+=20=20if=20(ss->count=20>=3D=20(symset_size=20(ss->bits)=20>>=20= 1))=0A+=20=20=20=20{=0A+=20=20=20=20=20=20symset_expand=20(ss);=0A+=20=20= =20=20=20=20jo->ss_table=20=3D=20ss->table;=0A+=20=20=20=20}=0A=20=0A= -The=20keyword=20argument=20`:null-object'=20specifies=20which=20object=20= to=20use=0A-to=20represent=20a=20JSON=20null=20value.=20=20It=20defaults=20= to=20`:null'.=0A+=20=20struct=20symset_tbl=20*tbl=20=3D=20ss->table;=0A+=20= =20ptrdiff_t=20mask=20=3D=20symset_size=20(ss->bits)=20-=201;=0A+=20=20= for=20(ptrdiff_t=20i=20=3D=20symset_hash=20(sym,=20ss->bits);=20;=20i=20= =3D=20(i=20+=201)=20&=20mask)=0A+=20=20=20=20{=0A+=20=20=20=20=20=20= Lisp_Object=20s=20=3D=20tbl->entries[i];=0A+=20=20=20=20=20=20if=20= (BASE_EQ=20(s,=20sym))=0A+=09return=20false;=09=09/*=20Previous=20= occurrence=20found.=20=20*/=0A+=20=20=20=20=20=20if=20(BASE_EQ=20(s,=20= Qunbound))=0A+=09{=0A+=09=20=20/*=20Not=20in=20set,=20add=20it.=20=20*/=0A= +=09=20=20tbl->entries[i]=20=3D=20sym;=0A+=09=20=20ss->count++;=0A+=09=20= =20return=20true;=0A+=09}=0A+=20=20=20=20}=0A+}=0A=20=0A-The=20keyword=20= argument=20`:false-object'=20specifies=20which=20object=20to=20use=20to=0A= -represent=20a=20JSON=20false=20value.=20=20It=20defaults=20to=20= `:false'.=0A+static=20NO_INLINE=20void=0A+json_out_grow_buf=20= (json_out_t=20*jo,=20ptrdiff_t=20bytes)=0A+{=0A+=20=20ptrdiff_t=20need=20= =3D=20jo->size=20+=20bytes;=0A+=20=20ptrdiff_t=20new_size=20=3D=20max=20= (jo->capacity,=20512);=0A+=20=20while=20(new_size=20<=20need)=0A+=20=20=20= =20new_size=20<<=3D=201;=0A+=20=20jo->buf=20=3D=20xrealloc=20(jo->buf,=20= new_size);=0A+=20=20jo->capacity=20=3D=20new_size;=0A+}=0A=20=0A-In=20= you=20specify=20the=20same=20value=20for=20`:null-object'=20and=20= `:false-object',=0A-a=20potentially=20ambiguous=20situation,=20the=20= JSON=20output=20will=20not=20contain=0A-any=20JSON=20false=20values.=0A= -usage:=20(json-serialize=20OBJECT=20&rest=20ARGS)=20=20*/)=0A-=20=20=20=20= =20(ptrdiff_t=20nargs,=20Lisp_Object=20*args)=0A+static=20void=0A= +cleanup_json_out=20(void=20*arg)=0A=20{=0A-=20=20specpdl_ref=20count=20= =3D=20SPECPDL_INDEX=20();=0A+=20=20json_out_t=20*jo=20=3D=20arg;=0A+=20=20= xfree=20(jo->buf);=0A+=20=20jo->buf=20=3D=20NULL;=0A+=20=20= cleanup_symset_tables=20(jo->ss_table);=0A+}=0A=20=0A-#ifdef=20WINDOWSNT=0A= -=20=20ensure_json_available=20();=0A-#endif=0A+/*=20Make=20room=20for=20= `bytes`=20more=20bytes=20in=20buffer.=20=20*/=0A+static=20void=0A= +json_make_room=20(json_out_t=20*jo,=20ptrdiff_t=20bytes)=0A+{=0A+=20=20= if=20(bytes=20>=20jo->capacity=20-=20jo->size)=0A+=20=20=20=20= json_out_grow_buf=20(jo,=20bytes);=0A+}=0A=20=0A-=20=20struct=20= json_configuration=20conf=20=3D=0A-=20=20=20=20{json_object_hashtable,=20= json_array_array,=20QCnull,=20QCfalse};=0A-=20=20json_parse_args=20= (nargs=20-=201,=20args=20+=201,=20&conf,=20false);=0A+#define=20= JSON_OUT_STR(jo,=20str)=20(json_out_str=20(jo,=20str,=20sizeof=20(str)=20= -=201))=0A=20=0A-=20=20json_t=20*json=20=3D=20lisp_to_json=20(args[0],=20= &conf);=0A-=20=20record_unwind_protect_ptr=20(json_release_object,=20= json);=0A+/*=20Add=20`bytes`=20bytes=20from=20`str`=20to=20the=20buffer.=20= =20*/=0A+static=20void=0A+json_out_str=20(json_out_t=20*jo,=20const=20= char=20*str,=20size_t=20bytes)=0A+{=0A+=20=20json_make_room=20(jo,=20= bytes);=0A+=20=20memcpy=20(jo->buf=20+=20jo->size,=20str,=20bytes);=0A+=20= =20jo->size=20+=3D=20bytes;=0A+}=0A=20=0A-=20=20char=20*string=20=3D=20= json_dumps=20(json,=20JSON_COMPACT=20|=20JSON_ENCODE_ANY);=0A-=20=20if=20= (string=20=3D=3D=20NULL)=0A-=20=20=20=20json_out_of_memory=20();=0A-=20=20= record_unwind_protect_ptr=20(json_free,=20string);=0A+static=20void=0A= +json_out_byte=20(json_out_t=20*jo,=20unsigned=20char=20c)=0A+{=0A+=20=20= json_make_room=20(jo,=201);=0A+=20=20jo->buf[jo->size++]=20=3D=20c;=0A+}=0A= =20=0A-=20=20return=20unbind_to=20(count,=20build_string_from_utf8=20= (string));=0A+static=20void=0A+json_out_fixnum=20(json_out_t=20*jo,=20= EMACS_INT=20x)=0A+{=0A+=20=20char=20buf[INT_BUFSIZE_BOUND=20= (EMACS_INT)];=0A+=20=20char=20*end=20=3D=20buf=20+=20sizeof=20buf;=0A+=20= =20char=20*p=20=3D=20fixnum_to_string=20(x,=20buf,=20end);=0A+=20=20= json_out_str=20(jo,=20p,=20end=20-=20p);=0A=20}=0A=20=0A-struct=20= json_buffer_and_size=0A+static=20AVOID=0A+string_not_unicode=20= (Lisp_Object=20obj)=0A=20{=0A-=20=20const=20char=20*buffer;=0A-=20=20= ptrdiff_t=20size;=0A-=20=20/*=20This=20tracks=20how=20many=20bytes=20= were=20inserted=20by=20the=20callback=20since=0A-=20=20=20=20=20= json_dump_callback=20was=20called.=20=20*/=0A-=20=20ptrdiff_t=20= inserted_bytes;=0A+=20=20/*=20FIXME:=20this=20is=20just=20for=20= compatibility=20with=20existing=20tests,=20it's=20not=0A+=20=20=20=20=20= a=20very=20descriptive=20error.=20=20*/=0A+=20=20wrong_type_argument=20= (Qjson_value_p,=20obj);=0A+}=0A+=0A+static=20const=20unsigned=20char=20= json_plain_char[256]=20=3D=20{=0A+=20=20/*=2032=20chars/line:=201=20for=20= printable=20ASCII=20+=20DEL=20except=20"=20and=20\,=200=20elsewhere=20*/=0A= +=20=200,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,=20= /*=2000-1f=20*/=0A+=20=20= 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/*=20= 20-3f=20*/=0A+=20=20= 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,=20/*=20= 40-5f=20*/=0A+=20=20= 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,=20/*=20= 60-7f=20*/=0A+=20=20= 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,=20/*=20= 80-9f=20*/=0A+=20=20= 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,=20/*=20= a0-bf=20*/=0A+=20=20= 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,=20/*=20= c0-df=20*/=0A+=20=20= 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,=20/*=20= e0-ff=20*/=0A=20};=0A=20=0A-static=20Lisp_Object=0A-json_insert=20(void=20= *data)=0A+static=20void=0A+json_out_string=20(json_out_t=20*jo,=20= Lisp_Object=20str,=20int=20skip)=0A+{=0A+=20=20/*=20FIXME:=20this=20code=20= is=20slow,=20make=20faster!=20*/=0A+=0A+=20=20static=20const=20char=20= hexchar[16]=20=3D=20"0123456789ABCDEF";=0A+=20=20ptrdiff_t=20len=20=3D=20= SBYTES=20(str);=0A+=20=20json_make_room=20(jo,=20len=20+=202);=0A+=20=20= json_out_byte=20(jo,=20'"');=0A+=20=20unsigned=20char=20*p=20=3D=20SDATA=20= (str);=0A+=20=20unsigned=20char=20*end=20=3D=20p=20+=20len;=0A+=20=20p=20= +=3D=20skip;=0A+=20=20while=20(p=20<=20end)=0A+=20=20=20=20{=0A+=20=20=20= =20=20=20unsigned=20char=20c=20=3D=20*p;=0A+=20=20=20=20=20=20if=20= (json_plain_char[c])=0A+=09{=0A+=09=20=20json_out_byte=20(jo,=20c);=0A+=09= =20=20p++;=0A+=09}=0A+=20=20=20=20=20=20else=20if=20(c=20>=200x7f)=0A+=09= {=0A+=09=20=20if=20(STRING_MULTIBYTE=20(str))=0A+=09=20=20=20=20{=0A+=09=20= =20=20=20=20=20int=20n;=0A+=09=20=20=20=20=20=20if=20(c=20<=3D=200xc1)=0A= +=09=09string_not_unicode=20(str);=0A+=09=20=20=20=20=20=20if=20(c=20<=3D=20= 0xdf)=0A+=09=09n=20=3D=202;=0A+=09=20=20=20=20=20=20else=20if=20(c=20<=3D=20= 0xef)=0A+=09=09{=0A+=09=09=20=20int=20v=20=3D=20(((c=20&=200x0f)=20<<=20= 12)=0A+=09=09=09=20=20=20+=20((p[1]=20&=200x3f)=20<<=206)=20+=20(p[2]=20= &=200x3f));=0A+=09=09=20=20if=20(char_surrogate_p=20(v))=0A+=09=09=20=20=20= =20string_not_unicode=20(str);=0A+=09=09=20=20n=20=3D=203;=0A+=09=09}=0A= +=09=20=20=20=20=20=20else=20if=20(c=20<=3D=200xf7)=0A+=09=09{=0A+=09=09=20= =20int=20v=20=3D=20(((c=20&=200x07)=20<<=2018)=0A+=09=09=09=20=20=20+=20= ((p[1]=20&=200x3f)=20<<=2012)=0A+=09=09=09=20=20=20+=20((p[2]=20&=20= 0x3f)=20<<=206)=0A+=09=09=09=20=20=20+=20(p[3]=20&=200x3f));=0A+=09=09=20= =20if=20(v=20>=20MAX_UNICODE_CHAR)=0A+=09=09=20=20=20=20= string_not_unicode=20(str);=0A+=09=09=20=20n=20=3D=204;=0A+=09=09}=0A+=09= =20=20=20=20=20=20else=0A+=09=09string_not_unicode=20(str);=0A+=09=20=20=20= =20=20=20json_out_str=20(jo,=20(const=20char=20*)p,=20n);=0A+=09=20=20=20= =20=20=20jo->chars_delta=20+=3D=20n=20-=201;=0A+=09=20=20=20=20=20=20p=20= +=3D=20n;=0A+=09=20=20=20=20}=0A+=09=20=20else=0A+=09=20=20=20=20= string_not_unicode=20(str);=0A+=09}=0A+=20=20=20=20=20=20else=0A+=09{=0A= +=09=20=20json_out_byte=20(jo,=20'\\');=0A+=09=20=20switch=20(c)=0A+=09=20= =20=20=20{=0A+=09=20=20=20=20case=20'"':=0A+=09=20=20=20=20case=20'\\':=20= json_out_byte=20(jo,=20c);=20break;=0A+=09=20=20=20=20case=20'\b':=20= json_out_byte=20(jo,=20'b');=20break;=0A+=09=20=20=20=20case=20'\t':=20= json_out_byte=20(jo,=20't');=20break;=0A+=09=20=20=20=20case=20'\n':=20= json_out_byte=20(jo,=20'n');=20break;=0A+=09=20=20=20=20case=20'\f':=20= json_out_byte=20(jo,=20'f');=20break;=0A+=09=20=20=20=20case=20'\r':=20= json_out_byte=20(jo,=20'r');=20break;=0A+=09=20=20=20=20default:=0A+=09=20= =20=20=20=20=20{=0A+=09=09char=20hex[5]=20=3D=20{=20'u',=20'0',=20'0',=0A= +=09=09=09=09hexchar[c=20>>=204],=20hexchar[c=20&=200xf]=20};=0A+=09=09= json_out_str=20(jo,=20hex,=205);=0A+=09=09break;=0A+=09=20=20=20=20=20=20= }=0A+=09=20=20=20=20}=0A+=09=20=20p++;=0A+=09}=0A+=20=20=20=20}=0A+=20=20= json_out_byte=20(jo,=20'"');=0A+}=0A+=0A+static=20void=0A+json_out_nest=20= (json_out_t=20*jo)=0A+{=0A+=20=20--jo->maxdepth;=0A+=20=20if=20= (jo->maxdepth=20<=200)=0A+=20=20=20=20error=20("Maximum=20JSON=20= serialisation=20depth=20exceeded");=0A+}=0A+=0A+static=20void=0A= +json_out_unnest=20(json_out_t=20*jo)=0A=20{=0A-=20=20struct=20= json_buffer_and_size=20*buffer_and_size=20=3D=20data;=0A-=20=20ptrdiff_t=20= len=20=3D=20buffer_and_size->size;=0A-=20=20ptrdiff_t=20inserted_bytes=20= =3D=20buffer_and_size->inserted_bytes;=0A-=20=20ptrdiff_t=20gap_size=20=3D= =20GAP_SIZE=20-=20inserted_bytes;=0A+=20=20++jo->maxdepth;=0A+}=0A=20=0A= -=20=20/*=20Enlarge=20the=20gap=20if=20necessary.=20=20*/=0A-=20=20if=20= (gap_size=20<=20len)=0A-=20=20=20=20make_gap=20(len=20-=20gap_size);=0A= +static=20void=20json_out_something=20(json_out_t=20*jo,=20Lisp_Object=20= obj);=0A=20=0A-=20=20/*=20Copy=20this=20chunk=20of=20data=20into=20the=20= gap.=20=20*/=0A-=20=20memcpy=20((char=20*)=20BEG_ADDR=20+=20PT_BYTE=20-=20= BEG_BYTE=20+=20inserted_bytes,=0A-=09=20=20buffer_and_size->buffer,=20= len);=0A-=20=20buffer_and_size->inserted_bytes=20+=3D=20len;=0A-=20=20= return=20Qnil;=0A+static=20void=0A+json_out_object_cons=20(json_out_t=20= *jo,=20Lisp_Object=20obj)=0A+{=0A+=20=20json_out_nest=20(jo);=0A+=20=20= symset_t=20ss=20=3D=20push_symset=20(jo);=0A+=20=20json_out_byte=20(jo,=20= '{');=0A+=20=20bool=20is_alist=20=3D=20CONSP=20(XCAR=20(obj));=0A+=20=20= bool=20first=20=3D=20true;=0A+=20=20Lisp_Object=20tail=20=3D=20obj;=0A+=20= =20FOR_EACH_TAIL=20(tail)=0A+=20=20=20=20{=0A+=20=20=20=20=20=20= Lisp_Object=20key;=0A+=20=20=20=20=20=20Lisp_Object=20value;=0A+=20=20=20= =20=20=20if=20(is_alist)=0A+=09{=0A+=09=20=20Lisp_Object=20pair=20=3D=20= XCAR=20(tail);=0A+=09=20=20CHECK_CONS=20(pair);=0A+=09=20=20key=20=3D=20= XCAR=20(pair);=0A+=09=20=20value=20=3D=20XCDR=20(pair);=0A+=09}=0A+=20=20= =20=20=20=20else=0A+=09{=0A+=09=20=20key=20=3D=20XCAR=20(tail);=0A+=09=20= =20tail=20=3D=20XCDR=20(tail);=0A+=09=20=20CHECK_CONS=20(tail);=0A+=09=20= =20value=20=3D=20XCAR=20(tail);=0A+=09}=0A+=20=20=20=20=20=20key=20=3D=20= maybe_remove_pos_from_symbol=20(key);=0A+=20=20=20=20=20=20CHECK_TYPE=20= (BARE_SYMBOL_P=20(key),=20Qsymbolp,=20key);=0A+=0A+=20=20=20=20=20=20if=20= (symset_add=20(jo,=20&ss,=20key))=0A+=09{=0A+=09=20=20if=20(!first)=0A+=09= =20=20=20=20json_out_byte=20(jo,=20',');=0A+=09=20=20first=20=3D=20= false;=0A+=0A+=09=20=20Lisp_Object=20key_str=20=3D=20SYMBOL_NAME=20= (key);=0A+=09=20=20const=20char=20*str=20=3D=20SSDATA=20(key_str);=0A+=09= =20=20/*=20Skip=20leading=20':'=20in=20plist=20keys.=20=20*/=0A+=09=20=20= int=20skip=20=3D=20!is_alist=20&&=20str[0]=20=3D=3D=20':'=20&&=20str[1]=20= ?=201=20:=200;=0A+=09=20=20json_out_string=20(jo,=20key_str,=20skip);=0A= +=09=20=20json_out_byte=20(jo,=20':');=0A+=09=20=20json_out_something=20= (jo,=20value);=0A+=09}=0A+=20=20=20=20}=0A+=20=20CHECK_LIST_END=20(tail,=20= obj);=0A+=20=20json_out_byte=20(jo,=20'}');=0A+=20=20pop_symset=20(jo,=20= &ss);=0A+=20=20json_out_unnest=20(jo);=0A=20}=0A=20=0A-static=20= Lisp_Object=0A-json_handle_nonlocal_exit=20(enum=20nonlocal_exit=20type,=20= Lisp_Object=20data)=0A+static=20void=0A+json_out_object_hash=20= (json_out_t=20*jo,=20Lisp_Object=20obj)=0A=20{=0A-=20=20switch=20(type)=0A= +=20=20json_out_nest=20(jo);=0A+=20=20json_out_byte=20(jo,=20'{');=0A+=20= =20struct=20Lisp_Hash_Table=20*h=20=3D=20XHASH_TABLE=20(obj);=0A+=20=20= bool=20first=20=3D=20true;=0A+=20=20DOHASH=20(h,=20k,=20v)=0A=20=20=20=20= =20{=0A-=20=20=20=20case=20NONLOCAL_EXIT_SIGNAL:=0A-=20=20=20=20=20=20= return=20data;=0A-=20=20=20=20case=20NONLOCAL_EXIT_THROW:=0A-=20=20=20=20= =20=20return=20Fcons=20(Qno_catch,=20data);=0A-=20=20=20=20default:=0A-=20= =20=20=20=20=20eassume=20(false);=0A+=20=20=20=20=20=20if=20(!first)=0A+=09= json_out_byte=20(jo,=20',');=0A+=20=20=20=20=20=20first=20=3D=20false;=0A= +=20=20=20=20=20=20CHECK_STRING=20(k);=0A+=20=20=20=20=20=20/*=20It's=20= the=20user's=20responsibility=20to=20ensure=20that=20hash=20keys=20are=0A= +=09=20unique;=20we=20don't=20check=20for=20it.=20=20*/=0A+=20=20=20=20=20= =20json_out_string=20(jo,=20k,=200);=0A+=20=20=20=20=20=20json_out_byte=20= (jo,=20':');=0A+=20=20=20=20=20=20json_out_something=20(jo,=20v);=0A=20=20= =20=20=20}=0A+=20=20json_out_byte=20(jo,=20'}');=0A+=20=20= json_out_unnest=20(jo);=0A+=0A=20}=0A=20=0A-struct=20json_insert_data=0A= +static=20void=0A+json_out_array=20(json_out_t=20*jo,=20Lisp_Object=20= obj)=0A=20{=0A-=20=20/*=20This=20tracks=20how=20many=20bytes=20were=20= inserted=20by=20the=20callback=20since=0A-=20=20=20=20=20= json_dump_callback=20was=20called.=20=20*/=0A-=20=20ptrdiff_t=20= inserted_bytes;=0A-=20=20/*=20nil=20if=20json_insert=20succeeded,=20= otherwise=20the=20symbol=0A-=20=20=20=20=20Qcatch_all_memory_full=20or=20= a=20cons=20(ERROR-SYMBOL=20.=20ERROR-DATA).=20=20*/=0A-=20=20Lisp_Object=20= error;=0A-};=0A+=20=20json_out_nest=20(jo);=0A+=20=20json_out_byte=20= (jo,=20'[');=0A+=20=20ptrdiff_t=20n=20=3D=20ASIZE=20(obj);=0A+=20=20for=20= (ptrdiff_t=20i=20=3D=200;=20i=20<=20n;=20i++)=0A+=20=20=20=20{=0A+=20=20=20= =20=20=20if=20(i=20>=200)=0A+=09json_out_byte=20(jo,=20',');=0A+=20=20=20= =20=20=20json_out_something=20(jo,=20AREF=20(obj,=20i));=0A+=20=20=20=20= }=0A+=20=20json_out_byte=20(jo,=20']');=0A+=20=20json_out_unnest=20(jo);=0A= +}=0A=20=0A-/*=20Callback=20for=20json_dump_callback=20that=20inserts=20= a=20JSON=20representation=0A-=20=20=20as=20a=20unibyte=20string=20into=20= the=20gap.=20=20DATA=20must=20point=20to=20a=20structure=0A-=20=20=20of=20= type=20json_insert_data.=20=20This=20function=20may=20not=20exit=20= nonlocally.=0A-=20=20=20It=20catches=20all=20nonlocal=20exits=20and=20= stores=20them=20in=20data->error=20for=0A-=20=20=20reraising.=20=20*/=0A= +static=20void=0A+json_out_float=20(json_out_t=20*jo,=20Lisp_Object=20f)=0A= +{=0A+=20=20double=20x=20=3D=20XFLOAT_DATA=20(f);=0A+=20=20if=20(isinf=20= (x)=20||=20isnan=20(x))=0A+=20=20=20=20signal_error=20("not=20a=20finite=20= number",=20f);=0A+=20=20/*=20As=20luck=20has=20it,=20float_to_string=20= emits=20correct=20JSON=20float=20syntax=20for=0A+=20=20=20=20=20all=20= numbers=20(because=20Vfloat_output_format=20is=20Qnil).=20=20*/=0A+=20=20= json_make_room=20(jo,=20FLOAT_TO_STRING_BUFSIZE);=0A+=20=20int=20n=20=3D=20= float_to_string=20(jo->buf=20+=20jo->size,=20x);=0A+=20=20jo->size=20+=3D=20= n;=0A+}=0A=20=0A-static=20int=0A-json_insert_callback=20(const=20char=20= *buffer,=20size_t=20size,=20void=20*data)=0A+static=20void=0A= +json_out_bignum=20(json_out_t=20*jo,=20Lisp_Object=20x)=0A=20{=0A-=20=20= struct=20json_insert_data=20*d=20=3D=20data;=0A-=20=20struct=20= json_buffer_and_size=20buffer_and_size=0A-=20=20=20=20=3D=20{.buffer=20=3D= =20buffer,=20.size=20=3D=20size,=20.inserted_bytes=20=3D=20= d->inserted_bytes};=0A-=20=20d->error=20=3D=20internal_catch_all=20= (json_insert,=20&buffer_and_size,=0A-=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= json_handle_nonlocal_exit);=0A-=20=20d->inserted_bytes=20=3D=20= buffer_and_size.inserted_bytes;=0A-=20=20return=20NILP=20(d->error)=20?=20= 0=20:=20-1;=0A+=20=20int=20base=20=3D=2010;=0A+=20=20ptrdiff_t=20size=20= =3D=20bignum_bufsize=20(x,=20base);=0A+=20=20json_make_room=20(jo,=20= size);=0A+=20=20int=20n=20=3D=20bignum_to_c_string=20(jo->buf=20+=20= jo->size,=20size,=20x,=20base);=0A+=20=20jo->size=20+=3D=20n;=0A+}=0A+=0A= +static=20void=0A+json_out_something=20(json_out_t=20*jo,=20Lisp_Object=20= obj)=0A+{=0A+=20=20if=20(EQ=20(obj,=20jo->conf.null_object))=0A+=20=20=20= =20JSON_OUT_STR=20(jo,=20"null");=0A+=20=20else=20if=20(EQ=20(obj,=20= jo->conf.false_object))=0A+=20=20=20=20JSON_OUT_STR=20(jo,=20"false");=0A= +=20=20else=20if=20(EQ=20(obj,=20Qt))=0A+=20=20=20=20JSON_OUT_STR=20(jo,=20= "true");=0A+=20=20else=20if=20(NILP=20(obj))=0A+=20=20=20=20JSON_OUT_STR=20= (jo,=20"{}");=0A+=20=20else=20if=20(FIXNUMP=20(obj))=0A+=20=20=20=20= json_out_fixnum=20(jo,=20XFIXNUM=20(obj));=0A+=20=20else=20if=20(STRINGP=20= (obj))=0A+=20=20=20=20json_out_string=20(jo,=20obj,=200);=0A+=20=20else=20= if=20(CONSP=20(obj))=0A+=20=20=20=20json_out_object_cons=20(jo,=20obj);=0A= +=20=20else=20if=20(FLOATP=20(obj))=0A+=20=20=20=20json_out_float=20(jo,=20= obj);=0A+=20=20else=20if=20(HASH_TABLE_P=20(obj))=0A+=20=20=20=20= json_out_object_hash=20(jo,=20obj);=0A+=20=20else=20if=20(VECTORP=20= (obj))=0A+=20=20=20=20json_out_array=20(jo,=20obj);=0A+=20=20else=20if=20= (BIGNUMP=20(obj))=0A+=20=20=20=20json_out_bignum=20(jo,=20obj);=0A+=20=20= else=0A+=20=20=20=20wrong_type_argument=20(Qjson_value_p,=20obj);=0A+}=0A= +=0A+static=20Lisp_Object=0A+json_out_to_string=20(json_out_t=20*jo)=0A= +{=0A+=20=20/*=20FIXME:=20should=20this=20be=20a=20unibyte=20or=20= multibyte=20string?=0A+=20=20=20=20=20Right=20now=20we=20make=20a=20= multibyte=20string=20for=20test=20compatibility,=0A+=20=20=20=20=20but=20= we=20are=20really=20encoding=20so=20unibyte=20would=20make=20more=20= sense.=20=20*/=0A+=20=20ptrdiff_t=20nchars=20=3D=20jo->size=20-=20= jo->chars_delta;=0A+=20=20return=20make_multibyte_string=20(jo->buf,=20= nchars,=20jo->size);=0A+}=0A+=0A+static=20void=0A+json_serialize=20= (json_out_t=20*jo,=20Lisp_Object=20object,=0A+=09=09ptrdiff_t=20nargs,=20= Lisp_Object=20*args)=0A+{=0A+=20=20*jo=20=3D=20(json_out_t)=20{=0A+=20=20= =20=20/*=20The=20maximum=20nesting=20depth=20allowed=20should=20be=20= sufficient=20for=20most=0A+=20=20=20=20=20=20=20uses=20but=20could=20be=20= raised=20if=20necessary.=20=20(The=20default=20maximum=0A+=20=20=20=20=20= =20=20depth=20for=20JSON_checker=20is=2020.)=20=20*/=0A+=20=20=20=20= .maxdepth=20=3D=2050,=0A+=20=20=20=20.conf=20=3D=20= {json_object_hashtable,=20json_array_array,=20QCnull,=20QCfalse}=0A+=20=20= };=0A+=20=20json_parse_args=20(nargs,=20args,=20&jo->conf,=20false);=0A+=20= =20record_unwind_protect_ptr=20(cleanup_json_out,=20jo);=0A+=0A+=20=20/*=20= Make=20float=20conversion=20independent=20of=20float-output-format.=20=20= */=0A+=20=20if=20(!NILP=20(Vfloat_output_format))=0A+=20=20=20=20= specbind=20(Qfloat_output_format,=20Qnil);=0A+=0A+=20=20= json_out_something=20(jo,=20object);=0A+}=0A+=0A+DEFUN=20= ("json-serialize",=20Fjson_serialize,=20Sjson_serialize,=201,=20MANY,=0A= +=20=20=20=20=20=20=20NULL,=0A+=20=20=20=20=20=20=20doc:=20/*=20Return=20= the=20JSON=20representation=20of=20OBJECT=20as=20a=20string.=0A+=0A= +OBJECT=20must=20be=20t,=20a=20number,=20string,=20vector,=20hashtable,=20= alist,=20plist,=0A+or=20the=20Lisp=20equivalents=20to=20the=20JSON=20= null=20and=20false=20values,=20and=20its=0A+elements=20must=20= recursively=20consist=20of=20the=20same=20kinds=20of=20values.=20=20t=20= will=0A+be=20converted=20to=20the=20JSON=20true=20value.=20=20Vectors=20= will=20be=20converted=20to=0A+JSON=20arrays,=20whereas=20hashtables,=20= alists=20and=20plists=20are=20converted=20to=0A+JSON=20objects.=20=20= Hashtable=20keys=20must=20be=20strings,=20unique=20within=20each=20= object.=0A+Alist=20and=20plist=20keys=20must=20be=20symbols;=20if=20a=20= key=20is=20duplicate,=20the=20first=0A+instance=20is=20used.=20=20A=20= leading=20colon=20in=20plist=20keys=20is=20elided.=0A+=0A+The=20Lisp=20= equivalents=20to=20the=20JSON=20null=20and=20false=20values=20are=0A= +configurable=20in=20the=20arguments=20ARGS,=20a=20list=20of=20= keyword/argument=20pairs:=0A+=0A+The=20keyword=20argument=20= `:null-object'=20specifies=20which=20object=20to=20use=0A+to=20represent=20= a=20JSON=20null=20value.=20=20It=20defaults=20to=20`:null'.=0A+=0A+The=20= keyword=20argument=20`:false-object'=20specifies=20which=20object=20to=20= use=20to=0A+represent=20a=20JSON=20false=20value.=20=20It=20defaults=20= to=20`:false'.=0A+=0A+In=20you=20specify=20the=20same=20value=20for=20= `:null-object'=20and=20`:false-object',=0A+a=20potentially=20ambiguous=20= situation,=20the=20JSON=20output=20will=20not=20contain=0A+any=20JSON=20= false=20values.=0A+usage:=20(json-serialize=20OBJECT=20&rest=20ARGS)=20=20= */)=0A+=20=20=20=20=20(ptrdiff_t=20nargs,=20Lisp_Object=20*args)=0A+{=0A= +=20=20specpdl_ref=20count=20=3D=20SPECPDL_INDEX=20();=0A+=20=20= json_out_t=20jo;=0A+=20=20json_serialize=20(&jo,=20args[0],=20nargs=20-=20= 1,=20args=20+=201);=0A+=20=20return=20unbind_to=20(count,=20= json_out_to_string=20(&jo));=0A=20}=0A=20=0A=20DEFUN=20("json-insert",=20= Fjson_insert,=20Sjson_insert,=201,=20MANY,=0A@@=20-614,71=20+637,52=20@@=20= DEFUN=20("json-insert",=20Fjson_insert,=20Sjson_insert,=201,=20MANY,=0A=20= =20=20=20=20=20(ptrdiff_t=20nargs,=20Lisp_Object=20*args)=0A=20{=0A=20=20= =20specpdl_ref=20count=20=3D=20SPECPDL_INDEX=20();=0A+=20=20json_out_t=20= jo;=0A+=20=20json_serialize=20(&jo,=20args[0],=20nargs=20-=201,=20args=20= +=201);=0A=20=0A-#ifdef=20WINDOWSNT=0A-=20=20ensure_json_available=20();=0A= -#endif=0A-=0A-=20=20struct=20json_configuration=20conf=20=3D=0A-=20=20=20= =20{json_object_hashtable,=20json_array_array,=20QCnull,=20QCfalse};=0A-=20= =20json_parse_args=20(nargs=20-=201,=20args=20+=201,=20&conf,=20false);=0A= -=0A-=20=20json_t=20*json=20=3D=20lisp_to_json=20(args[0],=20&conf);=0A-=20= =20record_unwind_protect_ptr=20(json_release_object,=20json);=0A+=20=20= /*=20FIXME:=20All=20the=20work=20below=20just=20to=20insert=20a=20string=20= into=20a=20buffer?=20=20*/=0A=20=0A=20=20=20prepare_to_modify_buffer=20= (PT,=20PT,=20NULL);=0A=20=20=20move_gap_both=20(PT,=20PT_BYTE);=0A-=20=20= struct=20json_insert_data=20data;=0A-=20=20data.inserted_bytes=20=3D=20= 0;=0A-=20=20/*=20Could=20have=20used=20json_dumpb,=20but=20that=20became=20= available=20only=20in=0A-=20=20=20=20=20Jansson=202.10,=20whereas=20we=20= want=20to=20support=202.7=20and=20upward.=20=20*/=0A-=20=20int=20status=20= =3D=20json_dump_callback=20(json,=20json_insert_callback,=20&data,=0A-=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20JSON_COMPACT=20|=20JSON_ENCODE_ANY);=0A-=20=20= if=20(status=20=3D=3D=20-1)=0A-=20=20=20=20{=0A-=20=20=20=20=20=20if=20= (CONSP=20(data.error))=0A-=20=20=20=20=20=20=20=20xsignal=20(XCAR=20= (data.error),=20XCDR=20(data.error));=0A-=20=20=20=20=20=20else=0A-=20=20= =20=20=20=20=20=20json_out_of_memory=20();=0A-=20=20=20=20}=0A+=20=20if=20= (GAP_SIZE=20<=20jo.size)=0A+=20=20=20=20make_gap=20(jo.size=20-=20= GAP_SIZE);=0A+=20=20memcpy=20((char=20*)=20BEG_ADDR=20+=20PT_BYTE=20-=20= BEG_BYTE,=20jo.buf,=20jo.size);=0A+=0A+=20=20/*=20No=20need=20to=20keep=20= allocation=20beyond=20this=20point.=20=20*/=0A+=20=20unbind_to=20(count,=20= Qnil);=0A=20=0A=20=20=20ptrdiff_t=20inserted=20=3D=200;=0A-=20=20= ptrdiff_t=20inserted_bytes=20=3D=20data.inserted_bytes;=0A-=20=20if=20= (inserted_bytes=20>=200)=0A+=20=20ptrdiff_t=20inserted_bytes=20=3D=20= jo.size;=0A+=0A+=20=20/*=20If=20required,=20decode=20the=20stuff=20we've=20= read=20into=20the=20gap.=20=20*/=0A+=20=20struct=20coding_system=20= coding;=0A+=20=20/*=20JSON=20strings=20are=20UTF-8=20encoded=20strings.=20= =20*/=0A+=20=20setup_coding_system=20(Qutf_8_unix,=20&coding);=0A+=20=20= coding.dst_multibyte=20=3D=20!NILP=20(BVAR=20(current_buffer,=0A+=09=09=09= =09=20=20=20=20=20=20enable_multibyte_characters));=0A+=20=20if=20= (CODING_MAY_REQUIRE_DECODING=20(&coding))=0A=20=20=20=20=20{=0A-=20=20=20= =20=20=20/*=20If=20required,=20decode=20the=20stuff=20we've=20read=20= into=20the=20gap.=20=20*/=0A-=20=20=20=20=20=20struct=20coding_system=20= coding;=0A-=20=20=20=20=20=20/*=20JSON=20strings=20are=20UTF-8=20encoded=20= strings.=20=20If=20for=20some=20reason=0A-=09=20the=20text=20returned=20= by=20the=20Jansson=20library=20includes=20invalid=0A-=09=20byte=20= sequences,=20they=20will=20be=20represented=20by=20raw=20bytes=20in=20= the=0A-=09=20buffer=20text.=20=20*/=0A-=20=20=20=20=20=20= setup_coding_system=20(Qutf_8_unix,=20&coding);=0A-=20=20=20=20=20=20= coding.dst_multibyte=20=3D=0A-=09!NILP=20(BVAR=20(current_buffer,=20= enable_multibyte_characters));=0A-=20=20=20=20=20=20if=20= (CODING_MAY_REQUIRE_DECODING=20(&coding))=0A-=09{=0A-=20=20=20=20=20=20=20= =20=20=20/*=20Now=20we=20have=20all=20the=20new=20bytes=20at=20the=20= beginning=20of=20the=20gap,=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20= but=20`decode_coding_gap`=20needs=20them=20at=20the=20end=20of=20the=20= gap,=20so=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20we=20need=20to=20= move=20them.=20=20*/=0A-=20=20=20=20=20=20=20=20=20=20memmove=20= (GAP_END_ADDR=20-=20inserted_bytes,=20GPT_ADDR,=20inserted_bytes);=0A-=09= =20=20decode_coding_gap=20(&coding,=20inserted_bytes);=0A-=09=20=20= inserted=20=3D=20coding.produced_char;=0A-=09}=0A-=20=20=20=20=20=20else=0A= -=09{=0A-=20=20=20=20=20=20=20=20=20=20/*=20Make=20the=20inserted=20text=20= part=20of=20the=20buffer,=20as=20unibyte=20text.=20=20*/=0A-=20=20=20=20=20= =20=20=20=20=20eassert=20(NILP=20(BVAR=20(current_buffer,=20= enable_multibyte_characters)));=0A-=20=20=20=20=20=20=20=20=20=20= insert_from_gap_1=20(inserted_bytes,=20inserted_bytes,=20false);=0A-=0A-=09= =20=20/*=20The=20target=20buffer=20is=20unibyte,=20so=20we=20don't=20= need=20to=20decode.=20=20*/=0A-=09=20=20invalidate_buffer_caches=20= (current_buffer,=0A-=09=09=09=09=20=20=20=20PT,=20PT=20+=20= inserted_bytes);=0A-=09=20=20adjust_after_insert=20(PT,=20PT_BYTE,=0A-=09= =09=09=20=20=20=20=20=20=20PT=20+=20inserted_bytes,=0A-=09=09=09=20=20=20= =20=20=20=20PT_BYTE=20+=20inserted_bytes,=0A-=09=09=09=20=20=20=20=20=20=20= inserted_bytes);=0A-=09=20=20inserted=20=3D=20inserted_bytes;=0A-=09}=0A= +=20=20=20=20=20=20/*=20Now=20we=20have=20all=20the=20new=20bytes=20at=20= the=20beginning=20of=20the=20gap,=0A+=09=20but=20`decode_coding_gap`=20= needs=20them=20at=20the=20end=20of=20the=20gap,=20so=0A+=09=20we=20need=20= to=20move=20them.=20=20*/=0A+=20=20=20=20=20=20memmove=20(GAP_END_ADDR=20= -=20inserted_bytes,=20GPT_ADDR,=20inserted_bytes);=0A+=20=20=20=20=20=20= decode_coding_gap=20(&coding,=20inserted_bytes);=0A+=20=20=20=20=20=20= inserted=20=3D=20coding.produced_char;=0A+=20=20=20=20}=0A+=20=20else=0A= +=20=20=20=20{=0A+=20=20=20=20=20=20/*=20Make=20the=20inserted=20text=20= part=20of=20the=20buffer,=20as=20unibyte=20text.=20=20*/=0A+=20=20=20=20=20= =20eassert=20(NILP=20(BVAR=20(current_buffer,=20= enable_multibyte_characters)));=0A+=20=20=20=20=20=20insert_from_gap_1=20= (inserted_bytes,=20inserted_bytes,=20false);=0A+=0A+=20=20=20=20=20=20/*=20= The=20target=20buffer=20is=20unibyte,=20so=20we=20don't=20need=20to=20= decode.=20=20*/=0A+=20=20=20=20=20=20invalidate_buffer_caches=20= (current_buffer,=0A+=09=09=09=09PT,=20PT=20+=20inserted_bytes);=0A+=20=20= =20=20=20=20adjust_after_insert=20(PT,=20PT_BYTE,=0A+=09=09=09=20=20=20= PT=20+=20inserted_bytes,=0A+=09=09=09=20=20=20PT_BYTE=20+=20= inserted_bytes,=0A+=09=09=09=20=20=20inserted_bytes);=0A+=20=20=20=20=20=20= inserted=20=3D=20inserted_bytes;=0A=20=20=20=20=20}=0A=20=0A=20=20=20/*=20= Call=20after-change=20hooks.=20=20*/=0A@@=20-690,7=20+694,26=20@@=20= DEFUN=20("json-insert",=20Fjson_insert,=20Sjson_insert,=201,=20MANY,=0A=20= =20=20=20=20=20=20SET_PT_BOTH=20(PT=20+=20inserted,=20PT_BYTE=20+=20= inserted_bytes);=0A=20=20=20=20=20}=0A=20=0A-=20=20return=20unbind_to=20= (count,=20Qnil);=0A+=20=20return=20Qnil;=0A+}=0A+=0A+=0A+/*=20Note=20= that=20all=20callers=20of=20make_string_from_utf8=20and=20= build_string_from_utf8=0A+=20=20=20below=20either=20pass=20only=20value=20= UTF-8=20strings=20or=20use=20the=20function=20for=0A+=20=20=20formatting=20= error=20messages;=20in=20the=20latter=20case=20correctness=20isn't=0A+=20= =20=20critical.=20=20*/=0A+=0A+/*=20Return=20a=20unibyte=20string=20= containing=20the=20sequence=20of=20UTF-8=20encoding=0A+=20=20=20units=20= of=20the=20UTF-8=20representation=20of=20STRING.=20=20If=20STRING=20does=20= not=0A+=20=20=20represent=20a=20sequence=20of=20Unicode=20scalar=20= values,=20return=20a=20string=20with=0A+=20=20=20unspecified=20contents.=20= =20*/=0A+=0A+static=20Lisp_Object=0A+json_encode=20(Lisp_Object=20= string)=0A+{=0A+=20=20/*=20FIXME:=20Raise=20an=20error=20if=20STRING=20= is=20not=20a=20scalar=20value=0A+=20=20=20=20=20sequence.=20=20*/=0A+=20=20= return=20encode_string_utf_8=20(string,=20Qnil,=20false,=20Qt,=20Qt);=0A=20= }=0A=20=0A=20#define=20JSON_PARSER_INTERNAL_OBJECT_WORKSPACE_SIZE=2064=0A= @@=20-1894,7=20+1917,6=20@@=20syms_of_json=20(void)=0A=20=20=20DEFSYM=20= (QCnull,=20":null");=0A=20=20=20DEFSYM=20(QCfalse,=20":false");=0A=20=0A= -=20=20DEFSYM=20(Qstring_without_embedded_nulls_p,=20= "string-without-embedded-nulls-p");=0A=20=20=20DEFSYM=20(Qjson_value_p,=20= "json-value-p");=0A=20=0A=20=20=20DEFSYM=20(Qjson_error,=20= "json-error");=0A@@=20-1907,7=20+1929,6=20@@=20syms_of_json=20(void)=0A=20= =20=20DEFSYM=20(Qjson_invalid_surrogate_error,=20= "json-invalid-surrogate-error")=0A=20=20=20DEFSYM=20= (Qjson_number_out_of_range,=20"json-number-out-of-range-error")=0A=20=20=20= DEFSYM=20(Qjson_escape_sequence_error,=20"json-escape-sequence-error")=0A= -=20=20DEFSYM=20(Qjson_unavailable,=20"json-unavailable");=0A=20=20=20= define_error=20(Qjson_error,=20"generic=20JSON=20error",=20Qerror);=0A=20= =20=20define_error=20(Qjson_out_of_memory,=0A=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20"not=20enough=20memory=20for=20creating=20JSON=20= object",=20Qjson_error);=0Adiff=20--git=20a/src/lisp.h=20b/src/lisp.h=0A= index=20f066c876619..7c4bd435cd8=20100644=0A---=20a/src/lisp.h=0A+++=20= b/src/lisp.h=0A@@=20-4327,7=20+4327,6=20@@=20verify=20(FLT_RADIX=20=3D=3D=20= 2=20||=20FLT_RADIX=20=3D=3D=2016);=0A=20=0A=20#ifdef=20HAVE_JSON=0A=20/*=20= Defined=20in=20json.c.=20=20*/=0A-extern=20void=20init_json=20(void);=0A=20= extern=20void=20syms_of_json=20(void);=0A=20#endif=0A=20=0Adiff=20--git=20= a/src/print.c=20b/src/print.c=0Aindex=2076c577ec800..0d867b89395=20= 100644=0A---=20a/src/print.c=0A+++=20b/src/print.c=0A@@=20-2859,6=20= +2859,7=20@@=20syms_of_print=20(void)=0A=20A=20value=20of=20nil=20means=20= to=20use=20the=20shortest=20notation=0A=20that=20represents=20the=20= number=20without=20losing=20information.=20=20*/);=0A=20=20=20= Vfloat_output_format=20=3D=20Qnil;=0A+=20=20DEFSYM=20= (Qfloat_output_format,=20"float-output-format");=0A=20=0A=20=20=20= DEFVAR_BOOL=20("print-integers-as-characters",=20= print_integers_as_characters,=0A=20=09=20=20=20=20=20=20=20doc:=20/*=20= Non-nil=20means=20integers=20are=20printed=20using=20characters=20= syntax.=0Adiff=20--git=20a/test/src/json-tests.el=20= b/test/src/json-tests.el=0Aindex=20dffc6291ca1..e5cbe8bff5c=20100644=0A= ---=20a/test/src/json-tests.el=0A+++=20b/test/src/json-tests.el=0A@@=20= -126,11=20+126,38=20@@=20json-serialize/object=0A=20=0A=20(ert-deftest=20= json-serialize/object-with-duplicate-keys=20()=0A=20=20=20(skip-unless=20= (fboundp=20'json-serialize))=0A-=20=20(let=20((table=20(make-hash-table=20= :test=20#'eq)))=0A-=20=20=20=20(puthash=20(copy-sequence=20"abc")=20[1=20= 2=20t]=20table)=0A-=20=20=20=20(puthash=20(copy-sequence=20"abc")=20= :null=20table)=0A-=20=20=20=20(should=20(equal=20(hash-table-count=20= table)=202))=0A-=20=20=20=20(should-error=20(json-serialize=20table)=20= :type=20'wrong-type-argument)))=0A+=0A+=20=20(dolist=20(n=20'(1=205=2020=20= 100))=0A+=20=20=20=20(let=20((symbols=20(mapcar=20(lambda=20(i)=20= (make-symbol=20(format=20"s%d"=20i)))=0A+=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(number-sequence=201=20= n)))=0A+=20=20=20=20=20=20=20=20=20=20(expected=20(concat=20"{"=0A+=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20(mapconcat=20(lambda=20(i)=20(format=20"\"s%d\":%d"=20i=20i))=0A+=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20(number-sequence=201=20n)=20",")=0A+=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20"}")))=0A+=20=20=20=20=20=20;;=20alist=0A+=20=20=20=20=20=20= (should=20(equal=20(json-serialize=0A+=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20(append=0A+=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20(cl-mapcar=20#'cons=0A+=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20symbols=20(number-sequence=201=20n))=0A+=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(cl-mapcar=20#'cons=0A+=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20symbols=20(number-sequence=201001=20(+=201000=20= n)))))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= expected))=0A+=20=20=20=20=20=20;;=20plist=0A+=20=20=20=20=20=20(should=20= (equal=20(json-serialize=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20(append=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20(cl-mapcan=20#'list=0A+=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20symbols=20(number-sequence=201=20n))=0A+=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20(cl-mapcan=20#'list=0A+=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20symbols=20(number-sequence=201001=20(+=201000=20n)))))=0A= +=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= expected))))=0A+=0A+=20=20;;=20We=20don't=20check=20for=20duplicated=20= keys=20in=20hash=20tables.=0A+=20=20;;=20(let=20((table=20= (make-hash-table=20:test=20#'eq)))=0A+=20=20;;=20=20=20(puthash=20= (copy-sequence=20"abc")=20[1=202=20t]=20table)=0A+=20=20;;=20=20=20= (puthash=20(copy-sequence=20"abc")=20:null=20table)=0A+=20=20;;=20=20=20= (should=20(equal=20(hash-table-count=20table)=202))=0A+=20=20;;=20=20=20= (should-error=20(json-serialize=20table)=20:type=20= 'wrong-type-argument))=0A+=20=20)=0A=20=0A=20(ert-deftest=20= json-parse-string/object=20()=0A=20=20=20(skip-unless=20(fboundp=20= 'json-parse-string))=0A@@=20-173,8=20+200,8=20@@=20json-serialize/string=0A= =20=20=20(should=20(equal=20(json-serialize=20= ["\nasd=D1=84=D1=8B=D0=B2\u001f\u007ffgh\t"])=0A=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20"[\"\\nasd=D1=84=D1=8B=D0=B2\\u001F\u007ffgh\\t= \"]"))=0A=20=20=20(should=20(equal=20(json-serialize=20["a\0b"])=20= "[\"a\\u0000b\"]"))=0A-=20=20;;=20FIXME:=20Is=20this=20the=20right=20= behavior?=0A-=20=20(should=20(equal=20(json-serialize=20= ["\u00C4\xC3\x84"])=20"[\"\u00C4\u00C4\"]")))=0A+=20=20(should-error=20= (json-serialize=20["\xC3\x84"]))=0A+=20=20(should-error=20= (json-serialize=20["\u00C4\xC3\x84"])))=0A=20=0A=20(ert-deftest=20= json-serialize/invalid-unicode=20()=0A=20=20=20(skip-unless=20(fboundp=20= 'json-serialize))=0A--=20=0A2.32.0=20(Apple=20Git-132)=0A=0A= --Apple-Mail=_558A8A9C-18B2-4CE8-A0C1-865EBAE3B503--