From nobody Mon Feb 9 04:09:14 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767775762; cv=none; d=zohomail.com; s=zohoarc; b=SrdLNv0iE/LeaGgJVTqC4oveTF/OfPCSuuTGIitzoIXqMRAmtvFGWPE6EWPg+Xsmc8DD0OoYaUNqsPVDXUZvr1EcbIaVQHFTcYC3XX4zLfhey9lqXHQ/JTMfzKfyQE4WiBD0WpZk6H0CsDslSNH+R5WsaUbiUVhuHMJJ4oXxKlM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767775762; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=L9afH1pn9SL3+pMXkAtwJhwWo5oJMAHQYUxmUA6SZKU=; b=J8JhCpHDiwPeCZPrdeQAbhMt55Jg7dLsMX0XgJAnItxdOyxw4BmhCNfZXLK+VKCUUe4VPC9vU6Ep/NOBxeGJfenDo3tiugiB5hm5Th60TvV80NdoHfIN71DFBPWobWPO/7Id8HWre88xw6zAwMvo4olge86NbPzQlgYiucYgyZ4= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767775762242241.0419244772478; Wed, 7 Jan 2026 00:49:22 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdPDq-0007mi-5F; Wed, 07 Jan 2026 03:48:54 -0500 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 1vdPDn-0007lQ-7C for qemu-devel@nongnu.org; Wed, 07 Jan 2026 03:48:51 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdPDl-0008Gm-55 for qemu-devel@nongnu.org; Wed, 07 Jan 2026 03:48:50 -0500 Received: from mail-wr1-f70.google.com (mail-wr1-f70.google.com [209.85.221.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-572-_kr9qy-CO6OLEFCtaIhLNQ-1; Wed, 07 Jan 2026 03:48:47 -0500 Received: by mail-wr1-f70.google.com with SMTP id ffacd0b85a97d-430fcfe4494so1387558f8f.2 for ; Wed, 07 Jan 2026 00:48:46 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-432bd5fe83bsm9369824f8f.38.2026.01.07.00.48.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 07 Jan 2026 00:48:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767775728; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=L9afH1pn9SL3+pMXkAtwJhwWo5oJMAHQYUxmUA6SZKU=; b=DtrWuHTNme9ll33dIagi4/cCHtDvqDraQr/1gnHteGmMHJaqIFuAHT734RXRNXouVak7P3 lmRQRw9LuSLCEKqVhhQSgBd8Xw+6R+OtazjZfkkV+6tugeGBxIlTFgMj3Ir0Fb4cG2iRPH BSAeib+tQ4thnXyIOtfFB5pmhsSqapk= X-MC-Unique: _kr9qy-CO6OLEFCtaIhLNQ-1 X-Mimecast-MFC-AGG-ID: _kr9qy-CO6OLEFCtaIhLNQ_1767775726 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767775725; x=1768380525; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=L9afH1pn9SL3+pMXkAtwJhwWo5oJMAHQYUxmUA6SZKU=; b=HiOL4oKJTOB9yubYHEQEhfOrrziDaNJtkqCHQBR8cvMrkcns4BvGIq8nk+DfKIe4IY 4pwO8v1n/rph8bh4wv4De93upTuA2YKUrQz5tXyNAkwQrin9qGbBLNwKi1fGQcpwzucJ B+hceQBW/cQewnsffF/pWNQVI8SD+vMdnD1oh8SZM+OPyy+hUs7bfm8VH5bwFPoNquyd svTlC4KkmKSpl805eFMxkR/aTk7q6Os8k/xEgyvcdt2f7cawS214eF8dfwajntFLZUBp zbQROf+6eGb//EWU5670EHF5teAbCvHdbSIyRpwgmpTq0ffQ031n2WT4WmbJ+ev1Ic9Z Tsuw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767775725; x=1768380525; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=L9afH1pn9SL3+pMXkAtwJhwWo5oJMAHQYUxmUA6SZKU=; b=FkH8ZcMwlG4FUprlTaH8iCukV6MfGwAU2LnkHJyMow5wGhRrJNi/de31R9b4udOXaL Fni2WCL7yMd/d4T39tBmY01hv8IP50Bqf8fjKxcHKEd3B2228oWc/4QOUnXKl6ow+M4F ZFJZWa8Pq+3/SNA7CtHAx5RxIok5PWckEPPmohegzniBiglUwRFJgg2DHhTvqJkJOiN6 81GExktf0inGwj58bFpWrIx6PladS3L5FK4ixzxFuHIuHsbhJypT8PU2cz0UawYRCijy U9m3FTiUDd7FVtzEzk5nmWYC7s+idXh94sdZ4aejjdOesFeS6cPswTgL1yoqbWQfRJjT smww== X-Gm-Message-State: AOJu0Yyc+CtYnTaIeV3OmkwVNp9/3bfQ1ELIIKRt/JW7zRJZL7LP97tA RUWkDeCLsaOZKtvXStqGCgpBgB4wSGAAiJQ67EcjlD0Q/yisc9yvlgaByYiTMzrnUzh2j8Oc3aS DyUV/4RMSQHL+ljPvPy9aky49pVoLEiKD7+23of0I5tdgNg3np/sbU4aOmPTlxG3ftr6/JRXiMg 8/NDFTfu4Ws76xohbRCw1rckI1lEn1pVyWbq0MKuhm X-Gm-Gg: AY/fxX4WTdA3tc6rv3Sy0y2w/OI81ornYHs7q5+yoeWCRxXU+HX6XO2ozdefiRjnoHP k017Fu2xx/YDLsakBRv9f62Wt6Efjs/sBRsE25qNod7B8XQBGr4dq5RTkB4RFiD0/CoPpw1ni/k B0LyuJF0m/2TS2/mZM1Scgd26JSFK1+fjCzxGW5UwfW+nqBG/eDzqsGWoMbhszBe5gIRFHtEr5h yaE5Hz9BtG5nP934e9WvuK3IHVioDYECmjFM0DooM/eaxslveDE8E5TxSZ28j5ltv69sSVyeBzq 4Cz8PAuPYI2pb+DyprSj1IvC9HkT8/Q5fpBBA41KHu3fWE2vcoHhyJpLqnnwmHZvw0hXqdTzSO1 Y7L1dRxouoGJWRFGlUqJJVkiWjmGy7Hbm06U1raizmMc6K/OFn4AZitjsjw4lCEUXPdrnLp+fmb Q6tH/enAHOFdFzew== X-Received: by 2002:a05:6000:40cd:b0:430:f7bc:4d0c with SMTP id ffacd0b85a97d-432c3775717mr1724953f8f.28.1767775725185; Wed, 07 Jan 2026 00:48:45 -0800 (PST) X-Google-Smtp-Source: AGHT+IGQeGlThVCrhOoXxXJ+Dtqd4hO6VR8DzASEeKAzNcywrfwt6Tk/lf11Q2P8N8ZheH1AtDrS8Q== X-Received: by 2002:a05:6000:40cd:b0:430:f7bc:4d0c with SMTP id ffacd0b85a97d-432c3775717mr1724920f8f.28.1767775724631; Wed, 07 Jan 2026 00:48:44 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com Subject: [PATCH 1/5] json-parser: pass around lookahead token, constify Date: Wed, 7 Jan 2026 09:48:36 +0100 Message-ID: <20260107084840.150843-2-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260107084840.150843-1-pbonzini@redhat.com> References: <20260107084840.150843-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.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, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767775765727158500 Content-Type: text/plain; charset="utf-8" Pass the lookahead token down to the various functions implementing the recursive descent, instead of first peeking and then getting the token again multiple times. The main purpose of this patch is to switch the argument passing style for parse_* functions to something more desirable for a push parser, which gets one and exactly one token at a time. However, there are some minor improvement in code size and a bugfix as well. In particular, because parse_array() and parse_object() can assume that the opening bracket/brace is not anymore in the token stream, it is now apparent that the first entry of an array incorrectly used the '[' character (stored in "token") as its location. parse_pair, for comparison, handled it correctly: if (!key) { parse_error(ctxt, peek, "key is not a string in object"); goto out; } Signed-off-by: Paolo Bonzini --- qobject/json-parser.c | 125 +++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 70 deletions(-) diff --git a/qobject/json-parser.c b/qobject/json-parser.c index 7483e582fea..eabc9f8358c 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -49,13 +49,13 @@ typedef struct JSONParserContext { * 4) deal with premature EOI */ =20 -static QObject *parse_value(JSONParserContext *ctxt); +static QObject *parse_value(JSONParserContext *ctxt, const JSONToken *toke= n); =20 /** * Error handler */ static void G_GNUC_PRINTF(3, 4) parse_error(JSONParserContext *ctxt, - JSONToken *token, const char *m= sg, ...) + const JSONToken *token, const c= har *msg, ...) { va_list ap; char message[1024]; @@ -126,7 +126,7 @@ static int cvt4hex(const char *s) * - Invalid Unicode characters are rejected. * - Control characters \x00..\x1F are rejected by the lexer. */ -static QString *parse_string(JSONParserContext *ctxt, JSONToken *token) +static QString *parse_string(JSONParserContext *ctxt, const JSONToken *tok= en) { const char *ptr =3D token->str; GString *str; @@ -235,42 +235,29 @@ out: return NULL; } =20 -/* Note: the token object returned by parser_context_peek_token or - * parser_context_pop_token is deleted as soon as parser_context_pop_token - * is called again. +/* Note: the token object returned by parser_context_pop_token is + * deleted as soon as parser_context_pop_token is called again. */ -static JSONToken *parser_context_pop_token(JSONParserContext *ctxt) +static const JSONToken *parser_context_pop_token(JSONParserContext *ctxt) { g_free(ctxt->current); ctxt->current =3D g_queue_pop_head(ctxt->buf); return ctxt->current; } =20 -static JSONToken *parser_context_peek_token(JSONParserContext *ctxt) -{ - return g_queue_peek_head(ctxt->buf); -} - /** * Parsing rules */ -static int parse_pair(JSONParserContext *ctxt, QDict *dict) +static int parse_pair(JSONParserContext *ctxt, const JSONToken *token, QDi= ct *dict) { QObject *key_obj =3D NULL; QString *key; QObject *value; - JSONToken *peek, *token; =20 - peek =3D parser_context_peek_token(ctxt); - if (peek =3D=3D NULL) { - parse_error(ctxt, NULL, "premature EOI"); - goto out; - } - - key_obj =3D parse_value(ctxt); + key_obj =3D parse_value(ctxt, token); key =3D qobject_to(QString, key_obj); if (!key) { - parse_error(ctxt, peek, "key is not a string in object"); + parse_error(ctxt, token, "key is not a string in object"); goto out; } =20 @@ -285,7 +272,13 @@ static int parse_pair(JSONParserContext *ctxt, QDict *= dict) goto out; } =20 - value =3D parse_value(ctxt); + token =3D parser_context_pop_token(ctxt); + if (token =3D=3D NULL) { + parse_error(ctxt, NULL, "premature EOI"); + goto out; + } + + value =3D parse_value(ctxt, token); if (value =3D=3D NULL) { parse_error(ctxt, token, "Missing value in dict"); goto out; @@ -309,21 +302,18 @@ out: static QObject *parse_object(JSONParserContext *ctxt) { QDict *dict =3D NULL; - JSONToken *token, *peek; - - token =3D parser_context_pop_token(ctxt); - assert(token && token->type =3D=3D JSON_LCURLY); + const JSONToken *token; =20 dict =3D qdict_new(); =20 - peek =3D parser_context_peek_token(ctxt); - if (peek =3D=3D NULL) { + token =3D parser_context_pop_token(ctxt); + if (token =3D=3D NULL) { parse_error(ctxt, NULL, "premature EOI"); goto out; } =20 - if (peek->type !=3D JSON_RCURLY) { - if (parse_pair(ctxt, dict) =3D=3D -1) { + if (token->type !=3D JSON_RCURLY) { + if (parse_pair(ctxt, token, dict) =3D=3D -1) { goto out; } =20 @@ -339,7 +329,13 @@ static QObject *parse_object(JSONParserContext *ctxt) goto out; } =20 - if (parse_pair(ctxt, dict) =3D=3D -1) { + token =3D parser_context_pop_token(ctxt); + if (token =3D=3D NULL) { + parse_error(ctxt, NULL, "premature EOI"); + goto out; + } + + if (parse_pair(ctxt, token, dict) =3D=3D -1) { goto out; } =20 @@ -349,8 +345,6 @@ static QObject *parse_object(JSONParserContext *ctxt) goto out; } } - } else { - (void)parser_context_pop_token(ctxt); } =20 return QOBJECT(dict); @@ -363,23 +357,20 @@ out: static QObject *parse_array(JSONParserContext *ctxt) { QList *list =3D NULL; - JSONToken *token, *peek; - - token =3D parser_context_pop_token(ctxt); - assert(token && token->type =3D=3D JSON_LSQUARE); + const JSONToken *token; =20 list =3D qlist_new(); =20 - peek =3D parser_context_peek_token(ctxt); - if (peek =3D=3D NULL) { + token =3D parser_context_pop_token(ctxt); + if (token =3D=3D NULL) { parse_error(ctxt, NULL, "premature EOI"); goto out; } =20 - if (peek->type !=3D JSON_RSQUARE) { + if (token->type !=3D JSON_RSQUARE) { QObject *obj; =20 - obj =3D parse_value(ctxt); + obj =3D parse_value(ctxt, token); if (obj =3D=3D NULL) { parse_error(ctxt, token, "expecting value"); goto out; @@ -399,7 +390,13 @@ static QObject *parse_array(JSONParserContext *ctxt) goto out; } =20 - obj =3D parse_value(ctxt); + token =3D parser_context_pop_token(ctxt); + if (token =3D=3D NULL) { + parse_error(ctxt, NULL, "premature EOI"); + goto out; + } + + obj =3D parse_value(ctxt, token); if (obj =3D=3D NULL) { parse_error(ctxt, token, "expecting value"); goto out; @@ -413,8 +410,6 @@ static QObject *parse_array(JSONParserContext *ctxt) goto out; } } - } else { - (void)parser_context_pop_token(ctxt); } =20 return QOBJECT(list); @@ -424,11 +419,8 @@ out: return NULL; } =20 -static QObject *parse_keyword(JSONParserContext *ctxt) +static QObject *parse_keyword(JSONParserContext *ctxt, const JSONToken *to= ken) { - JSONToken *token; - - token =3D parser_context_pop_token(ctxt); assert(token && token->type =3D=3D JSON_KEYWORD); =20 if (!strcmp(token->str, "true")) { @@ -442,11 +434,8 @@ static QObject *parse_keyword(JSONParserContext *ctxt) return NULL; } =20 -static QObject *parse_interpolation(JSONParserContext *ctxt) +static QObject *parse_interpolation(JSONParserContext *ctxt, const JSONTok= en *token) { - JSONToken *token; - - token =3D parser_context_pop_token(ctxt); assert(token && token->type =3D=3D JSON_INTERP); =20 if (!strcmp(token->str, "%p")) { @@ -478,11 +467,8 @@ static QObject *parse_interpolation(JSONParserContext = *ctxt) return NULL; } =20 -static QObject *parse_literal(JSONParserContext *ctxt) +static QObject *parse_literal(JSONParserContext *ctxt, const JSONToken *to= ken) { - JSONToken *token; - - token =3D parser_context_pop_token(ctxt); assert(token); =20 switch (token->type) { @@ -530,29 +516,21 @@ static QObject *parse_literal(JSONParserContext *ctxt) } } =20 -static QObject *parse_value(JSONParserContext *ctxt) +static QObject *parse_value(JSONParserContext *ctxt, const JSONToken *toke= n) { - JSONToken *token; - - token =3D parser_context_peek_token(ctxt); - if (token =3D=3D NULL) { - parse_error(ctxt, NULL, "premature EOI"); - return NULL; - } - switch (token->type) { case JSON_LCURLY: return parse_object(ctxt); case JSON_LSQUARE: return parse_array(ctxt); case JSON_INTERP: - return parse_interpolation(ctxt); + return parse_interpolation(ctxt, token); case JSON_INTEGER: case JSON_FLOAT: case JSON_STRING: - return parse_literal(ctxt); + return parse_literal(ctxt, token); case JSON_KEYWORD: - return parse_keyword(ctxt); + return parse_keyword(ctxt, token); default: parse_error(ctxt, token, "expecting value"); return NULL; @@ -575,8 +553,15 @@ QObject *json_parser_parse(GQueue *tokens, va_list *ap= , Error **errp) { JSONParserContext ctxt =3D { .buf =3D tokens, .ap =3D ap }; QObject *result; + const JSONToken *token; =20 - result =3D parse_value(&ctxt); + token =3D parser_context_pop_token(&ctxt); + if (token =3D=3D NULL) { + parse_error(&ctxt, NULL, "premature EOI"); + return NULL; + } + + result =3D parse_value(&ctxt, token); assert(ctxt.err || g_queue_is_empty(ctxt.buf)); =20 error_propagate(errp, ctxt.err); --=20 2.52.0 From nobody Mon Feb 9 04:09:14 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767775805; cv=none; d=zohomail.com; s=zohoarc; b=fZIeFWcjOUv5faTUe/oo2WcCghsIDqO3dNYgBTV6r7ulRSQ7UDLN4xgxmKs9h4dsYoYeMqqmdvEIWWXXGpnqUhfgso6hRH5AGEJfumI452GLNFhbEJ1vGAfz2Trsk8vV+AU9dCEwCR132r4bQiOM5SlapDJwCcAZNd5RnCJ4kAI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767775805; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=ysXu2nTAR4iWJ0hU03xZoojklQ6UiTmsXyuyw3CSLDU=; b=FpaZsmpq259yh1WVLZldh+Tbva4L+aqZ3vorMeALHSgyQ27aA/1/jVPDtjofObXay7qkTjzn3K/Wj5YNYr/rkS4NXLmboOhVmjE/Go7HfjdanXztZeq22o6NHZ9eCYdgTQwCc0qJCtDfhzpc7p6J7rnZ84c+uKo4dFxF74+4qUA= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767775805812684.5118200003907; Wed, 7 Jan 2026 00:50:05 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdPEC-000893-Ev; Wed, 07 Jan 2026 03:49:16 -0500 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 1vdPDr-0007oL-Dv for qemu-devel@nongnu.org; Wed, 07 Jan 2026 03:48:55 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdPDp-0008H4-2o for qemu-devel@nongnu.org; Wed, 07 Jan 2026 03:48:55 -0500 Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-179-VefmtilNNouMPuP4tOxtlg-1; Wed, 07 Jan 2026 03:48:50 -0500 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-47a83800743so2684775e9.0 for ; Wed, 07 Jan 2026 00:48:49 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-47d7f68f4ddsm89106815e9.2.2026.01.07.00.48.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 07 Jan 2026 00:48:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767775731; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ysXu2nTAR4iWJ0hU03xZoojklQ6UiTmsXyuyw3CSLDU=; b=M1f4HG/dix7W1AF2DTYHo7VPrximsUH0vkH+0HlXbGlYpy8NaOuU9bEnwKqKq7hh1/F8G2 NNbbx1boYKnnKAdFuHv3PuVWKzeMQlb56speexhLAZwtIwO84XeXDAeJP6sSg+kXEtE9K8 SOZ1/PRmxqCtFgqUPg4pfHQ1C8hAyG8= X-MC-Unique: VefmtilNNouMPuP4tOxtlg-1 X-Mimecast-MFC-AGG-ID: VefmtilNNouMPuP4tOxtlg_1767775729 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767775728; x=1768380528; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ysXu2nTAR4iWJ0hU03xZoojklQ6UiTmsXyuyw3CSLDU=; b=JMQgrmI3PzscLd1UxV3s0Kfzxbmadh+nco/uq/jTPf/a+EAyw5Eg7mh+xdksXrhhZq pRh+3kHGnQtio737/yEUbdHeuEklENJZKJu7ZZvfcvL142PUy0a8BU2OOJyo+rw6Ap5g z7qLCHNpzi1lNLjUnf4shSbcFOsUrJyGODcUoNGBwclftA02KEUkJsMfsdxBVHCsBIo5 aeFxKlAvk+1pJGM9SzhIeqA7qjJEBzN0VJCCuCBI04ZSmDFOT1BWVJL2azaoioCJ24j2 8Mk9wVfXdk+D/2Z3bxq/1/wiLg6+IKjGPX3yy3yig+qFc8HylxB7T1oi2+lFPhdFKVJP 0Dlg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767775728; x=1768380528; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=ysXu2nTAR4iWJ0hU03xZoojklQ6UiTmsXyuyw3CSLDU=; b=AoVZsr+qBIIuonqmuovp0VI3zDfBjSrJwCVDpXtCZn6fY3w2R3x2B8xA8D06z/jYLX GEJHTP04F0QJ2VI0HRFmb7DTL0A9LYC0uGdjUR1N0ol1NCuD9q7xzPGUzUbsZZMy7WMH 1djqc8IaPrcQa/mSTYuB/OZmn12+fj5QFMMv5eX3fbnYmedVxGGdzSGARHg9DrNrvnZx POEbzxV4CnVlrhnW5n3/QOI8ROrXA+hTJ1pe2g699PBEWSi56LbSNehmjYpFpLYoCGSh lH7N1VgfOOWoWpvQgGmN+bOh64j5ZbcxEJm98nUe3d6AVwDHCoXD49KU7siUqX+YhS6z IUCA== X-Gm-Message-State: AOJu0Yy/xxLG6TlvWcDxaQgiLp5qGVVH4q4I/RFSdOKqkNEj8XEqLtvp lZ8CLduqgGNQK50VdCJ+ljJC9fp56zE0RnnQg3DL4wCqs2NcxbQYK0tnsyiSCmrBbYDygZ5vrIn BYcEni36iGxvmNlF0ELR6NOZr2jExsPOfeQBBh4wJOOzF7s7f/LhkvYOQB+V9uJEgl+1xSoGHAx 4XzIbrGJwEUk8Txhb3Wu+95322VgveZbtQGBDJH11M X-Gm-Gg: AY/fxX7g1M91mfJ3EDQb1b17/H6bkc8s/qRenuteZeCy9/h1WFv1Cmz+sABQ1qydACL w74eteQkeBd9EAm8lsO7hTdOGUExvB7LvDBYydPB7I+++kBEA2QSpIz6hoCYuYG+CYzgi6TzN24 E4DhV/CmxGHdH+CXGKY8jqXypA8PHKxUzDfTztIkbISWOEBUyc3iSiC+MkFD6gMWL4BwZqNN0No kEi009cRR0oivuJg7dHUbafqYZfcNcWPopnRIlZhIENYIYmw+G46IVA/00FX5FeyVcmwZTjuWUF fl+yu6c1y6cMmCKKs5wzzBnPpfJYEbcAnh3OFbULtlvQSx230ZSM0elTEktxX3bBOIvU71dcMyz Qnw3+lPLgdSOB2s+b4YxfU52MRRkGzG2nZX7p8QkctRy60u5vEgxsyKAdkp4ryooSq1jpi9wNZp Juo2qBMolzlbrGiQ== X-Received: by 2002:a05:600c:6749:b0:471:5c0:94fc with SMTP id 5b1f17b1804b1-47d84849fb2mr18833185e9.6.1767775727633; Wed, 07 Jan 2026 00:48:47 -0800 (PST) X-Google-Smtp-Source: AGHT+IG+Zw68NHZ1mOI8/cfZZMV3aA55FoGtdU7S7MOIYSXWN4AEGA/P5F9Z+HXBpOYCfir8YyN8sQ== X-Received: by 2002:a05:600c:6749:b0:471:5c0:94fc with SMTP id 5b1f17b1804b1-47d84849fb2mr18832935e9.6.1767775727065; Wed, 07 Jan 2026 00:48:47 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com Subject: [PATCH 2/5] json-parser: replace with a push parser Date: Wed, 7 Jan 2026 09:48:37 +0100 Message-ID: <20260107084840.150843-3-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260107084840.150843-1-pbonzini@redhat.com> References: <20260107084840.150843-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.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, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767775807969158500 Content-Type: text/plain; charset="utf-8" In order to avoid stashing all the tokens corresponding to a JSON value, embed the parsing stack and state machine in JSONParser. This is more efficient and allows for more prompt error recovery; it also does not make the code substantially larger than the current recursive descent parser, though the state machine is probably a bit harder to follow. The stack consists of QLists and QDicts corresponding to open brackets and braces, plus optionally a QString with the current key on top of each QDict. After each value is parsed, it is added to the top array or dictionary or, if the stack is empty, json_parser_feed returns the complete QObject. For now, json-streamer.c keeps tracking the tokens up until braces and brackets are balanced, and then shoves the whole queue of tokens into the push parser. The only logic change is that JSON_END_OF_INPUT always triggers the emptying of the queue; the parser takes notice and checks that there is nothing on the stack. Not using brace_count and bracket_count for this is the first step towards improved separation of concerns between json-parser.c and json-streamer.c. Signed-off-by: Paolo Bonzini --- include/qobject/json-parser.h | 6 + qobject/json-parser-int.h | 4 +- qobject/json-parser.c | 430 +++++++++++++++++----------------- qobject/json-streamer.c | 21 +- 4 files changed, 245 insertions(+), 216 deletions(-) diff --git a/include/qobject/json-parser.h b/include/qobject/json-parser.h index 7345a9bd5cb..05346fa816b 100644 --- a/include/qobject/json-parser.h +++ b/include/qobject/json-parser.h @@ -20,6 +20,12 @@ typedef struct JSONLexer { int x, y; } JSONLexer; =20 +typedef struct JSONParserContext { + Error *err; + GQueue *stack; + va_list *ap; +} JSONParserContext; + typedef struct JSONMessageParser { void (*emit)(void *opaque, QObject *json, Error *err); void *opaque; diff --git a/qobject/json-parser-int.h b/qobject/json-parser-int.h index 8c01f236276..05e2e8e1831 100644 --- a/qobject/json-parser-int.h +++ b/qobject/json-parser-int.h @@ -49,6 +49,8 @@ void json_message_process_token(JSONLexer *lexer, GString= *input, =20 /* json-parser.c */ JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr); -QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp); +void json_parser_init(JSONParserContext *ctxt, va_list *ap); +QObject *json_parser_feed(JSONParserContext *ctxt, const JSONToken *token,= Error **errp); +void json_parser_destroy(JSONParserContext *ctxt); =20 #endif diff --git a/qobject/json-parser.c b/qobject/json-parser.c index eabc9f8358c..338f0c5aae6 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -31,12 +31,36 @@ struct JSONToken { char str[]; }; =20 -typedef struct JSONParserContext { - Error *err; - JSONToken *current; - GQueue *buf; - va_list *ap; -} JSONParserContext; +/* + * Objects: { } | { members } + * - Empty: { -> AFTER_LCURLY -> } + * - Non-empty: { -> AFTER_LCURLY -> BEFORE_KEY -> string -> END_OF_KEY ->= : -> + * BEFORE_VALUE -> value -> END_OF_VALUE -> , -> BEFORE_KEY -= > ... -> } + * + * Arrays: [ ] | [ elements ] + * - Empty: [ -> AFTER_LSQUARE -> ] + * - Non-empty: [ -> AFTER_LSQUARE -> BEFORE_VALUE -> value -> END_OF_VALU= E -> , -> + * BEFORE_VALUE -> ... -> ] + * + * The two cases for END_OF_VALUE are distinguished based on the type of Q= Object at + * top-of-stack. + */ +typedef enum JSONParserState { + AFTER_LCURLY, + AFTER_LSQUARE, + BEFORE_KEY, + BEFORE_VALUE, + END_OF_KEY, + END_OF_VALUE, +} JSONParserState; + +typedef struct JSONParserStackEntry { + /* A QString with the last parsed key, or a QList/QDict for the curren= t container. */ + QObject *partial; + + /* Needed to distinguish whether the parser is waiting for a colon or = comma. */ + JSONParserState state; +} JSONParserStackEntry; =20 #define BUG_ON(cond) assert(!(cond)) =20 @@ -49,7 +73,29 @@ typedef struct JSONParserContext { * 4) deal with premature EOI */ =20 -static QObject *parse_value(JSONParserContext *ctxt, const JSONToken *toke= n); +static inline JSONParserStackEntry *current_entry(JSONParserContext *ctxt) +{ + return g_queue_peek_tail(ctxt->stack); +} + +static void push_entry(JSONParserContext *ctxt, QObject *partial, JSONPars= erState state) +{ + JSONParserStackEntry *entry =3D g_new(JSONParserStackEntry, 1); + entry->partial =3D partial; + entry->state =3D state; + g_queue_push_tail(ctxt->stack, entry); +} + +/* Note that entry->partial does *not* lose its reference count even if va= lue =3D=3D NULL. */ +static JSONParserStackEntry *pop_entry(JSONParserContext *ctxt, QObject **= value) +{ + JSONParserStackEntry *entry =3D g_queue_pop_tail(ctxt->stack); + if (value) { + *value =3D entry->partial; + } + g_free(entry); + return current_entry(ctxt); +} =20 /** * Error handler @@ -235,189 +281,7 @@ out: return NULL; } =20 -/* Note: the token object returned by parser_context_pop_token is - * deleted as soon as parser_context_pop_token is called again. - */ -static const JSONToken *parser_context_pop_token(JSONParserContext *ctxt) -{ - g_free(ctxt->current); - ctxt->current =3D g_queue_pop_head(ctxt->buf); - return ctxt->current; -} - -/** - * Parsing rules - */ -static int parse_pair(JSONParserContext *ctxt, const JSONToken *token, QDi= ct *dict) -{ - QObject *key_obj =3D NULL; - QString *key; - QObject *value; - - key_obj =3D parse_value(ctxt, token); - key =3D qobject_to(QString, key_obj); - if (!key) { - parse_error(ctxt, token, "key is not a string in object"); - goto out; - } - - token =3D parser_context_pop_token(ctxt); - if (token =3D=3D NULL) { - parse_error(ctxt, NULL, "premature EOI"); - goto out; - } - - if (token->type !=3D JSON_COLON) { - parse_error(ctxt, token, "missing : in object pair"); - goto out; - } - - token =3D parser_context_pop_token(ctxt); - if (token =3D=3D NULL) { - parse_error(ctxt, NULL, "premature EOI"); - goto out; - } - - value =3D parse_value(ctxt, token); - if (value =3D=3D NULL) { - parse_error(ctxt, token, "Missing value in dict"); - goto out; - } - - if (qdict_haskey(dict, qstring_get_str(key))) { - parse_error(ctxt, token, "duplicate key"); - goto out; - } - - qdict_put_obj(dict, qstring_get_str(key), value); - - qobject_unref(key_obj); - return 0; - -out: - qobject_unref(key_obj); - return -1; -} - -static QObject *parse_object(JSONParserContext *ctxt) -{ - QDict *dict =3D NULL; - const JSONToken *token; - - dict =3D qdict_new(); - - token =3D parser_context_pop_token(ctxt); - if (token =3D=3D NULL) { - parse_error(ctxt, NULL, "premature EOI"); - goto out; - } - - if (token->type !=3D JSON_RCURLY) { - if (parse_pair(ctxt, token, dict) =3D=3D -1) { - goto out; - } - - token =3D parser_context_pop_token(ctxt); - if (token =3D=3D NULL) { - parse_error(ctxt, NULL, "premature EOI"); - goto out; - } - - while (token->type !=3D JSON_RCURLY) { - if (token->type !=3D JSON_COMMA) { - parse_error(ctxt, token, "expected separator in dict"); - goto out; - } - - token =3D parser_context_pop_token(ctxt); - if (token =3D=3D NULL) { - parse_error(ctxt, NULL, "premature EOI"); - goto out; - } - - if (parse_pair(ctxt, token, dict) =3D=3D -1) { - goto out; - } - - token =3D parser_context_pop_token(ctxt); - if (token =3D=3D NULL) { - parse_error(ctxt, NULL, "premature EOI"); - goto out; - } - } - } - - return QOBJECT(dict); - -out: - qobject_unref(dict); - return NULL; -} - -static QObject *parse_array(JSONParserContext *ctxt) -{ - QList *list =3D NULL; - const JSONToken *token; - - list =3D qlist_new(); - - token =3D parser_context_pop_token(ctxt); - if (token =3D=3D NULL) { - parse_error(ctxt, NULL, "premature EOI"); - goto out; - } - - if (token->type !=3D JSON_RSQUARE) { - QObject *obj; - - obj =3D parse_value(ctxt, token); - if (obj =3D=3D NULL) { - parse_error(ctxt, token, "expecting value"); - goto out; - } - - qlist_append_obj(list, obj); - - token =3D parser_context_pop_token(ctxt); - if (token =3D=3D NULL) { - parse_error(ctxt, NULL, "premature EOI"); - goto out; - } - - while (token->type !=3D JSON_RSQUARE) { - if (token->type !=3D JSON_COMMA) { - parse_error(ctxt, token, "expected separator in list"); - goto out; - } - - token =3D parser_context_pop_token(ctxt); - if (token =3D=3D NULL) { - parse_error(ctxt, NULL, "premature EOI"); - goto out; - } - - obj =3D parse_value(ctxt, token); - if (obj =3D=3D NULL) { - parse_error(ctxt, token, "expecting value"); - goto out; - } - - qlist_append_obj(list, obj); - - token =3D parser_context_pop_token(ctxt); - if (token =3D=3D NULL) { - parse_error(ctxt, NULL, "premature EOI"); - goto out; - } - } - } - - return QOBJECT(list); - -out: - qobject_unref(list); - return NULL; -} +/* Terminals */ =20 static QObject *parse_keyword(JSONParserContext *ctxt, const JSONToken *to= ken) { @@ -516,13 +380,17 @@ static QObject *parse_literal(JSONParserContext *ctxt= , const JSONToken *token) } } =20 -static QObject *parse_value(JSONParserContext *ctxt, const JSONToken *toke= n) +/* Parsing state machine */ + +static QObject *parse_begin_value(JSONParserContext *ctxt, const JSONToken= *token) { switch (token->type) { case JSON_LCURLY: - return parse_object(ctxt); + push_entry(ctxt, QOBJECT(qdict_new()), AFTER_LCURLY); + return NULL; case JSON_LSQUARE: - return parse_array(ctxt); + push_entry(ctxt, QOBJECT(qlist_new()), AFTER_LSQUARE); + return NULL; case JSON_INTERP: return parse_interpolation(ctxt, token); case JSON_INTEGER: @@ -537,6 +405,130 @@ static QObject *parse_value(JSONParserContext *ctxt, = const JSONToken *token) } } =20 +static QObject *json_parser_parse_token(JSONParserContext *ctxt, const JSO= NToken *token) +{ + JSONParserStackEntry *entry; + JSONParserState state; + QString *key; + QObject *value =3D NULL; + + entry =3D current_entry(ctxt); + state =3D entry ? entry->state : BEFORE_VALUE; + switch (state) { + case AFTER_LCURLY: + /* Grab '}' for empty object or fall through to BEFORE_KEY */ + if (token->type =3D=3D JSON_RCURLY) { + entry =3D pop_entry(ctxt, &value); + break; + } + entry->state =3D BEFORE_KEY; + /* fall through */ + + case BEFORE_KEY: + /* Expecting object key */ + if (token->type =3D=3D JSON_STRING) { + key =3D parse_string(ctxt, token); + if (!key) { + return NULL; + } + + /* Store key in a special entry on the stack */ + push_entry(ctxt, QOBJECT(key), END_OF_KEY); + } else { + parse_error(ctxt, token, "expecting key"); + } + return NULL; + + case END_OF_KEY: + /* Expecting ':' after key */ + if (token->type =3D=3D JSON_COLON) { + entry->state =3D BEFORE_VALUE; + } else { + parse_error(ctxt, token, "expecting ':'"); + } + return NULL; + + case AFTER_LSQUARE: + /* Grab ']' for empty array or fall through to BEFORE_VALUE */ + if (token->type =3D=3D JSON_RSQUARE) { + entry =3D pop_entry(ctxt, &value); + break; + } + entry->state =3D BEFORE_VALUE; + /* fall through */ + + case BEFORE_VALUE: + /* Expecting value */ + value =3D parse_begin_value(ctxt, token); + if (!value) { + /* Error or '['/'{' */ + return NULL; + } + /* Return value or insert it into a container */ + break; + + case END_OF_VALUE: + /* Grab ',' or ']' for array; ',' or '}' for object */ + if (qobject_to(QList, entry->partial)) { + /* Array */ + if (token->type !=3D JSON_RSQUARE) { + if (token->type =3D=3D JSON_COMMA) { + entry->state =3D BEFORE_VALUE; + } else { + parse_error(ctxt, token, "expected ',' or ']'"); + } + return NULL; + } + } else if (qobject_to(QDict, entry->partial)) { + /* Object */ + if (token->type !=3D JSON_RCURLY) { + if (token->type =3D=3D JSON_COMMA) { + entry->state =3D BEFORE_KEY; + } else { + parse_error(ctxt, token, "expected ',' or '}'"); + } + return NULL; + } + } else { + g_assert_not_reached(); + } + + /* Got ']' or '}', return value or insert into parent container */ + entry =3D pop_entry(ctxt, &value); + break; + } + + assert(value); + if (entry =3D=3D NULL) { + /* The toplevel value is complete. */ + return value; + } + + key =3D qobject_to(QString, entry->partial); + if (key) { + const char *key_str; + QDict *dict; + + entry =3D pop_entry(ctxt, NULL); + dict =3D qobject_to(QDict, entry->partial); + assert(dict); + key_str =3D qstring_get_str(key); + if (qdict_haskey(dict, key_str)) { + parse_error(ctxt, token, "duplicate key"); + qobject_unref(value); + return NULL; + } + qdict_put_obj(dict, key_str, value); + qobject_unref(key); + } else { + /* Add to array */ + qlist_append_obj(qobject_to(QList, entry->partial), value); + } + + entry->state =3D END_OF_VALUE; + return NULL; +} + JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr) { JSONToken *token =3D g_malloc(sizeof(JSONToken) + tokstr->len + 1); @@ -549,27 +541,43 @@ JSONToken *json_token(JSONTokenType type, int x, int = y, GString *tokstr) return token; } =20 -QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp) +void json_parser_init(JSONParserContext *ctxt, va_list *ap) { - JSONParserContext ctxt =3D { .buf =3D tokens, .ap =3D ap }; - QObject *result; - const JSONToken *token; + ctxt->err =3D NULL; + ctxt->stack =3D g_queue_new(); + ctxt->ap =3D ap; +} =20 - token =3D parser_context_pop_token(&ctxt); - if (token =3D=3D NULL) { - parse_error(&ctxt, NULL, "premature EOI"); - return NULL; +void json_parser_destroy(JSONParserContext *ctxt) +{ + JSONParserStackEntry *entry; + + while ((entry =3D g_queue_pop_tail(ctxt->stack)) !=3D NULL) { + qobject_unref(entry->partial); + g_free(entry); + } + g_queue_free(ctxt->stack); + ctxt->stack =3D NULL; +} + +QObject *json_parser_feed(JSONParserContext *ctxt, const JSONToken *token,= Error **errp) +{ + QObject *result =3D NULL; + + assert(!ctxt->err); + switch (token->type) { + case JSON_END_OF_INPUT: + /* Check for premature end of input */ + if (!g_queue_is_empty(ctxt->stack)) { + parse_error(ctxt, token, "premature end of input"); + } + break; + + default: + result =3D json_parser_parse_token(ctxt, token); + break; } =20 - result =3D parse_value(&ctxt, token); - assert(ctxt.err || g_queue_is_empty(ctxt.buf)); - - error_propagate(errp, ctxt.err); - - while (!g_queue_is_empty(ctxt.buf)) { - parser_context_pop_token(&ctxt); - } - g_free(ctxt.current); - + error_propagate(errp, ctxt->err); return result; } diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c index b93d97b995f..6c93e6fd78d 100644 --- a/qobject/json-streamer.c +++ b/qobject/json-streamer.c @@ -32,6 +32,7 @@ void json_message_process_token(JSONLexer *lexer, GString= *input, JSONTokenType type, int x, int y) { JSONMessageParser *parser =3D container_of(lexer, JSONMessageParser, l= exer); + JSONParserContext ctxt; QObject *json =3D NULL; Error *err =3D NULL; JSONToken *token; @@ -56,8 +57,7 @@ void json_message_process_token(JSONLexer *lexer, GString= *input, if (g_queue_is_empty(&parser->tokens)) { return; } - json =3D json_parser_parse(&parser->tokens, parser->ap, &err); - goto out_emit; + break; default: break; } @@ -85,11 +85,24 @@ void json_message_process_token(JSONLexer *lexer, GStri= ng *input, g_queue_push_tail(&parser->tokens, token); =20 if ((parser->brace_count > 0 || parser->bracket_count > 0) - && parser->brace_count >=3D 0 && parser->bracket_count >=3D 0) { + && parser->brace_count >=3D 0 && parser->bracket_count >=3D 0 + && type !=3D JSON_END_OF_INPUT) { return; } =20 - json =3D json_parser_parse(&parser->tokens, parser->ap, &err); + json_parser_init(&ctxt, parser->ap); + + /* Process all tokens in the queue */ + while (!g_queue_is_empty(&parser->tokens)) { + token =3D g_queue_pop_head(&parser->tokens); + json =3D json_parser_feed(&ctxt, token, &err); + g_free(token); + if (json || err) { + break; + } + } + + json_parser_destroy(&ctxt); =20 out_emit: parser->brace_count =3D 0; --=20 2.52.0 From nobody Mon Feb 9 04:09:14 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767775762; cv=none; d=zohomail.com; s=zohoarc; b=bTKiK9H2JgPPSaxINeDVK8Co2Q1CmBaCxX6uWrktmHkGvy2Y5tbf3W+o1sXKimY7nNbICpXiqgr4mfb2P0X0EKWXzg9RwBK6bmbPK7A3eKoXP2dZymAfKBbWAeYteMLfC4ReQxhhuFmjrHcSH7pKjCPYaVWAAn/YMtdnHUIhfR8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767775762; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=f/FsbdCWOY56dDxZsECSCejih4ncADi7hDumpDJ65gk=; b=MGPjy3+WhEf2V1EE/gSSGNZj25HlRBcFmlxvxEFrKdLoRTI9hb8rgvNpKURrhgUt8RD8ZeP2xfGpM1tIMrsZvmlOe/09baSjrG6U0ERJ+em5bfMpqmav3AvU5R3dVppdPo6LhYonolkf3uMmKZvstO3WNjfg1YP+Ykj6VyrpoFc= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767775762550523.31422759045; Wed, 7 Jan 2026 00:49:22 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdPEA-0007yR-6L; Wed, 07 Jan 2026 03:49:15 -0500 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 1vdPDr-0007nP-0y for qemu-devel@nongnu.org; Wed, 07 Jan 2026 03:48:55 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdPDp-0008H9-4x for qemu-devel@nongnu.org; Wed, 07 Jan 2026 03:48:54 -0500 Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-346-qlp-v9NgMNqT1iIphehwqA-1; Wed, 07 Jan 2026 03:48:51 -0500 Received: by mail-wm1-f71.google.com with SMTP id 5b1f17b1804b1-4775f51ce36so17338215e9.1 for ; Wed, 07 Jan 2026 00:48:50 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-47d7f410c6csm92171235e9.1.2026.01.07.00.48.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 07 Jan 2026 00:48:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767775732; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=f/FsbdCWOY56dDxZsECSCejih4ncADi7hDumpDJ65gk=; b=d57TST26S9Tpr6M/LlwSpgqx1ocWqMoHKGtI+sCvnX+hDnNaLRdEjEV1yhl3kVFFOguzi+ DhUeA+NUQo7LU1F8pQJwPWJfr3ZlLRmoFohni88bFb9CfMoVmQ+7tTY6IeLIrgKj9uwiPg nBisrHE9w4nA76yzZY33e0JR2JSUoKA= X-MC-Unique: qlp-v9NgMNqT1iIphehwqA-1 X-Mimecast-MFC-AGG-ID: qlp-v9NgMNqT1iIphehwqA_1767775730 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767775729; x=1768380529; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=f/FsbdCWOY56dDxZsECSCejih4ncADi7hDumpDJ65gk=; b=R8rappoSQurWKn9b8QXk40gEfPjTxFIfRZ3uy9trvIA99M9jVQZ6EGnMsxQRE1EJiy 1rZi6e+oPruAO6Yol3YgYKGaDiBsDRRbhkuL8rPDIx3ya3LGO9Jaa8v8RVR09WMYZcJt a5Ae4drlhhg7gRof1ZyRCGToA3mO3Z1qlLel29CB59TtmGQLsWP13L4gxYoiN+iirL7R 9hlvP5flJrEmEEbbXikEhB5YTIbtP6RX1e0auCPz9FtY40woqYCN/JLplJ7NQqy4/Tzk tcXhxJpZJiZ+OMSWNCTPhzVYvDJaG32wJySottfu9Oak9GdtfLEgbRqsw5pTMEkNEBTO /Ydg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767775729; x=1768380529; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=f/FsbdCWOY56dDxZsECSCejih4ncADi7hDumpDJ65gk=; b=eBHGp47mqJ5Xb41ZcT7ZbvQbUfI7CF6zjIRSTrNP27WgJNdyuQabnfzDK7YAxWTvfW 3/tJPV3JdPa1aW30ZDrgcHm+crpEUpKI0DPNzKMVhq+omuzoaZU005EBestTxBZSUoFT KuEz0+mNWXyFEY2lEYfMcsRfuSmtFCuQtFXVLztgKShiMcX0tBKPApBanYlz678aFda3 27e2PEb+K3OyoUNMwNY/inqRysCodPkLB/5LkD3nBuu3aDQ0l4Ps9W07/QUgdvBcJ2/V UiXyuMr5jkyTRoTU62yZ1SZQE8pJAoUVnQD3G6JEZZPUs3mTSiqRRE/PR2ouWHP8Pahw ihKA== X-Gm-Message-State: AOJu0YwRb/g6TPpFOt6u38mcI8veAQc4n9GCf/u3DaPRMrJLI1+yCdiv QP0WmOVY72MxQ/KWrXi42ykJEpzz41os4trOj1EGYgDQrkJL5NDftsUo/zE/zWGBUDjF/WWA6gm HMq9dv9mN0Sg6/yjyQZFzv1HjAy8OvmDchbYtkR59qheRUZfUh0LM1HGx4ptlGPKJAVZ2Axknh5 USJ/EwjF8LFhvidRE3O6O/1XUU5iPVmmjMPIgXcPqS X-Gm-Gg: AY/fxX594Mo2JhGWiPhdw6OJzoHfS3qjuwYS0znoT4onrVD2vWaKv9QQXhXv8urnQvD 0mE9z9FEBflAqjhhym1s/7HiC8GsoxkyIV43uXsAokysOWU62VuQp3JX/93nn/P4KTlUKhctNwx NfucnQmdSMmgYASZxPxnrEUPNr8plyVDhDNUsPXh5o3g4ljuMxznyrvBz0dK0VfjjoiyrKemCDP /Es2KHG5+BbEIUHGe4+LxKUWmtL5u+sond9g7YNdSUgAKiCTn6toFvkrfGzvksSSTnZIpP/5iu7 Ni9K4jQI/ZRPhqKRB+ggWGV4wpCQZBF2rQ7PwPziPI7Jmk6Su54j2qMVlc+zxxhILGp9Tn5PBwy xJXHb6Vmlj1Z4u4Q67FcPmmmFzTxehENBxv/HVatcqgVPPd3vUR2FeWleN3kq/upJvjkDi+AUYt 6OCXfV/x/LREqmbw== X-Received: by 2002:a05:600c:a16:b0:47b:de05:aa28 with SMTP id 5b1f17b1804b1-47d84b0aadcmr15982765e9.2.1767775729008; Wed, 07 Jan 2026 00:48:49 -0800 (PST) X-Google-Smtp-Source: AGHT+IHru43wlLq870Le44ky7slXIf33c1MeOM6bTg0/ZZVBIq0VnslBIdKkE8ZGD92BblvG0E2tOA== X-Received: by 2002:a05:600c:a16:b0:47b:de05:aa28 with SMTP id 5b1f17b1804b1-47d84b0aadcmr15982385e9.2.1767775728417; Wed, 07 Jan 2026 00:48:48 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com Subject: [PATCH 3/5] json-streamer: remove token queue Date: Wed, 7 Jan 2026 09:48:38 +0100 Message-ID: <20260107084840.150843-4-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260107084840.150843-1-pbonzini@redhat.com> References: <20260107084840.150843-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.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, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767775765611158500 Content-Type: text/plain; charset="utf-8" Now fully exploit the push parser, feeding it one token at a time without having to wait until braces and brackets are balanced. While the nesting counts are retained for error recovery purposes, the system can now report the first parsing error without waiting for delimiters to be balanced. This also means that JSON_ERROR can be handled in json-parser.c, not json-streamer.c. After reporting the error, json-streamer.c then enters an error recovery mode where subsequent errors are suppressed. This mimics the previous error reporting behavior, but it provides prompt feedback on parsing errors. As an example, here is an example interaction with qemu-ga. BEFORE (error reported only once braces are balanced): >> {'execute':foo >> } << {"error": {"class": "GenericError", "desc": "JSON parse error, invali= d keyword 'foo'"}} >> {'execute':'somecommand'} << {"error": {"class": "CommandNotFound", "desc": "The command somecomma= nd has not been found"}} AFTER (error reported immediately, but similar error recovery as before): >> {'execute':foo << {"error": {"class": "GenericError", "desc": "JSON parse error, invali= d keyword 'foo'"}} >> } >> {'execute':'qmp_capabilities'} << {"error": {"class": "CommandNotFound", "desc": "The command qmp_capab= ilities has not been found"}} Signed-off-by: Paolo Bonzini --- include/qobject/json-parser.h | 5 +- qobject/json-parser-int.h | 1 + qobject/json-parser.c | 26 +++++--- qobject/json-streamer.c | 116 ++++++++++++++-------------------- 4 files changed, 68 insertions(+), 80 deletions(-) diff --git a/include/qobject/json-parser.h b/include/qobject/json-parser.h index 05346fa816b..923eb74ca00 100644 --- a/include/qobject/json-parser.h +++ b/include/qobject/json-parser.h @@ -29,11 +29,12 @@ typedef struct JSONParserContext { typedef struct JSONMessageParser { void (*emit)(void *opaque, QObject *json, Error *err); void *opaque; - va_list *ap; JSONLexer lexer; + JSONParserContext parser; int brace_count; int bracket_count; - GQueue tokens; + int token_count; + bool error; uint64_t token_size; } JSONMessageParser; =20 diff --git a/qobject/json-parser-int.h b/qobject/json-parser-int.h index 05e2e8e1831..1f435cb8eb2 100644 --- a/qobject/json-parser-int.h +++ b/qobject/json-parser-int.h @@ -50,6 +50,7 @@ void json_message_process_token(JSONLexer *lexer, GString= *input, /* json-parser.c */ JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr); void json_parser_init(JSONParserContext *ctxt, va_list *ap); +void json_parser_reset(JSONParserContext *ctxt); QObject *json_parser_feed(JSONParserContext *ctxt, const JSONToken *token,= Error **errp); void json_parser_destroy(JSONParserContext *ctxt); =20 diff --git a/qobject/json-parser.c b/qobject/json-parser.c index 338f0c5aae6..7abdea4dacb 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -541,21 +541,27 @@ JSONToken *json_token(JSONTokenType type, int x, int = y, GString *tokstr) return token; } =20 -void json_parser_init(JSONParserContext *ctxt, va_list *ap) -{ - ctxt->err =3D NULL; - ctxt->stack =3D g_queue_new(); - ctxt->ap =3D ap; -} - -void json_parser_destroy(JSONParserContext *ctxt) +void json_parser_reset(JSONParserContext *ctxt) { JSONParserStackEntry *entry; =20 + ctxt->err =3D NULL; while ((entry =3D g_queue_pop_tail(ctxt->stack)) !=3D NULL) { qobject_unref(entry->partial); g_free(entry); } +} + +void json_parser_init(JSONParserContext *ctxt, va_list *ap) +{ + ctxt->stack =3D g_queue_new(); + ctxt->ap =3D ap; + json_parser_reset(ctxt); +} + +void json_parser_destroy(JSONParserContext *ctxt) +{ + json_parser_reset(ctxt); g_queue_free(ctxt->stack); ctxt->stack =3D NULL; } @@ -566,6 +572,10 @@ QObject *json_parser_feed(JSONParserContext *ctxt, con= st JSONToken *token, Error =20 assert(!ctxt->err); switch (token->type) { + case JSON_ERROR: + parse_error(ctxt, token, "JSON parse error, stray '%s'", token->st= r); + break; + case JSON_END_OF_INPUT: /* Check for premature end of input */ if (!g_queue_is_empty(ctxt->stack)) { diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c index 6c93e6fd78d..a1210128ac1 100644 --- a/qobject/json-streamer.c +++ b/qobject/json-streamer.c @@ -1,5 +1,5 @@ /* - * JSON streaming support + * JSON parser - callback interface and error recovery * * Copyright IBM, Corp. 2009 * @@ -19,97 +19,73 @@ #define MAX_TOKEN_COUNT (2ULL << 20) #define MAX_NESTING (1 << 10) =20 -static void json_message_free_tokens(JSONMessageParser *parser) -{ - JSONToken *token; - - while ((token =3D g_queue_pop_head(&parser->tokens))) { - g_free(token); - } -} - void json_message_process_token(JSONLexer *lexer, GString *input, JSONTokenType type, int x, int y) { JSONMessageParser *parser =3D container_of(lexer, JSONMessageParser, l= exer); - JSONParserContext ctxt; - QObject *json =3D NULL; + g_autofree JSONToken *token =3D json_token(type, x, y, input); Error *err =3D NULL; - JSONToken *token; =20 + parser->token_size +=3D input->len; + parser->token_count++; + + /* Detect message boundaries for error recovery purposes. */ switch (type) { case JSON_LCURLY: parser->brace_count++; break; case JSON_RCURLY: - parser->brace_count--; + if (parser->brace_count > 0) { + parser->brace_count--; + } break; case JSON_LSQUARE: parser->bracket_count++; break; case JSON_RSQUARE: - parser->bracket_count--; - break; - case JSON_ERROR: - error_setg(&err, "JSON parse error, stray '%s'", input->str); - goto out_emit; - case JSON_END_OF_INPUT: - if (g_queue_is_empty(&parser->tokens)) { - return; + if (parser->bracket_count > 0) { + parser->bracket_count--; } break; default: break; } =20 - /* - * Security consideration, we limit total memory allocated per object - * and the maximum recursion depth that a message can force. - */ - if (parser->token_size + input->len + 1 > MAX_TOKEN_SIZE) { - error_setg(&err, "JSON token size limit exceeded"); - goto out_emit; - } - if (g_queue_get_length(&parser->tokens) + 1 > MAX_TOKEN_COUNT) { - error_setg(&err, "JSON token count limit exceeded"); - goto out_emit; - } - if (parser->bracket_count + parser->brace_count > MAX_NESTING) { - error_setg(&err, "JSON nesting depth limit exceeded"); - goto out_emit; - } + /* during error recovery eat tokens until parentheses balance */ + if (!parser->error) { + /* + * Security consideration, we limit total memory allocated per obj= ect + * and the maximum recursion depth that a message can force. + */ + if (parser->token_size > MAX_TOKEN_SIZE) { + error_setg(&err, "JSON token size limit exceeded"); + } else if (parser->token_count > MAX_TOKEN_COUNT) { + error_setg(&err, "JSON token count limit exceeded"); + } else if (parser->bracket_count + parser->brace_count > MAX_NESTI= NG) { + error_setg(&err, "JSON nesting depth limit exceeded"); + } else { + QObject *json =3D json_parser_feed(&parser->parser, token, &er= r); + if (json) { + parser->emit(parser->opaque, json, NULL); + } + } =20 - token =3D json_token(type, x, y, input); - parser->token_size +=3D input->len; - - g_queue_push_tail(&parser->tokens, token); - - if ((parser->brace_count > 0 || parser->bracket_count > 0) - && parser->brace_count >=3D 0 && parser->bracket_count >=3D 0 - && type !=3D JSON_END_OF_INPUT) { - return; - } - - json_parser_init(&ctxt, parser->ap); - - /* Process all tokens in the queue */ - while (!g_queue_is_empty(&parser->tokens)) { - token =3D g_queue_pop_head(&parser->tokens); - json =3D json_parser_feed(&ctxt, token, &err); - g_free(token); - if (json || err) { - break; + if (err) { + parser->emit(parser->opaque, NULL, err); + /* start recovery */ + parser->error =3D true; } } =20 - json_parser_destroy(&ctxt); - -out_emit: - parser->brace_count =3D 0; - parser->bracket_count =3D 0; - json_message_free_tokens(parser); - parser->token_size =3D 0; - parser->emit(parser->opaque, json, err); + if ((parser->brace_count =3D=3D 0 && parser->bracket_count =3D=3D 0) + || type =3D=3D JSON_END_OF_INPUT) { + parser->error =3D false; + parser->brace_count =3D 0; + parser->bracket_count =3D 0; + parser->token_count =3D 0; + parser->token_size =3D 0; + json_parser_reset(&parser->parser); + } } =20 void json_message_parser_init(JSONMessageParser *parser, @@ -119,12 +95,13 @@ void json_message_parser_init(JSONMessageParser *parse= r, { parser->emit =3D emit; parser->opaque =3D opaque; - parser->ap =3D ap; + parser->error =3D false; parser->brace_count =3D 0; parser->bracket_count =3D 0; - g_queue_init(&parser->tokens); + parser->token_count =3D 0; parser->token_size =3D 0; =20 + json_parser_init(&parser->parser, ap); json_lexer_init(&parser->lexer, !!ap); } =20 @@ -137,11 +114,10 @@ void json_message_parser_feed(JSONMessageParser *pars= er, void json_message_parser_flush(JSONMessageParser *parser) { json_lexer_flush(&parser->lexer); - assert(g_queue_is_empty(&parser->tokens)); } =20 void json_message_parser_destroy(JSONMessageParser *parser) { json_lexer_destroy(&parser->lexer); - json_message_free_tokens(parser); + json_parser_destroy(&parser->parser); } --=20 2.52.0 From nobody Mon Feb 9 04:09:14 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767775794; cv=none; d=zohomail.com; s=zohoarc; b=ns1aVnHzVJOSxPZaBE58SX11/b7pMC3cImDzn7sRtE2WxIN0Gk0RdnQSuIsxpyvDC+K3Lvhpc4zB8V9F/IXww6BF5RhpzylWcoe4L3+al1L+OW7nuQhYHo8lkHNnGrzoE5eyckJhq05oV+BdF8UKTFy02lWKJy8Vw7YvcTmHJdA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767775794; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=5po6tsrt4IQ0D/x9eF96zQfTjykL6tNnFFFoC5rONSQ=; b=YlvG4ItHGWCnIm9xUbwfFfYkM2GZ2gg44KB+P71wRTHqw8l5Z1pMbFMmiFBb9V7OufiC9WFLp9a7fE+68hfInP2QtVtY5UlgntkaoL2c887V0MYHnp6hayLvfTxjmQTsBlwUINNUFXBAOl23C5Ze5jT+Hv1K2Q1HuftqGZkV094= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767775794183241.2736472336611; Wed, 7 Jan 2026 00:49:54 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdPEH-0008KE-0s; Wed, 07 Jan 2026 03:49:21 -0500 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 1vdPDs-0007qW-H8 for qemu-devel@nongnu.org; Wed, 07 Jan 2026 03:48:56 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdPDq-0008HU-9B for qemu-devel@nongnu.org; Wed, 07 Jan 2026 03:48:56 -0500 Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-356-FZPFTHJ-OjaGdJ5-lG7PLg-1; Wed, 07 Jan 2026 03:48:52 -0500 Received: by mail-wm1-f72.google.com with SMTP id 5b1f17b1804b1-4779edba8f3so13748195e9.3 for ; Wed, 07 Jan 2026 00:48:52 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-47d86c6ff40sm15191825e9.2.2026.01.07.00.48.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 07 Jan 2026 00:48:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767775733; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=5po6tsrt4IQ0D/x9eF96zQfTjykL6tNnFFFoC5rONSQ=; b=OiS9jAS3X9W6Kdl7IPHWxhsQIiKxRTPyxPIdI8L4agETlGeeiYsLzdAN0gMExCoUIC8inf zFADMUIGAFe2sZmABMiwwgDaYtbNvstnDcXlNUQG8pL0IWU8rIWVvQoCIFGv+qjW8bBlyi 2KGVUkmudoLTMhr5WbG+9ZjNNaLweUI= X-MC-Unique: FZPFTHJ-OjaGdJ5-lG7PLg-1 X-Mimecast-MFC-AGG-ID: FZPFTHJ-OjaGdJ5-lG7PLg_1767775731 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767775730; x=1768380530; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=5po6tsrt4IQ0D/x9eF96zQfTjykL6tNnFFFoC5rONSQ=; b=fwsku4m0OLIl9ranVXIuh8hxgTNsAXbkv2pec31G8NbHqJI3B1+soGsF4N7eeQ65ty YrY1vQh7AzJoE2B/KOKlCELChY+eR1KiMuNLXOJowxi/KRO68tGLYBmvlfgD0hjmgEKo eQlOQWG3h98fpJvfq/akIMTkGbzKr0stzUAyO0XS3bwoXtQaL0ziNvJ2V3hxbDcrLjdH bbZYWWdu4JlVBEGM2pab47KBqZwrLfCGmBNu5N5FiUc1Co4hoKpHIdsO5N7PqPdzZ7rH f+FtnDTFqs5EhFGWrdlqW51HydFWLqcE8dNK6r3NcbcvOw1VaOUGU6RlXKfZcOt54jwc cCjw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767775730; x=1768380530; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=5po6tsrt4IQ0D/x9eF96zQfTjykL6tNnFFFoC5rONSQ=; b=sc8zk8nZVB5psNf25EYS8pQdgMjIPr41utBEjU4f0o5fI08B93f9oiZfud0LWZSAi8 epM1Zwt7UBNim930Blu8dCrSywszc2Ylr9DIuc4YefCSzz4PArb7PgBZxjKf1JugQRqs fSiN1RCg43i5feeLkp2Mx46gvsoDYFASCZ9IdUN1s7PTc9TK4XLjhX75zWakXbeKoojv 87kkxsCYTbzYHgTEpLlXfFKiuHLZ4Omh9Qd3M5oySH9nlH7VkrC8qpp8Io/5w4AkGAu+ FN9PvLzeEtipdb4S3uwDwGbCil3kvt5M3UNY1IHyz2DwU6uea5VtURiqgYEW8M3s67HP U0JQ== X-Gm-Message-State: AOJu0Yw6BqWnvVLJoZkcW9tNdxry8YqGT8x0kqQ305o4GlHjSqbUEv+v +kQnx8YOBNE9THBkNeFEN6W2pyFRB6kEe+D9dgpMinP1vAJka8U8fLy7XcoawQwAK4NYJxj5/5d cMSkgu42qkJbsoOH7Q2QrJaCUwsgLpNliccF65hwo74+rBOBP+ZyH6ZmUCbCHpmXwIcIutnyTz1 sODG38mJR6FvTg/mYxrS/trVnEE5ogvC/h7xtJ/9eU X-Gm-Gg: AY/fxX4kaab5dLu7N53nkNZFpbN4bndmFClt92e44jD8qNqB5ji/O6wkYPdjrBEoRxs eNvGkPLQo91QmlcsbiOJIpWHvGXJXJNQq5kjwNRGeMNxY8UDF71T2GTL3y0etM0FLcDlWLNYZfT eNRvhojL5Azc3zy/yHWVaCNIAhF/oeaKoCyTMxfkV/afefsYpALFISzIdXFIqhtiONaXVQ4Sbfm Ewr1mUs0eGanurn/MHoO0kPjVn4Nn4gXtUskd1OyR4Vz8tFd66myN+ehY9tpIKA5cyneLEXBoQU tRX2Ta/QmlLN2DO+GLzFLlfGzpyeCRsWedwwodRzd33/NBT5Wnptw550Wjutiio+o13x1Ywu3iP N7wBsN4CBLNGvVEGafEsWWunYV8Y2mXujkOq2m/eLx0TKA4cvjY6T3QpXs/BXu+Wj9FTbu4N8j9 j53QEfBAjxfUVBUA== X-Received: by 2002:a05:600c:1385:b0:477:9cdb:e337 with SMTP id 5b1f17b1804b1-47d84b0a929mr13878255e9.7.1767775730285; Wed, 07 Jan 2026 00:48:50 -0800 (PST) X-Google-Smtp-Source: AGHT+IF/RsxVjTpz3FafFQPpLvfcqF3PTXEKnv4NXhyKWXjD18PJUmNPcJM8hpJG4DFcpIfizDcUtA== X-Received: by 2002:a05:600c:1385:b0:477:9cdb:e337 with SMTP id 5b1f17b1804b1-47d84b0a929mr13877975e9.7.1767775729819; Wed, 07 Jan 2026 00:48:49 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com Subject: [PATCH 4/5] json-streamer: do not heap-allocate JSONToken Date: Wed, 7 Jan 2026 09:48:39 +0100 Message-ID: <20260107084840.150843-5-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260107084840.150843-1-pbonzini@redhat.com> References: <20260107084840.150843-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.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, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767775795859158500 Content-Type: text/plain; charset="utf-8" This is not needed with a push parser. Since it processes tokens immediately, the JSONToken can be created directly on the stack and does not need to copy the lexer's string data. Signed-off-by: Paolo Bonzini --- qobject/json-parser-int.h | 8 ++++++-- qobject/json-parser.c | 18 ------------------ qobject/json-streamer.c | 4 ++-- 3 files changed, 8 insertions(+), 22 deletions(-) diff --git a/qobject/json-parser-int.h b/qobject/json-parser-int.h index 1f435cb8eb2..5a6b5c9af90 100644 --- a/qobject/json-parser-int.h +++ b/qobject/json-parser-int.h @@ -35,7 +35,12 @@ typedef enum json_token_type { JSON_MAX =3D JSON_END_OF_INPUT } JSONTokenType; =20 -typedef struct JSONToken JSONToken; +typedef struct JSONToken { + JSONTokenType type; + int x; + int y; + char *str; +} JSONToken; =20 /* json-lexer.c */ void json_lexer_init(JSONLexer *lexer, bool enable_interpolation); @@ -48,7 +53,6 @@ void json_message_process_token(JSONLexer *lexer, GString= *input, JSONTokenType type, int x, int y); =20 /* json-parser.c */ -JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr); void json_parser_init(JSONParserContext *ctxt, va_list *ap); void json_parser_reset(JSONParserContext *ctxt); QObject *json_parser_feed(JSONParserContext *ctxt, const JSONToken *token,= Error **errp); diff --git a/qobject/json-parser.c b/qobject/json-parser.c index 7abdea4dacb..2fede59842f 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -24,13 +24,6 @@ #include "qobject/qstring.h" #include "json-parser-int.h" =20 -struct JSONToken { - JSONTokenType type; - int x; - int y; - char str[]; -}; - /* * Objects: { } | { members } * - Empty: { -> AFTER_LCURLY -> } @@ -529,17 +522,6 @@ static QObject *json_parser_parse_token(JSONParserCont= ext *ctxt, const JSONToken return NULL; } =20 -JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr) -{ - JSONToken *token =3D g_malloc(sizeof(JSONToken) + tokstr->len + 1); - - token->type =3D type; - memcpy(token->str, tokstr->str, tokstr->len); - token->str[tokstr->len] =3D 0; - token->x =3D x; - token->y =3D y; - return token; -} =20 void json_parser_reset(JSONParserContext *ctxt) { diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c index a1210128ac1..07e0ef51ce3 100644 --- a/qobject/json-streamer.c +++ b/qobject/json-streamer.c @@ -23,7 +23,7 @@ void json_message_process_token(JSONLexer *lexer, GString= *input, JSONTokenType type, int x, int y) { JSONMessageParser *parser =3D container_of(lexer, JSONMessageParser, l= exer); - g_autofree JSONToken *token =3D json_token(type, x, y, input); + JSONToken token =3D (JSONToken) { .type =3D type, .x =3D x, .y =3D y, = .str =3D input->str }; Error *err =3D NULL; =20 parser->token_size +=3D input->len; @@ -64,7 +64,7 @@ void json_message_process_token(JSONLexer *lexer, GString= *input, } else if (parser->bracket_count + parser->brace_count > MAX_NESTI= NG) { error_setg(&err, "JSON nesting depth limit exceeded"); } else { - QObject *json =3D json_parser_feed(&parser->parser, token, &er= r); + QObject *json =3D json_parser_feed(&parser->parser, &token, &e= rr); if (json) { parser->emit(parser->opaque, json, NULL); } --=20 2.52.0 From nobody Mon Feb 9 04:09:14 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767775815; cv=none; d=zohomail.com; s=zohoarc; b=ZCCFI3JAjN/+QBV2fhUQCXzaSxemO3r1fGM2Dutnv4o7E1dCmmcQWEv9Adwalo3qz1EDl9657U+inCCVfBSOEvzxedDzHSanqUvLArz5dgMsl9lEOnAnvKG4JZUDQAEBiDeGo07BjoCNZA/QzaC68qnKM5fZRI1NM7uakUfC0Zk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767775815; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=XU7tVc2WcpOXgLVF21ZjZzEa8fYwcstCpt/1TetGoNM=; b=kDiQTvdEIPYxdTdtbKHsiZqKMIRAFfh6Ee2d2BakW0EG73iQDnufHzF/MEpQjBhkMiMmkTjG20+sgp6JGctr/d6Htm7T/wzan1HI+9ELCv9DFmegKTQxzdb4/vn96F4yZsHm8iT3l3E6RkVC+DvzPg3FmiOrSyxYNqB1sQcq4wk= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767775815369466.0314907346259; Wed, 7 Jan 2026 00:50:15 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdPEL-0000Ec-57; Wed, 07 Jan 2026 03:49:25 -0500 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 1vdPDw-0007rT-FU for qemu-devel@nongnu.org; Wed, 07 Jan 2026 03:49:08 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdPDu-0008I1-8P for qemu-devel@nongnu.org; Wed, 07 Jan 2026 03:48:59 -0500 Received: from mail-wr1-f69.google.com (mail-wr1-f69.google.com [209.85.221.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-381-BXgJPKNQPJC0NDsJ-aUvmw-1; Wed, 07 Jan 2026 03:48:55 -0500 Received: by mail-wr1-f69.google.com with SMTP id ffacd0b85a97d-4325aa61c6bso946272f8f.0 for ; Wed, 07 Jan 2026 00:48:54 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-432bd0e17aasm9149922f8f.15.2026.01.07.00.48.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 07 Jan 2026 00:48:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767775736; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=XU7tVc2WcpOXgLVF21ZjZzEa8fYwcstCpt/1TetGoNM=; b=QIucR3bwXS8doC+YESujRxlHXc/KASH45sQkIoKpaN/7HJ6nRoROKvQfOXT5TuxUohmO7e 2X0/WBMjnM7hn7hs8BSREBle3S1Rnvs/4zHm4w8OW4W/cudt9f52XC5ciEMq20Ui3fdKMV OfUvrvw4gqXos//1BpIVWLqcVxT7EiY= X-MC-Unique: BXgJPKNQPJC0NDsJ-aUvmw-1 X-Mimecast-MFC-AGG-ID: BXgJPKNQPJC0NDsJ-aUvmw_1767775734 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767775733; x=1768380533; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=XU7tVc2WcpOXgLVF21ZjZzEa8fYwcstCpt/1TetGoNM=; b=K59VQd72KVscep1g9oBQX9sDagBzaqyMQ8MFJZFuUrp0hQxHkcHrTQye7PO7y81kWi UU5YMi8Ol9XBDW/Ha3/C1WghZj6jLqVG/w4kRBGzJHatW3hdUosEnk/QFUkDYeevyR+N 1GgQr5AmIz2u+d1CArXdHjncgwHbv+11HCVi5cwKA1Nl5bUEa/kBjhz09uTw0ZDuXhzR KzmkRsRmeOxZI1YJUg3T/eG7hvtxtR//eVex+zWLqV4rU63jUwedbm7F7WaSv2A3zhOi LJJ3yfsgTvQSII+DtgT9oidUtfVo1CygM+zI3golHZ3Vzqe0y2g1QvZ9le+WRThIaShf XKWQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767775733; x=1768380533; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=XU7tVc2WcpOXgLVF21ZjZzEa8fYwcstCpt/1TetGoNM=; b=ttDY+GMqIOFUlGBU9sg2a8EPm0WyeO1TOd2x0jr7wzVaKjrY8owBfLEU7nOBcuHM2t cpglRRKhROKGPpbTD3Jje94qBIKBXjs6CbzBmQxjTZ4ANLLRw6RCtl/D0wIm0xq3X3fk 8FqM0DdWaWQ4etLQyEzfkQAfKuuax3+2grETVonRfxPl6r2/0QHcPiM6TpyfDunmWEm+ GAFgC5NnFCKb1v9TqiDSnaS+8U0B0zF/vPt2XDQ6GedarF6LOA4FsJe97JEKomV0ZqqS CvC0C07FjKt2CNuNJ7/bLTCMe/b1i/zVQVoEHxArBrav8aehYDJzUqzh+7UNOO8W3Ly5 skHg== X-Gm-Message-State: AOJu0YwTmFVHJLt8DgSCrtAR4G/8f4p3t3uqpXbiJj9EUPis0ZDmkq6Y 6yzxv8WYmwZ3GofW847J2aEkLKk0JqvJbDyx/MxQoTEZ+M1gGBC8u3hnjflEO6c9/cLBU3Tv3wC 0oUeR6IdnT9sP6W93vuSasFnwJA2t8iB6p2ZdgMoWhdrstkMeGv2CMzQxAmz8mPiloS/4qjGBjQ YMTPznzpfnrO81t9mNunzukYjSY4M6Z2v3vOWMYWHz X-Gm-Gg: AY/fxX6R7wEpDznFePEzYsr2XFjH2MsGMu1yi+aC62IZ7Duss68nu9QDY3gLWFswAYI TVy7HbNCch/JwGqBYixnwsGMHQW2ncGium66sp7i/qvFkxFYuA8cUXSf9tRwATVP4pE7izX/xZ5 63hDuEjBEAFI8TS22+/PfvyAtJ9EHFGD1kSyXqNZnEzrnejrCQbvsD8/erq960B6fBwlnM3ff2K aRdd+WnHaIlRI+r3IMBIsFeZiKHWTyNS0Hbf0tkm8Jf5sbaekXDyU8iynu4S/Slqet0lfgSZ/EX 4y6B5wZLyf+ockw6mdYJinLCcpMNxEsbCBj8CaaWjAOqSx3+wriuccMtr2Zom7F9pedgZ3bLhPn xBESDr4HlAk0/ODYv2ozrgnE1s6uqumNKg12MmVhqpFwiOlGLWPawWdktBUJBb8HSCM3VKfQw5f MzWiEj91K9Kzj1eg== X-Received: by 2002:a05:6000:2384:b0:431:397:4c45 with SMTP id ffacd0b85a97d-432c37767c2mr1776207f8f.59.1767775732958; Wed, 07 Jan 2026 00:48:52 -0800 (PST) X-Google-Smtp-Source: AGHT+IGzk6N38h+EaptIec2/gjAdE2wtBTvDB+r/MM1rScsl+hqkq4x3IOr0EHRWwkjLNkwHP0sw+Q== X-Received: by 2002:a05:6000:2384:b0:431:397:4c45 with SMTP id ffacd0b85a97d-432c37767c2mr1776173f8f.59.1767775732372; Wed, 07 Jan 2026 00:48:52 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com Subject: [PATCH 5/5] json-parser: add location to JSON parsing errors Date: Wed, 7 Jan 2026 09:48:40 +0100 Message-ID: <20260107084840.150843-6-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260107084840.150843-1-pbonzini@redhat.com> References: <20260107084840.150843-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.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, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767775815849158500 Content-Type: text/plain; charset="utf-8" Now that all calls to parse_error have a token, add the line and column to the message. As far as I can see the two important TODOs (better errors and better EOI handling) are done, and the others (token range information and "parsed size"?) do not really matter or are handled better by json-streamer.c. So remove the list, which had sat unchanged since 2009. Note however that right now the x and y are those of the *last* character in the token. Modify json-lexer.c to freeze tok->x and tok->y at the first character added to the GString. Signed-off-by: Paolo Bonzini --- include/qobject/json-parser.h | 1 + qobject/json-lexer.c | 11 +++++++---- qobject/json-parser.c | 12 ++---------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/include/qobject/json-parser.h b/include/qobject/json-parser.h index 923eb74ca00..98fe1371a85 100644 --- a/include/qobject/json-parser.h +++ b/include/qobject/json-parser.h @@ -17,6 +17,7 @@ typedef struct JSONLexer { int start_state, state; GString *token; + int cur_x, cur_y; int x, y; } JSONLexer; =20 diff --git a/qobject/json-lexer.c b/qobject/json-lexer.c index 51341d96e49..7753ba6c092 100644 --- a/qobject/json-lexer.c +++ b/qobject/json-lexer.c @@ -277,7 +277,8 @@ void json_lexer_init(JSONLexer *lexer, bool enable_inte= rpolation) lexer->start_state =3D lexer->state =3D enable_interpolation ? IN_START_INTERP : IN_START; lexer->token =3D g_string_sized_new(3); - lexer->x =3D lexer->y =3D 0; + lexer->cur_x =3D lexer->cur_y =3D 1; + lexer->x =3D lexer->y =3D 1; } =20 static void json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) @@ -285,10 +286,10 @@ static void json_lexer_feed_char(JSONLexer *lexer, ch= ar ch, bool flush) int new_state; bool char_consumed =3D false; =20 - lexer->x++; + lexer->cur_x++; if (ch =3D=3D '\n') { - lexer->x =3D 0; - lexer->y++; + lexer->cur_x =3D 1; + lexer->cur_y++; } =20 while (flush ? lexer->state !=3D lexer->start_state : !char_consumed) { @@ -316,6 +317,8 @@ static void json_lexer_feed_char(JSONLexer *lexer, char= ch, bool flush) case IN_START: g_string_truncate(lexer->token, 0); new_state =3D lexer->start_state; + lexer->x =3D lexer->cur_x; + lexer->y =3D lexer->cur_y; break; case JSON_ERROR: json_message_process_token(lexer, lexer->token, JSON_ERROR, diff --git a/qobject/json-parser.c b/qobject/json-parser.c index 2fede59842f..93b737511d1 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -57,15 +57,6 @@ typedef struct JSONParserStackEntry { =20 #define BUG_ON(cond) assert(!(cond)) =20 -/** - * TODO - * - * 0) make errors meaningful again - * 1) add geometry information to tokens - * 3) should we return a parsed size? - * 4) deal with premature EOI - */ - static inline JSONParserStackEntry *current_entry(JSONParserContext *ctxt) { return g_queue_peek_tail(ctxt->stack); @@ -105,7 +96,8 @@ static void G_GNUC_PRINTF(3, 4) parse_error(JSONParserCo= ntext *ctxt, va_start(ap, msg); vsnprintf(message, sizeof(message), msg, ap); va_end(ap); - error_setg(&ctxt->err, "JSON parse error, %s", message); + error_setg(&ctxt->err, "JSON parse error at line %d, column %d, %s", + token->y, token->x, message); } =20 static int cvt4hex(const char *s) --=20 2.52.0