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: Tue, 26 Mar 2024 16:33:52 +0100 Message-ID: <1BF559D1-DB9F-4FEB-90ED-72E0EFD76424@gmail.com> Mime-Version: 1.0 (Mac OS X Mail 14.0 \(3654.120.0.1.15\)) Content-Type: multipart/mixed; boundary="Apple-Mail=_7330F7C9-D7F2-4246-9F8E-A734AAA47A92" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="36901"; mail-complaints-to="usenet@ciao.gmane.io" To: 70007@debbugs.gnu.org Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Tue Mar 26 16:35:16 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 1rp8pX-0009JW-Jp for geb-bug-gnu-emacs@m.gmane-mx.org; Tue, 26 Mar 2024 16:35:15 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rp8pM-0001Sf-Jz; Tue, 26 Mar 2024 11:35:04 -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 1rp8pK-0001Rw-Uq for bug-gnu-emacs@gnu.org; Tue, 26 Mar 2024 11:35:03 -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 1rp8pK-0006VE-LH for bug-gnu-emacs@gnu.org; Tue, 26 Mar 2024 11:35:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1rp8pJ-00029t-Vu for bug-gnu-emacs@gnu.org; Tue, 26 Mar 2024 11:35: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: Tue, 26 Mar 2024 15:35:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 70007 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch X-Debbugs-Original-To: Emacs Bug Report Original-Received: via spool by submit@debbugs.gnu.org id=B.17114672728219 (code B ref -1); Tue, 26 Mar 2024 15:35:01 +0000 Original-Received: (at submit) by debbugs.gnu.org; 26 Mar 2024 15:34:32 +0000 Original-Received: from localhost ([127.0.0.1]:34488 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rp8op-00028V-Kg for submit@debbugs.gnu.org; Tue, 26 Mar 2024 11:34:31 -0400 Original-Received: from lists.gnu.org ([2001:470:142::17]:35458) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rp8on-00027b-9P for submit@debbugs.gnu.org; Tue, 26 Mar 2024 11:34:30 -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 1rp8oQ-0000yM-Ok for bug-gnu-emacs@gnu.org; Tue, 26 Mar 2024 11:34:08 -0400 Original-Received: from mail-lj1-x22f.google.com ([2a00:1450:4864:20::22f]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1rp8oI-0006Il-4c for bug-gnu-emacs@gnu.org; Tue, 26 Mar 2024 11:34:01 -0400 Original-Received: by mail-lj1-x22f.google.com with SMTP id 38308e7fff4ca-2d6ee81bc87so6440421fa.1 for ; Tue, 26 Mar 2024 08:33:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1711467234; x=1712072034; darn=gnu.org; h=to:date:message-id:subject:mime-version:from:sender:from:to:cc :subject:date:message-id:reply-to; bh=QRkLtcr+EgvnDYUk8l8Y21pG2S4sv24IUJFjQ7aUOys=; b=I3DD68b7I1GN8VSiwVp/4f/htxSRUHYUh6R768JkjAk5h9yhUOeJS3rfY6XC8uwVOb 7ozZaq2LC0+rYu7IcC5HhBkvvDohXv2/GFWeIxQ4p38g6cP0XRrWH0yqMbgqYjii6sCU v35oKjLD/jvPCxMp7SB9iV6o/eh2YTQfZxhLDt6ce/D1g87Q7ut1JoDa6z19+rWmRouR zvI4IrVZoKVqg+UpAMQhtQubys28L36mOoWIrw6D9Hx4Puokkvyr4iC6tIvtdjmYME8G /dt0Bqj3aqs+b/04toYscsSNI+7ePP5GE9VMDA87vSdJ9qJVeFJXy48qDPQBpCWRu9rx f9Rw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711467234; x=1712072034; h=to:date:message-id:subject:mime-version:from:sender :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=QRkLtcr+EgvnDYUk8l8Y21pG2S4sv24IUJFjQ7aUOys=; b=Bn4I3GE5tEYUTcdfzgoChydTfc/aCKRAKsSdLe7Xdnbb36olv8v6GzD+EBvb8bIs5x vjBy5lEMckGeNpcq3rtsmJOvbZNmf91UbZG1bFOFA6xrri7gwizzv3sDe54PAPifBGBV 6L+YMVdTVCEeDHEOiR6/svwsvx+083snGNumruOTQ8m0JBgyQ4EOU10MoSJbYSq0/rc6 XlTrQiZ5Dl+Br1LZP/HsB0LafKpBamjjunSNb+QaRLw9BUSE71lxLlDB0yZVUzEqIPs7 ftJdhByL9XM5YgSl7SNFK95us+2IgvjE5Zz6C7XNXtDFbeBrJyhlkO3pcP2l3K+O4R8W 4QsQ== X-Gm-Message-State: AOJu0Yy060FSRlFbqS6eWmodcq6yaZ3iKizLgnISzAp7UCfupocvyNzy 6npo9rHdedbxlKiFSOtUjcn+ZTSUTc+/uL3DIedtmFXxATRdP7EGO2hXoN6H X-Google-Smtp-Source: AGHT+IEMmzZ3k0v2q3GovR/BVORH7TWPmui9//TuSSYNrk4ZKuC92QC5RmSg54I3jkfo1c7vKVWC2w== X-Received: by 2002:a2e:960b:0:b0:2d4:6786:e8 with SMTP id v11-20020a2e960b000000b002d4678600e8mr6276025ljh.12.1711467233471; Tue, 26 Mar 2024 08:33:53 -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 q126-20020a2e2a84000000b002d4764f825fsm1737761ljq.63.2024.03.26.08.33.52 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Tue, 26 Mar 2024 08:33:53 -0700 (PDT) X-Mailer: Apple Mail (2.3654.120.0.1.15) Received-SPF: pass client-ip=2a00:1450:4864:20::22f; envelope-from=mattias.engdegard@gmail.com; helo=mail-lj1-x22f.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action 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:282092 Archived-At: --Apple-Mail=_7330F7C9-D7F2-4246-9F8E-A734AAA47A92 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii If we replace the lisp-to-JSON encoder with native code, we would not = need the jansson library for it and it would be faster. There is ongoing work on a JSON-to-lisp parser, but the author has made = it clear that he does not have time to write an encoder, so I spent a = morning mashing up the attached patch. It generally produces the same result as the old code, except: - The old code incorrectly accepted strings with non-Unicode characters = (raw bytes). There is no reason to do this; JSON is UTF-8 only. - The old code spent a lot of time ensuring that object keys were = unique. The new code doesn't: it's a garbage-in garbage-out type of = situation. The new code could do with some optimisation but it's already about = twice as fast as the old code, sometimes more. I'd be very happy if someone could test it with packages that use this = interface (json-serialise, json-insert). --Apple-Mail=_7330F7C9-D7F2-4246-9F8E-A734AAA47A92 Content-Disposition: attachment; filename=json-serialise.diff Content-Type: application/octet-stream; x-unix-mode=0644; name="json-serialise.diff" Content-Transfer-Encoding: quoted-printable diff=20--git=20a/src/json.c=20b/src/json.c=0Aindex=20= e849ccaf722..7fbe700e07c=20100644=0A---=20a/src/json.c=0A+++=20= b/src/json.c=0A@@=20-23,6=20+23,7=20@@=20Copyright=20(C)=202017-2024=20= Free=20Software=20Foundation,=20Inc.=0A=20#include=20=0A=20= #include=20=0A=20#include=20=0A+#include=20=0A= =20=0A=20#include=20=0A=20=0A@@=20-231,12=20+232,6=20@@=20= json_encode=20(Lisp_Object=20string)=0A=20=20=20return=20= encode_string_utf_8=20(string,=20Qnil,=20false,=20Qt,=20Qt);=0A=20}=0A=20= =0A-static=20AVOID=0A-json_out_of_memory=20(void)=0A-{=0A-=20=20xsignal0=20= (Qjson_out_of_memory);=0A-}=0A-=0A=20/*=20Signal=20a=20Lisp=20error=20= corresponding=20to=20the=20JSON=20ERROR.=20=20*/=0A=20=0A=20static=20= AVOID=0A@@=20-289,26=20+284,6=20@@=20check_string_without_embedded_nulls=20= (Lisp_Object=20object)=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= Qstring_without_embedded_nulls_p,=20object);=0A=20}=0A=20=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-327,179=20+302,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-585,6=20+387,332=20@@=20= DEFUN=20("json--available-p",=20Fjson__available_p,=20= Sjson__available_p,=200,=200,=20NULL,=0A=20=20=20return=20= json_available_p=20()=20?=20Qt=20:=20Qnil;=0A=20}=0A=20=0A+/*=20JSON=20= encoding=20context=20*/=0A+typedef=20struct=20{=0A+=20=20char=20*buf;=0A= +=20=20ptrdiff_t=20size;=09=09/*=20number=20of=20bytes=20in=20buf=20*/=0A= +=20=20ptrdiff_t=20capacity;=09=09/*=20allocated=20size=20of=20buf=20*/=0A= +=20=20ptrdiff_t=20chars_delta;=20=20=20=20=20=20=20=20/*=20size=20-=20= {number=20of=20Unicode=20chars=20in=20buf}=20*/=0A+=0A+=20=20int=20= maxdepth;=0A+=20=20struct=20json_configuration=20conf;=0A+}=20= json_out_t;=0A+=0A+static=20NO_INLINE=20void=0A+json_out_grow=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= (need,=20512);=0A+=20=20while=20(new_size=20<=20need)=0A+=20=20=20=20= new_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+=0A+static=20= void=0A+cleanup_json_out=20(void=20*arg)=0A+{=0A+=20=20json_out_t=20*jo=20= =3D=20arg;=0A+=20=20xfree=20(jo->buf);=0A+=20=20jo->buf=20=3D=20NULL;=0A= +}=0A+=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,=20= ptrdiff_t=20bytes)=0A+{=0A+=20=20if=20(bytes=20>=20jo->capacity=20-=20= jo->size)=0A+=20=20=20=20json_out_grow=20(jo,=20bytes);=0A+}=0A+=0A= +#define=20JSON_OUT_STR(jo,=20str)=20(json_out_str=20(jo,=20str,=20= sizeof=20(str)=20-=201))=0A+=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=20char=20*str,=20size_t=20bytes)=0A+{=0A+=20=20= json_make_room=20(jo,=20bytes);=0A+=20=20memcpy=20(jo->buf=20+=20= jo->size,=20str,=20bytes);=0A+=20=20jo->size=20+=3D=20bytes;=0A+}=0A+=0A= +static=20void=0A+json_out_byte=20(json_out_t=20*jo,=20unsigned=20char=20= c)=0A+{=0A+=20=20json_make_room=20(jo,=201);=0A+=20=20= jo->buf[jo->size++]=20=3D=20c;=0A+}=0A+=0A+static=20void=0A= +json_out_fixnum=20(json_out_t=20*jo,=20EMACS_INT=20x)=0A+{=0A+=20=20= char=20buf[INT_BUFSIZE_BOUND=20(EMACS_INT)];=0A+=20=20char=20*end=20=3D=20= buf=20+=20sizeof=20buf;=0A+=20=20char=20*p=20=3D=20fixnum_to_string=20= (x,=20buf,=20end);=0A+=20=20json_out_str=20(jo,=20p,=20end=20-=20p);=0A= +}=0A+=0A+static=20AVOID=0A+string_not_unicode=20(Lisp_Object=20obj)=0A= +{=0A+=20=20/*=20FIXME:=20for=20test=20compatibility,=20not=20a=20very=20= descriptive=20error=20*/=0A+=20=20wrong_type_argument=20(Qjson_value_p,=20= obj);=0A+}=0A+=0A+static=20unsigned=20char=20json_plain_char[256]=20=3D=20= {=0A+=20=20/*=2032=20chars/line:=201=20for=20printable=20ASCII=20+=20DEL=20= except=20"=20and=20\,=200=20elsewhere=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= 00-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+};=0A+=0A+static=20void=0A+json_out_string=20(json_out_t=20= *jo,=20Lisp_Object=20str,=20int=20skip)=0A+{=0A+=20=20/*=20FIXME:=20this=20= code=20is=20slow,=20make=20faster!=20*/=0A+=0A+=20=20static=20const=20= char=20hexchar[16]=20=3D=20"0123456789ABCDEF";=0A+=20=20ptrdiff_t=20len=20= =3D=20SBYTES=20(str);=0A+=20=20json_make_room=20(jo,=20len=20+=202);=0A+=20= =20json_out_byte=20(jo,=20'"');=0A+=20=20unsigned=20char=20*p=20=3D=20= SDATA=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+{=0A+=20=20++jo->maxdepth;=0A+}=0A= +=0A+static=20void=20json_out_something=20(json_out_t=20*jo,=20= Lisp_Object=20obj);=0A+=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=20json_out_byte=20(jo,=20'{');=0A+=20=20bool=20is_alist=20=3D= =20CONSP=20(XCAR=20(obj));=0A+=20=20bool=20first=20=3D=20true;=0A+=20=20= Lisp_Object=20tail=20=3D=20obj;=0A+=20=20FOR_EACH_TAIL=20(tail)=0A+=20=20= =20=20{=0A+=20=20=20=20=20=20if=20(!first)=0A+=09json_out_byte=20(jo,=20= ',');=0A+=20=20=20=20=20=20first=20=3D=20false;=0A+=20=20=20=20=20=20= Lisp_Object=20key_sym;=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_sym=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_sym=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=20/*=20= FIXME:=20do=20we=20care=20about=20dup=20keys=20here?=20(probably=20not)=20= */=0A+=20=20=20=20=20=20CHECK_SYMBOL=20(key_sym);=0A+=20=20=20=20=20=20= Lisp_Object=20key=20=3D=20SYMBOL_NAME=20(key_sym);=0A+=20=20=20=20=20=20= const=20char=20*keystr=20=3D=20SSDATA=20(key);=0A+=20=20=20=20=20=20/*=20= Skip=20leading=20':'=20in=20plist=20keys.=20=20*/=0A+=20=20=20=20=20=20= int=20skip=20=3D=20!is_alist=20&&=20keystr[0]=20=3D=3D=20':'=20&&=20= keystr[1]=20?=201=20:=200;=0A+=20=20=20=20=20=20json_out_string=20(jo,=20= key,=20skip);=0A+=20=20=20=20=20=20json_out_byte=20(jo,=20':');=0A+=20=20= =20=20=20=20json_out_something=20(jo,=20value);=0A+=20=20=20=20}=0A+=20=20= CHECK_LIST_END=20(tail,=20obj);=0A+=20=20json_out_byte=20(jo,=20'}');=0A= +=20=20json_out_unnest=20(jo);=0A+}=0A+=0A+static=20void=0A= +json_out_object_hash=20(json_out_t=20*jo,=20Lisp_Object=20obj)=0A+{=0A+=20= =20json_out_nest=20(jo);=0A+=20=20json_out_byte=20(jo,=20'{');=0A+=20=20= struct=20Lisp_Hash_Table=20*h=20=3D=20XHASH_TABLE=20(obj);=0A+=20=20bool=20= first=20=3D=20true;=0A+=20=20DOHASH=20(h,=20k,=20v)=0A+=20=20=20=20{=0A+=20= =20=20=20=20=20if=20(!first)=0A+=09json_out_byte=20(jo,=20',');=0A+=20=20= =20=20=20=20first=20=3D=20false;=0A+=20=20=20=20=20=20/*=20FIXME:=20do=20= we=20care=20about=20dup=20keys=20here?=20(probably=20not)=20*/=0A+=20=20=20= =20=20=20CHECK_STRING=20(k);=0A+=20=20=20=20=20=20json_out_string=20(jo,=20= k,=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}=0A+=20=20= json_out_byte=20(jo,=20'}');=0A+=20=20json_out_unnest=20(jo);=0A+=0A+}=0A= +=0A+static=20void=0A+json_out_array=20(json_out_t=20*jo,=20Lisp_Object=20= obj)=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+=0A+static=20void=0A+json_out_float=20(json_out_t=20*jo,=20= Lisp_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=20number",=20f);=0A+=20=20json_make_room=20(jo,=20= FLOAT_TO_STRING_BUFSIZE);=0A+=20=20int=20n=20=3D=20float_to_string=20= (jo->buf=20+=20jo->size,=20x);=0A+=20=20jo->size=20+=3D=20n;=0A+}=0A+=0A= +static=20void=0A+json_out_bignum=20(json_out_t=20*jo,=20Lisp_Object=20= x)=0A+{=0A+=20=20int=20base=20=3D=2010;=0A+=20=20ptrdiff_t=20size=20=3D=20= bignum_bufsize=20(x,=20base);=0A+=20=20json_make_room=20(jo,=20size);=0A= +=20=20int=20n=20=3D=20bignum_to_c_string=20(jo->buf=20+=20jo->size,=20= size,=20x,=20base);=0A+=20=20jo->size=20+=3D=20n;=0A+}=0A+=0A+static=20= void=0A+json_out_something=20(json_out_t=20*jo,=20Lisp_Object=20obj)=0A= +{=0A+=20=20if=20(EQ=20(obj,=20jo->conf.null_object))=0A+=20=20=20=20= JSON_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_string_result=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+/*=20FIXME:=20update=20doc=20string=20= */=0A=20DEFUN=20("json-serialize",=20Fjson_serialize,=20Sjson_serialize,=20= 1,=20MANY,=0A=20=20=20=20=20=20=20=20NULL,=0A=20=20=20=20=20=20=20=20= doc:=20/*=20Return=20the=20JSON=20representation=20of=20OBJECT=20as=20a=20= string.=0A@@=20-614,95=20+742,17=20@@=20DEFUN=20("json-serialize",=20= Fjson_serialize,=20Sjson_serialize,=201,=20MANY,=0A=20usage:=20= (json-serialize=20OBJECT=20&rest=20ARGS)=20=20*/)=0A=20=20=20=20=20=20= (ptrdiff_t=20nargs,=20Lisp_Object=20*args)=0A=20{=0A-=20=20specpdl_ref=20= count=20=3D=20SPECPDL_INDEX=20();=0A+=20=20json_out_t=20jo=20=3D=20{=0A+=20= =20=20=20.maxdepth=20=3D=2025,=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=20-=201,=20args=20+=201,=20= &jo.conf,=20false);=0A+=20=20Lisp_Object=20object=20=3D=20args[0];=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=20= json_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=20= record_unwind_protect_ptr=20(json_release_object,=20json);=0A-=0A-=20=20= char=20*string=20=3D=20json_dumps=20(json,=20JSON_COMPACT=20|=20= JSON_ENCODE_ANY);=0A-=20=20if=20(string=20=3D=3D=20NULL)=0A-=20=20=20=20= json_out_of_memory=20();=0A-=20=20record_unwind_protect_ptr=20= (json_free,=20string);=0A-=0A-=20=20return=20unbind_to=20(count,=20= build_string_from_utf8=20(string));=0A-}=0A-=0A-struct=20= json_buffer_and_size=0A-{=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-};=0A-=0A-static=20Lisp_Object=0A-json_insert=20(void=20= *data)=0A-{=0A-=20=20struct=20json_buffer_and_size=20*buffer_and_size=20= =3D=20data;=0A-=20=20ptrdiff_t=20len=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-=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-=0A= -=20=20/*=20Copy=20this=20chunk=20of=20data=20into=20the=20gap.=20=20*/=0A= -=20=20memcpy=20((char=20*)=20BEG_ADDR=20+=20PT_BYTE=20-=20BEG_BYTE=20+=20= inserted_bytes,=0A-=09=20=20buffer_and_size->buffer,=20len);=0A-=20=20= buffer_and_size->inserted_bytes=20+=3D=20len;=0A-=20=20return=20Qnil;=0A= -}=0A-=0A-static=20Lisp_Object=0A-json_handle_nonlocal_exit=20(enum=20= nonlocal_exit=20type,=20Lisp_Object=20data)=0A-{=0A-=20=20switch=20= (type)=0A-=20=20=20=20{=0A-=20=20=20=20case=20NONLOCAL_EXIT_SIGNAL:=0A-=20= =20=20=20=20=20return=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=20= default:=0A-=20=20=20=20=20=20eassume=20(false);=0A-=20=20=20=20}=0A-}=0A= -=0A-struct=20json_insert_data=0A-{=0A-=20=20/*=20This=20tracks=20how=20= many=20bytes=20were=20inserted=20by=20the=20callback=20since=0A-=20=20=20= =20=20json_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-=0A-/*=20Callback=20for=20json_dump_callback=20that=20= inserts=20a=20JSON=20representation=0A-=20=20=20as=20a=20unibyte=20= string=20into=20the=20gap.=20=20DATA=20must=20point=20to=20a=20structure=0A= -=20=20=20of=20type=20json_insert_data.=20=20This=20function=20may=20not=20= exit=20nonlocally.=0A-=20=20=20It=20catches=20all=20nonlocal=20exits=20= and=20stores=20them=20in=20data->error=20for=0A-=20=20=20reraising.=20=20= */=0A-=0A-static=20int=0A-json_insert_callback=20(const=20char=20= *buffer,=20size_t=20size,=20void=20*data)=0A-{=0A-=20=20struct=20= json_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=20specpdl_ref=20count=20=3D=20SPECPDL_INDEX=20();=0A+=20= =20record_unwind_protect_ptr=20(cleanup_json_out,=20&jo);=0A+=20=20= json_out_something=20(&jo,=20object);=0A+=20=20return=20unbind_to=20= (count,=20json_out_string_result=20(&jo));=0A=20}=0A=20=0A=20DEFUN=20= ("json-insert",=20Fjson_insert,=20Sjson_insert,=201,=20MANY,=0A@@=20= -714,72=20+764,61=20@@=20DEFUN=20("json-insert",=20Fjson_insert,=20= Sjson_insert,=201,=20MANY,=0A=20usage:=20(json-insert=20OBJECT=20&rest=20= ARGS)=20=20*/)=0A=20=20=20=20=20=20(ptrdiff_t=20nargs,=20Lisp_Object=20= *args)=0A=20{=0A-=20=20specpdl_ref=20count=20=3D=20SPECPDL_INDEX=20();=0A= +=20=20json_out_t=20jo=20=3D=20{=0A+=20=20=20=20.maxdepth=20=3D=2025,=0A= +=20=20=20=20.conf=20=3D=20{json_object_hashtable,=20json_array_array,=20= QCnull,=20QCfalse}=0A+=20=20};=0A+=20=20json_parse_args=20(nargs=20-=20= 1,=20args=20+=201,=20&jo.conf,=20false);=0A+=20=20Lisp_Object=20object=20= =3D=20args[0];=0A=20=0A-#ifdef=20WINDOWSNT=0A-=20=20= ensure_json_available=20();=0A-#endif=0A-=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+=20=20specpdl_ref=20= count=20=3D=20SPECPDL_INDEX=20();=0A+=20=20record_unwind_protect_ptr=20= (cleanup_json_out,=20&jo);=0A+=20=20json_out_something=20(&jo,=20= object);=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+=20=20/*=20FIXME:=20Do=20we=20really=20need=20to=20do=20all=20= this=20work=20below=20to=20insert=20a=20string?=0A+=20=20=20=20=20Is=20= there=20no=20function=20already=20written?=20=20I=20must=20be=20missing=20= something.=20=20*/=0A=20=0A=20=20=20prepare_to_modify_buffer=20(PT,=20= PT,=20NULL);=0A=20=20=20move_gap_both=20(PT,=20PT_BYTE);=0A-=20=20struct=20= json_insert_data=20data;=0A-=20=20data.inserted_bytes=20=3D=200;=0A-=20=20= /*=20Could=20have=20used=20json_dumpb,=20but=20that=20became=20available=20= only=20in=0A-=20=20=20=20=20Jansson=202.10,=20whereas=20we=20want=20to=20= support=202.7=20and=20upward.=20=20*/=0A-=20=20int=20status=20=3D=20= json_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=20if=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),=20= XCDR=20(data.error));=0A-=20=20=20=20=20=20else=0A-=20=20=20=20=20=20=20=20= json_out_of_memory=20();=0A-=20=20=20=20}=0A+=20=20if=20(GAP_SIZE=20<=20= jo.size)=0A+=20=20=20=20make_gap=20(jo.size=20-=20GAP_SIZE);=0A+=20=20= memcpy=20((char=20*)=20BEG_ADDR=20+=20PT_BYTE=20-=20BEG_BYTE,=20jo.buf,=20= jo.size);=0A+=0A+=20=20/*=20No=20need=20to=20keep=20allocation=20beyond=20= this=20point.=20=20*/=0A+=20=20unbind_to=20(count,=20Qnil);=0A=20=0A=20=20= =20ptrdiff_t=20inserted=20=3D=200;=0A-=20=20ptrdiff_t=20inserted_bytes=20= =3D=20data.inserted_bytes;=0A-=20=20if=20(inserted_bytes=20>=200)=0A+=20=20= ptrdiff_t=20inserted_bytes=20=3D=20jo.size;=0A+=0A+=20=20/*=20If=20= required,=20decode=20the=20stuff=20we've=20read=20into=20the=20gap.=20=20= */=0A+=20=20struct=20coding_system=20coding;=0A+=20=20/*=20JSON=20= strings=20are=20UTF-8=20encoded=20strings.=20=20*/=0A+=20=20= setup_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-791,7=20+830,7=20@@=20DEFUN=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=20}=0A=20=0A=20/*=20Convert=20= a=20JSON=20object=20to=20a=20Lisp=20object.=20=20*/=0Adiff=20--git=20= a/test/src/json-tests.el=20b/test/src/json-tests.el=0Aindex=20= dffc6291ca1..c908c031a82=20100644=0A---=20a/test/src/json-tests.el=0A+++=20= b/test/src/json-tests.el=0A@@=20-90,8=20+90,11=20@@=20= json-serialize/object=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20"{\"abc\":[1,2,true],\"def\":null}"))=0A=20=20=20(should=20(equal=20= (json-serialize=20nil)=20"{}"))=0A=20=20=20(should=20(equal=20= (json-serialize=20'((abc)))=20"{\"abc\":{}}"))=0A-=20=20(should=20(equal=20= (json-serialize=20'((a=20.=201)=20(b=20.=202)=20(a=20.=203)))=0A+=20=20= (should=20(equal=20(json-serialize=20'((a=20.=201)=20(b=20.=202)))=0A=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20"{\"a\":1,\"b\":2}"))=0A= +=20=20;;=20FIXME:=20we=20assume=20hash=20table=20keys=20to=20be=20= unique=0A+=20=20;;=20(should=20(equal=20(json-serialize=20'((a=20.=201)=20= (b=20.=202)=20(a=20.=203)))=0A+=20=20;;=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20"{\"a\":1,\"b\":2}"))=0A=20=20=20(should-error=20= (json-serialize=20'(abc))=20:type=20'wrong-type-argument)=0A=20=20=20= (should-error=20(json-serialize=20'((a=201)))=20:type=20= 'wrong-type-argument)=0A=20=20=20(should-error=20(json-serialize=20'((1=20= .=202)))=20:type=20'wrong-type-argument)=0A@@=20-124,13=20+127,14=20@@=20= json-serialize/object=0A=20\"detect-plist\":{\"bla\":\"ble\"}\=0A=20= }")))=0A=20=0A-(ert-deftest=20json-serialize/object-with-duplicate-keys=20= ()=0A-=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=202=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=20table)=202))=0A-=20=20=20=20(should-error=20= (json-serialize=20table)=20:type=20'wrong-type-argument)))=0A+;;=20= FIXME:=20we=20don't=20check=20for=20duplicate=20keys=0A+;;=20= (ert-deftest=20json-serialize/object-with-duplicate-keys=20()=0A+;;=20=20= =20(skip-unless=20(fboundp=20'json-serialize))=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=0A=20(ert-deftest=20= json-parse-string/object=20()=0A=20=20=20(skip-unless=20(fboundp=20= 'json-parse-string))=0A@@=20-174,7=20+178,10=20@@=20= json-serialize/string=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=20;;=20FIXME:=20Is=20this=20the=20right=20behavior?=0A-=20=20(should=20= (equal=20(json-serialize=20["\u00C4\xC3\x84"])=20"[\"\u00C4\u00C4\"]")))=0A= +=20=20;;=20FIXME:=20(no=20it's=20not)=0A+=20=20;;=20(should=20(equal=20= (json-serialize=20["\u00C4\xC3\x84"])=20"[\"\u00C4\u00C4\"]"))=0A+=20=20= (should-error=20(json-serialize=20["\u00C4\xC3\x84"]))=0A+=20=20)=0A=20=0A= =20(ert-deftest=20json-serialize/invalid-unicode=20()=0A=20=20=20= (skip-unless=20(fboundp=20'json-serialize))=0A= --Apple-Mail=_7330F7C9-D7F2-4246-9F8E-A734AAA47A92--