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: Wed, 27 Mar 2024 16:49:53 +0100 Message-ID: <4589243D-C11A-45C1-AF3E-6F4A5BADEB54@gmail.com> References: <1BF559D1-DB9F-4FEB-90ED-72E0EFD76424@gmail.com> <86wmpphrg7.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=_E4201434-B8C0-4597-91DC-3456766FB664" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="1991"; mail-complaints-to="usenet@ciao.gmane.io" Cc: 70007@debbugs.gnu.org To: Eli Zaretskii Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Wed Mar 27 16:51:21 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 1rpVYe-0000Mi-8o for geb-bug-gnu-emacs@m.gmane-mx.org; Wed, 27 Mar 2024 16:51:20 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rpVYP-0007F6-1U; Wed, 27 Mar 2024 11:51:06 -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 1rpVYM-0007El-8m for bug-gnu-emacs@gnu.org; Wed, 27 Mar 2024 11:51:02 -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 1rpVYM-0003to-0D for bug-gnu-emacs@gnu.org; Wed, 27 Mar 2024 11:51:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1rpVYM-0004du-3e for bug-gnu-emacs@gnu.org; Wed, 27 Mar 2024 11:51: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: Wed, 27 Mar 2024 15:51: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.171155460517574 (code B ref 70007); Wed, 27 Mar 2024 15:51:02 +0000 Original-Received: (at 70007) by debbugs.gnu.org; 27 Mar 2024 15:50:05 +0000 Original-Received: from localhost ([127.0.0.1]:38214 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rpVXQ-0004ZN-Hv for submit@debbugs.gnu.org; Wed, 27 Mar 2024 11:50:04 -0400 Original-Received: from mail-lf1-x132.google.com ([2a00:1450:4864:20::132]:60542) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rpVXN-0004YB-5Y for 70007@debbugs.gnu.org; Wed, 27 Mar 2024 11:50:02 -0400 Original-Received: by mail-lf1-x132.google.com with SMTP id 2adb3069b0e04-513e89d0816so8514488e87.0 for <70007@debbugs.gnu.org>; Wed, 27 Mar 2024 08:50:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1711554595; x=1712159395; 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=lP9/Mtc5lDk9Bh9CDr+RvAHZqYeHtr66KCBQMaEwfdM=; b=JDOea/pZWnjoS9ZTnWw+jN9iCSWr9YYgwPeW5QT4vaDSvsGCZ0pbPFQ9DfIx/AXhmA lrusCVVfpSPcCyZmCRWJdClLQCx1li4706423bUcMkA2Z/kgtEf2kuiO5cZc1tANZFrG rgu1xIfA4QEnZRPVGMoQlsitG2cMDUjiZYe4RQl6eHNePJ9FHOULPX0KkW9mqehxBswz Rf948CBH37R4jvItjhVdy45ZiyHentyspTGhgUuMpk6E5Q4Sx0ahxp5SkvKOD17ZdDzp IRAgqiXppwPvyFgLx1WQ4UO6iPXKtxPpUQrc3gMSFeobslYAptjXulxlcY6yqoQJLPuD r5Dg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711554595; x=1712159395; 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=lP9/Mtc5lDk9Bh9CDr+RvAHZqYeHtr66KCBQMaEwfdM=; b=UVlqwzzIyFB7zc/MrqvA53JKTis0AU0ujfIC+JyIXG6QpHJxXt2sz0TKoN57/i7xZ+ xV2GpOc4r+laQ132Z5r9GtowOtOnwVCLL/qDeVGMeKF6Eh2gvLoM6+VW/Bgv1ENBLDkQ EJHSH8jwrxdZtfFxtR1qyI6BcDdHbBvvG06hPFS6VnAHdS/z0OUPLLHu1yodk3ucEbFX hSub4/fCkUxBjEf/2f8MDo74hYdEr7RTivLLFJPzK5P5dbGmo0VlcBiJJYIcdlZXufHC htjakukPz1SYjXtSQHXuC2c2xfy7jXCVlVqo5PjvhFZavkGm31OHFDjE+UOKB1OR6TWe tzXg== X-Gm-Message-State: AOJu0YzKdxfJJ500QFTT3wDuA2QVmVvr0g+tZGEAPm4phndtdcrZUZcY UxpnXahT4APtOhXBe+LG/wVXZt0IkEdLacBz321qz7qa64+vxgf1 X-Google-Smtp-Source: AGHT+IH5vWNAMGrGnCnD9dAM4+opD+LVibrexN+tiKpD3l7XABPd7yMxUfmcJHN/pro0UCESqDwkbQ== X-Received: by 2002:a05:6512:1cd:b0:514:b4ca:f478 with SMTP id f13-20020a05651201cd00b00514b4caf478mr1255633lfp.39.1711554594583; Wed, 27 Mar 2024 08:49:54 -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 c11-20020a056512238b00b00515ae2cbefesm1046802lfv.247.2024.03.27.08.49.53 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 27 Mar 2024 08:49:53 -0700 (PDT) In-Reply-To: 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:282144 Archived-At: --Apple-Mail=_E4201434-B8C0-4597-91DC-3456766FB664 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii Here is an updated patch. It now ignores duplicated keys in objects = represented by alists and plists, just like the old encoder. (I didn't = include this in the first draft out of fear it would be slow and = complicated, but it turned out just to be complicated.) The performance is still acceptable, which means at least 2x the speed = of the Jansson-based encoder. --Apple-Mail=_E4201434-B8C0-4597-91DC-3456766FB664 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..b853dec3b21=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,124=20+387,498=20= @@=20DEFUN=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-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+/*=20= JSON=20encoding=20context=20*/=0A+typedef=20struct=20{=0A+=20=20char=20= *buf;=0A+=20=20ptrdiff_t=20size;=09=09/*=20number=20of=20bytes=20in=20= buf=20*/=0A+=20=20ptrdiff_t=20capacity;=09=09/*=20allocated=20size=20of=20= buf=20*/=0A+=20=20ptrdiff_t=20chars_delta;=20=20=20=20=20=20=20=20/*=20= size=20-=20{number=20of=20Unicode=20chars=20in=20buf}=20*/=0A+=0A+=20=20= int=20maxdepth;=0A+=20=20struct=20symset_tbl=20*ss_table;=09/*=20table=20= used=20by=20containing=20object=20*/=0A+=20=20struct=20= json_configuration=20conf;=0A+}=20json_out_t;=0A+=0A+/*=20Set=20of=20= symbols=20*/=0A+typedef=20struct=20{=0A+=20=20int=20count;=09=09=09/*=20= symbols=20in=20table=20*/=0A+=20=20int=20bits;=09=09=09/*=20log2(table=20= size)=20*/=0A+=20=20struct=20symset_tbl=20*table;=09/*=20heap-allocated=20= table=20*/=0A+}=20symset_t;=0A+=0A+struct=20symset_tbl=0A+{=0A+=20=20/*=20= Table=20used=20by=20the=20containing=20object=20if=20any,=20so=20that=20= we=20can=20easily=0A+=20=20=20=20=20all=20tables=20if=20an=20error=20= occurs.=20=20*/=0A+=20=20struct=20symset_tbl=20*up;=0A+=20=20/*=20Table=20= of=20symbols=20(2**bits=20entries),=20Qunbound=20where=20unused.=20=20*/=0A= +=20=20Lisp_Object=20entries[];=0A+};=0A=20=0A-OBJECT=20must=20be=20t,=20= a=20number,=20string,=20vector,=20hashtable,=20alist,=20plist,=0A-or=20= the=20Lisp=20equivalents=20to=20the=20JSON=20null=20and=20false=20= values,=20and=20its=0A-elements=20must=20recursively=20consist=20of=20= the=20same=20kinds=20of=20values.=20=20t=20will=0A-be=20converted=20to=20= the=20JSON=20true=20value.=20=20Vectors=20will=20be=20converted=20to=0A= -JSON=20arrays,=20whereas=20hashtables,=20alists=20and=20plists=20are=20= converted=20to=0A-JSON=20objects.=20=20Hashtable=20keys=20must=20be=20= strings=20without=20embedded=20null=0A-characters=20and=20must=20be=20= unique=20within=20each=20object.=20=20Alist=20and=20plist=0A-keys=20must=20= be=20symbols;=20if=20a=20key=20is=20duplicate,=20the=20first=20instance=20= is=0A-used.=0A+static=20struct=20symset_tbl=20*=0A+alloc_symset_table=20= (int=20bits)=0A+{=0A+=20=20struct=20symset_tbl=20*st=20=3D=20xmalloc=20= (sizeof=20*st=20+=20(sizeof=20*st->entries=20<<=20bits));=0A+=20=20int=20= size=20=3D=201=20<<=20bits;=0A+=20=20for=20(ptrdiff_t=20i=20=3D=200;=20i=20= <=20size;=20i++)=0A+=20=20=20=20st->entries[i]=20=3D=20Qunbound;=0A+=20=20= return=20st;=0A+}=0A=20=0A-The=20Lisp=20equivalents=20to=20the=20JSON=20= null=20and=20false=20values=20are=0A-configurable=20in=20the=20arguments=20= ARGS,=20a=20list=20of=20keyword/argument=20pairs:=0A+/*=20Create=20a=20= new=20symset=20to=20use=20for=20a=20new=20object.=20=20*/=0A+static=20= symset_t=0A+push_symset=20(json_out_t=20*jo)=0A+{=0A+=20=20int=20bits=20= =3D=204;=0A+=20=20struct=20symset_tbl=20*tbl=20=3D=20alloc_symset_table=20= (bits);=0A+=20=20tbl->up=20=3D=20jo->ss_table;=0A+=20=20jo->ss_table=20=3D= =20tbl;=0A+=20=20return=20(symset_t){=20.count=20=3D=200,=20.bits=20=3D=20= 4,=20.table=20=3D=20tbl=20};=0A+}=0A=20=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+/*=20= Destroy=20the=20current=20symset.=20=20*/=0A+static=20void=0A+pop_symset=20= (json_out_t=20*jo,=20symset_t=20*ss)=0A+{=0A+=20=20jo->ss_table=20=3D=20= ss->table->up;=0A+=20=20xfree=20(ss->table);=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+/*=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+{=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+}=0A=20=0A-In=20you=20specify=20the=20same=20= value=20for=20`:null-object'=20and=20`:false-object',=0A-a=20potentially=20= ambiguous=20situation,=20the=20JSON=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,=20= Lisp_Object=20*args)=0A+static=20inline=20uint32_t=0A+symset_hash=20= (Lisp_Object=20sym,=20int=20bits)=0A=20{=0A-=20=20specpdl_ref=20count=20= =3D=20SPECPDL_INDEX=20();=0A+=20=20return=20knuth_hash=20= (reduce_emacs_uint_to_hash_hash=20(XHASH=20(sym)),=20bits);=0A+}=0A=20=0A= -#ifdef=20WINDOWSNT=0A-=20=20ensure_json_available=20();=0A-#endif=0A+/*=20= Enlarge=20the=20table=20used=20by=20a=20symset.=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=20int=20oldsize=20=3D=201=20<<=20= oldbits;=0A+=20=20int=20bits=20=3D=20oldbits=20+=201;=0A+=20=20ss->bits=20= =3D=20bits;=0A+=20=20ss->table=20=3D=20alloc_symset_table=20(bits);=0A+=20= =20ss->table->up=20=3D=20old_table->up;=0A+=20=20/*=20Move=20all=20= entries=20from=20the=20old=20table=20to=20the=20new=20one.=20=20*/=0A+=20= =20int=20mask=20=3D=20(1=20<<=20bits)=20-=201;=0A+=20=20struct=20= symset_tbl=20*tbl=20=3D=20ss->table;=0A+=20=20for=20(ptrdiff_t=20i=20=3D=20= 0;=20i=20<=20oldsize;=20i++)=0A+=20=20=20=20{=0A+=20=20=20=20=20=20= Lisp_Object=20sym=20=3D=20old_table->entries[i];=0A+=20=20=20=20=20=20if=20= (!BASE_EQ=20(sym,=20Qunbound))=0A+=09{=0A+=09=20=20ptrdiff_t=20j=20=3D=20= symset_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-=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+/*=20If=20sym=20is=20= in=20ss,=20return=20false;=20otherwise=20add=20it=20and=20return=20true.=0A= +=20=20=20Comparison=20is=20done=20by=20strict=20identity.=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=20sure=20we=20don't=20= fill=20more=20than=20half=20of=20the=20table.=20=20*/=0A+=20=20if=20= (ss->count=20*=202=20>=3D=20(1=20<<=20ss->bits))=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=20= ss->table;=0A+=20=20=20=20}=0A+=0A+=20=20struct=20symset_tbl=20*tbl=20=3D=20= ss->table;=0A+=20=20int=20mask=20=3D=20(1=20<<=20ss->bits)=20-=201;=0A+=20= =20for=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-=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+static=20= NO_INLINE=20void=0A+json_out_grow=20(json_out_t=20*jo,=20ptrdiff_t=20= bytes)=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=20new_size=20<<=3D=201;=0A+=20=20= jo->buf=20=3D=20xrealloc=20(jo->buf,=20new_size);=0A+=20=20jo->capacity=20= =3D=20new_size;=0A+}=0A=20=0A-=20=20char=20*string=20=3D=20json_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= +cleanup_json_out=20(void=20*arg)=0A+{=0A+=20=20json_out_t=20*jo=20=3D=20= arg;=0A+=20=20xfree=20(jo->buf);=0A+=20=20cleanup_symset_tables=20= (jo->ss_table);=0A+}=0A=20=0A-=20=20return=20unbind_to=20(count,=20= build_string_from_utf8=20(string));=0A+/*=20Make=20room=20for=20`bytes`=20= more=20bytes=20in=20buffer.=20=20*/=0A+static=20void=0A+json_make_room=20= (json_out_t=20*jo,=20ptrdiff_t=20bytes)=0A+{=0A+=20=20if=20(bytes=20>=20= jo->capacity=20-=20jo->size)=0A+=20=20=20=20json_out_grow=20(jo,=20= bytes);=0A=20}=0A=20=0A-struct=20json_buffer_and_size=0A+#define=20= JSON_OUT_STR(jo,=20str)=20(json_out_str=20(jo,=20str,=20sizeof=20(str)=20= -=201))=0A+=0A+/*=20Add=20`bytes`=20bytes=20from=20`str`=20to=20the=20= buffer.=20=20*/=0A+static=20void=0A+json_out_str=20(json_out_t=20*jo,=20= const=20char=20*str,=20size_t=20bytes)=0A=20{=0A-=20=20const=20char=20= *buffer;=0A-=20=20ptrdiff_t=20size;=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=20json_make_room=20(jo,=20bytes);=0A+=20=20memcpy=20= (jo->buf=20+=20jo->size,=20str,=20bytes);=0A+=20=20jo->size=20+=3D=20= bytes;=0A+}=0A+=0A+static=20void=0A+json_out_byte=20(json_out_t=20*jo,=20= unsigned=20char=20c)=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=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=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-=0A-=20=20/*=20Enlarge=20the=20gap=20= if=20necessary.=20=20*/=0A-=20=20if=20(gap_size=20<=20len)=0A-=20=20=20=20= make_gap=20(len=20-=20gap_size);=0A-=0A-=20=20/*=20Copy=20this=20chunk=20= of=20data=20into=20the=20gap.=20=20*/=0A-=20=20memcpy=20((char=20*)=20= BEG_ADDR=20+=20PT_BYTE=20-=20BEG_BYTE=20+=20inserted_bytes,=0A-=09=20=20= buffer_and_size->buffer,=20len);=0A-=20=20= buffer_and_size->inserted_bytes=20+=3D=20len;=0A-=20=20return=20Qnil;=0A= +=20=20/*=20FIXME:=20this=20code=20is=20slow,=20make=20faster!=20*/=0A+=0A= +=20=20static=20const=20char=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=20= unsigned=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=20= json_out_byte=20(jo,=20c);=0A+=09=20=20p++;=0A+=09}=0A+=20=20=20=20=20=20= else=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=200xdf)=0A+=09=09n=20=3D=202;=0A+=09=20=20= =20=20=20=20else=20if=20(c=20<=3D=200xef)=0A+=09=09{=0A+=09=09=20=20int=20= v=20=3D=20(((c=20&=200x0f)=20<<=2012)=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=20= else=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&=200x3f)=20<<=206)=0A+=09=09=09= =20=20=20+=20(p[3]=20&=200x3f));=0A+=09=09=20=20if=20(v=20>=20= MAX_UNICODE_CHAR)=0A+=09=09=20=20=20=20string_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=09= string_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=20= n=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=20string_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'\\':=20json_out_byte=20(jo,=20c);=20break;=0A+=09=20=20= =20=20case=20'\b':=20json_out_byte=20(jo,=20'b');=20break;=0A+=09=20=20=20= =20case=20'\t':=20json_out_byte=20(jo,=20't');=20break;=0A+=09=20=20=20=20= case=20'\n':=20json_out_byte=20(jo,=20'n');=20break;=0A+=09=20=20=20=20= case=20'\f':=20json_out_byte=20(jo,=20'f');=20break;=0A+=09=20=20=20=20= case=20'\r':=20json_out_byte=20(jo,=20'r');=20break;=0A+=09=20=20=20=20= default:=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&=20= 0xf]=20};=0A+=09=09json_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=20json_out_byte=20(jo,=20'"');=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_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=20serialisation=20depth=20= exceeded");=0A+}=0A+=0A+static=20void=0A+json_out_unnest=20(json_out_t=20= *jo)=0A=20{=0A-=20=20switch=20(type)=0A+=20=20++jo->maxdepth;=0A+}=0A+=0A= +static=20void=20json_out_something=20(json_out_t=20*jo,=20Lisp_Object=20= obj);=0A+=0A+static=20void=0A+json_out_object_cons=20(json_out_t=20*jo,=20= Lisp_Object=20obj)=0A+{=0A+=20=20json_out_nest=20(jo);=0A+=20=20symset_t=20= ss=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=20bool=20first=20= =3D=20true;=0A+=20=20Lisp_Object=20tail=20=3D=20obj;=0A+=20=20= FOR_EACH_TAIL=20(tail)=0A=20=20=20=20=20{=0A-=20=20=20=20case=20= NONLOCAL_EXIT_SIGNAL:=0A-=20=20=20=20=20=20return=20data;=0A-=20=20=20=20= case=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=20= eassume=20(false);=0A+=20=20=20=20=20=20Lisp_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=20XCAR=20(tail);=0A+=09=20=20= CHECK_CONS=20(pair);=0A+=09=20=20key=20=3D=20XCAR=20(pair);=0A+=09=20=20= value=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+}=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=20}=0A+=20=20= json_out_byte=20(jo,=20'}');=0A+=20=20json_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=20obj)=0A=20{=0A-=20=20/*=20This=20= tracks=20how=20many=20bytes=20were=20inserted=20by=20the=20callback=20= since=0A-=20=20=20=20=20json_dump_callback=20was=20called.=20=20*/=0A-=20= =20ptrdiff_t=20inserted_bytes;=0A-=20=20/*=20nil=20if=20json_insert=20= succeeded,=20otherwise=20the=20symbol=0A-=20=20=20=20=20= Qcatch_all_memory_full=20or=20a=20cons=20(ERROR-SYMBOL=20.=20= ERROR-DATA).=20=20*/=0A-=20=20Lisp_Object=20error;=0A-};=0A+=20=20= json_out_nest=20(jo);=0A+=20=20json_out_byte=20(jo,=20'[');=0A+=20=20= ptrdiff_t=20n=20=3D=20ASIZE=20(obj);=0A+=20=20for=20(ptrdiff_t=20i=20=3D=20= 0;=20i=20<=20n;=20i++)=0A+=20=20=20=20{=0A+=20=20=20=20=20=20if=20(i=20>=20= 0)=0A+=09json_out_byte=20(jo,=20',');=0A+=20=20=20=20=20=20= json_out_something=20(jo,=20AREF=20(obj,=20i));=0A+=20=20=20=20}=0A+=20=20= json_out_byte=20(jo,=20']');=0A+=20=20json_out_unnest=20(jo);=0A+}=0A=20=0A= -/*=20Callback=20for=20json_dump_callback=20that=20inserts=20a=20JSON=20= representation=0A-=20=20=20as=20a=20unibyte=20string=20into=20the=20gap.=20= =20DATA=20must=20point=20to=20a=20structure=0A-=20=20=20of=20type=20= json_insert_data.=20=20This=20function=20may=20not=20exit=20nonlocally.=0A= -=20=20=20It=20catches=20all=20nonlocal=20exits=20and=20stores=20them=20= in=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=20= double=20x=20=3D=20XFLOAT_DATA=20(f);=0A+=20=20if=20(isinf=20(x)=20||=20= isnan=20(x))=0A+=20=20=20=20signal_error=20("not=20a=20finite=20number",=20= f);=0A+=20=20json_make_room=20(jo,=20FLOAT_TO_STRING_BUFSIZE);=0A+=20=20= int=20n=20=3D=20float_to_string=20(jo->buf=20+=20jo->size,=20x);=0A+=20=20= jo->size=20+=3D=20n;=0A+}=0A=20=0A-static=20int=0A-json_insert_callback=20= (const=20char=20*buffer,=20size_t=20size,=20void=20*data)=0A+static=20= void=0A+json_out_bignum=20(json_out_t=20*jo,=20Lisp_Object=20x)=0A=20{=0A= -=20=20struct=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_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+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+=0A+OBJECT=20must=20= be=20t,=20a=20number,=20string,=20vector,=20hashtable,=20alist,=20plist,=0A= +or=20the=20Lisp=20equivalents=20to=20the=20JSON=20null=20and=20false=20= values,=20and=20its=0A+elements=20must=20recursively=20consist=20of=20= the=20same=20kinds=20of=20values.=20=20t=20will=0A+be=20converted=20to=20= the=20JSON=20true=20value.=20=20Vectors=20will=20be=20converted=20to=0A= +JSON=20arrays,=20whereas=20hashtables,=20alists=20and=20plists=20are=20= converted=20to=0A+JSON=20objects.=20=20Hashtable=20keys=20must=20be=20= strings=20and=20must=20be=20unique=20within=0A+each=20object.=20=20Alist=20= and=20plist=20keys=20must=20be=20symbols;=20if=20a=20key=20is=20= duplicate,=0A+the=20first=20instance=20is=20used.=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=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+=0A+=20=20specpdl_ref=20count=20=3D=20SPECPDL_INDEX=20= ();=0A+=20=20record_unwind_protect_ptr=20(cleanup_json_out,=20&jo);=0A+=20= =20json_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+890,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+956,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..351d909f05b=20100644=0A---=20a/test/src/json-tests.el=0A+++=20= b/test/src/json-tests.el=0A@@=20-126,11=20+126,38=20@@=20= json-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-174,7=20+201,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=_E4201434-B8C0-4597-91DC-3456766FB664--