From patchwork Fri Oct 31 06:54:14 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 668 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1761893781; bh=k+MT8OgHsSA76FCQZwX54c51xkPJ9vLINqoBXWWNCC4=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=MSOMO0cr2KzXjfVvBql15HfN4kW3hKZgs2zJAgva0DT/Todqz/S1HUeT8UH0Xgocj 2SUYwME228r+bP1O0HzILYA1LbODOAkhxUVRJ5AYhEcupn2Bku+YYHZHxfNra/JrEq qvtmstdxCZ7JagJGNIDCND1BXeeauWfwxyR7yc3JvihrXn1nmm0LIQ0RC8xexBWYfO 6FYTKuWAQixtIcx/0SA5TjndPAeNPLRAvuRYZ5AcWdjPgythO/bK+2Hv/5wcD7fOgT c2lweWiI71kJ0rWp67OtERuPA1o6/xA7Rxp1b9MJBcNijOVKTRMB2fxk7+5LDxahPB vMXqyMgChUlkQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id A2D3E682E9 for ; Fri, 31 Oct 2025 00:56:21 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id XY-ZZ0NyUAbs for ; Fri, 31 Oct 2025 00:56:21 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1761893779; bh=k+MT8OgHsSA76FCQZwX54c51xkPJ9vLINqoBXWWNCC4=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=SuGSgUmqJ2Pq5iOL3qkXj8b62fQ6ZTIpUx2vxRpyIdXnioqO6Lgj05AehqT2sf28y PpLYjrAdTygTBwpbIeE4qQVyYk8I82zZnYkZTILEtWrW7Wmh+H4HOXCrNleYFPvs7t rJDSuhturNpS0AH4H3JIyR3vLtAZjlFeLnkZxWIylpIV2ag5RO+c+c3gT98gmNu8ZR e518GMHi9alR21aoZIQOBggcszdEBHrZV/NIlEvvWFtA0tcVYpPshb9zqutu+vfen+ B4yLC6MMcSPPt+aGquYBVfjY+W+BlZAhmqGhWbx3ia2do+JoQn1mMBNl7z/1igQjRB Mgdxehj90WxZQ== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 9F5EA682CD for ; Fri, 31 Oct 2025 00:56:19 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1761893777; bh=p9DtwHLKYZi74v5LYbUHWTNZh6/bFCbT52dmOPY1mLg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=vE4soZDuaC4wpBgIcwz6YuOt71uSrbkXMLLGe7dlM6E7Xst0HLvY08Cx46Oe7vFCi SYGkFhcHTsFJTJ5XQ2Ik/cQnJo6ggZpeooy98BKYJAc0bn0QTSFN0kuCrXl3B0GyIV R8/hNnDmXCennVJ9nHhufIqjG1PwmQzquTqo7U2inrZzW5P+xtZtDbpQWur3FbTMrK ek1bIsOTa+0QhKzmDPp9cSCUw651mklBrol2sGQC74OSxd5nsdqtmqBzDRSckH+QdL d4Vp0PRakfdK022lCz36Qx1SQGH+GUyUPG//aI8cEWfhv/JriEdCCM67mpvkD3OC4N f2geVzEPx99qQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id BC5DA682CD; Fri, 31 Oct 2025 00:56:17 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id 0DTVVb_Vfo6Q; Fri, 31 Oct 2025 00:56:17 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1761893777; bh=GfBq2S82sWZ7MIWUGCkSq/K9HjyyFw6Mb1hMTgy7ooM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=b0L/WvgfuUf5/GsV9WYs6J9J5fTnowUCDijYL3fdACqEm15szCD5nbnLBsjNdGvyn 1aIOkuhYEMLvQhidJySwHRSwzAMAs7cuf9acGfYm1F5yDIiEfmfj+mo467HcoEa1L2 fFGkSSUNrHfFvdZKSXiCFtMqQGiDA47ldh0K8UTYo2DgR0duNeaw6rpazeF08kvtJC KtaeD6NSe/UUjbhuFLLqOyovGsmTrvhUQhA0d2YAu/lIN4nhtbtDUnjlx6RnbgeouK ayJZYYzI+ob8aT5DZr6nMToBGwswDRWT/BI2EDBi4GNIcbJ/MmaYra4ljY6a5QPQIn oj5Us1dw9t58Q== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 0035C5FBA9; Fri, 31 Oct 2025 00:56:16 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 31 Oct 2025 00:54:14 -0600 Message-ID: <20251031065439.3251464-19-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251031065439.3251464-1-sjg@u-boot.org> References: <20251031065439.3251464-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: N5XACBT2ULX5SYY7SZZMJ4MAPQGINTEQ X-Message-ID-Hash: N5XACBT2ULX5SYY7SZZMJ4MAPQGINTEQ X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass , Claude X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 18/24] luks: Create a very simple JSON library List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass LUKS version 2 uses JSON as a means of communicating the key information. Add a simple library which can print JSON in a human-readable format. Note that it does not fully parse the JSON fragment. That may be considered later, if needed. Co-developed-by: Claude Signed-off-by: Simon Glass --- MAINTAINERS | 3 + include/json.h | 23 +++++ lib/Kconfig | 7 ++ lib/Makefile | 1 + lib/json.c | 122 +++++++++++++++++++++++++++ test/lib/Makefile | 1 + test/lib/json.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 368 insertions(+) create mode 100644 include/json.h create mode 100644 lib/json.c create mode 100644 test/lib/json.c diff --git a/MAINTAINERS b/MAINTAINERS index 9b00829db93..a0374df1c4d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1297,7 +1297,10 @@ F: cmd/luks.c F: doc/usage/cmd/luks.rst F: drivers/block/luks.c F: include/luks.h +F: include/json.h +F: lib/json.c F: test/boot/luks.c +F: test/lib/json.c MALI DISPLAY PROCESSORS M: Liviu Dudau diff --git a/include/json.h b/include/json.h new file mode 100644 index 00000000000..4d925e9db36 --- /dev/null +++ b/include/json.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * JSON utilities + * + * Copyright (C) 2025 Canonical Ltd + * Written by Simon Glass + */ + +#ifndef __JSON_H__ +#define __JSON_H__ + +/** + * json_print_pretty() - Print JSON with indentation + * + * This function takes a JSON string and prints it with proper indentation, + * making it more human-readable. It handles nested objects and arrays. + * + * @json: JSON string to print (may be nul terminated before @len) + * @len: Length of JSON string + */ +void json_print_pretty(const char *json, int len); + +#endif /* __JSON_H__ */ diff --git a/lib/Kconfig b/lib/Kconfig index 9de4667731e..c8bf4b4b049 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -976,6 +976,13 @@ config GETOPT help This enables functions for parsing command-line options. +config JSON + bool "Enable JSON parsing and printing" + help + This enables JSON (JavaScript Object Notation) parsing and pretty- + printing functions. JSON is used for structured data representation, + such as LUKS2 metadata. + config OF_LIBFDT bool "Enable the FDT library" default y if OF_CONTROL diff --git a/lib/Makefile b/lib/Makefile index 43df5733ffd..71c9c0d1766 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -39,6 +39,7 @@ endif obj-y += crc8.o obj-$(CONFIG_ERRNO_STR) += errno_str.o +obj-$(CONFIG_JSON) += json.o obj-$(CONFIG_FIT) += fdtdec_common.o obj-$(CONFIG_TEST_FDTDEC) += fdtdec_test.o obj-$(CONFIG_GZIP_COMPRESSED) += gzip.o diff --git a/lib/json.c b/lib/json.c new file mode 100644 index 00000000000..8cce042d631 --- /dev/null +++ b/lib/json.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * JSON pretty-printer + * + * Copyright (C) 2025 Canonical Ltd + * Written by Simon Glass + */ + +#include +#include + +/** + * print_indent() - Print indentation spaces + * + * @indent: Indentation level (each level is 2 spaces) + */ +static void print_indent(int indent) +{ + for (int i = 0; i < indent * 2; i++) + putc(' '); +} + +void json_print_pretty(const char *json, int len) +{ + int indent = 0; + bool in_string = false; + bool escaped = false; + bool after_open = false; + int i; + + for (i = 0; i < len && json[i]; i++) { + char c = json[i]; + + /* Handle escape sequences */ + if (escaped) { + putc(c); + escaped = false; + continue; + } + + if (c == '\\') { + putc(c); + escaped = true; + continue; + } + + /* Track whether we're inside a string */ + if (c == '"') { + in_string = !in_string; + if (after_open) { + print_indent(indent); + after_open = false; + } + putc(c); + continue; + } + + /* Don't format inside strings */ + if (in_string) { + putc(c); + continue; + } + + /* Format structural characters */ + switch (c) { + case '{': + case '[': + if (after_open) { + print_indent(indent); + after_open = false; + } + putc(c); + putc('\n'); + indent++; + after_open = true; + break; + + case '}': + case ']': + if (!after_open) { + putc('\n'); + indent--; + print_indent(indent); + } else { + indent--; + } + putc(c); + after_open = false; + break; + + case ',': + putc(c); + putc('\n'); + print_indent(indent); + after_open = false; + break; + + case ':': + putc(c); + putc(' '); + after_open = false; + break; + + case ' ': + case '\t': + case '\n': + case '\r': + /* Skip whitespace outside strings */ + break; + + default: + if (after_open) { + print_indent(indent); + after_open = false; + } + putc(c); + break; + } + } + + putc('\n'); +} diff --git a/test/lib/Makefile b/test/lib/Makefile index 5c89421918b..1d94d6604d5 100644 --- a/test/lib/Makefile +++ b/test/lib/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_EFI_LOADER) += efi_device_path.o obj-$(CONFIG_EFI_SECURE_BOOT) += efi_image_region.o obj-$(CONFIG_EFI_LOG) += efi_log.o obj-y += hexdump.o +obj-$(CONFIG_JSON) += json.o obj-$(CONFIG_SANDBOX) += kconfig.o obj-y += lmb.o obj-$(CONFIG_HAVE_SETJMP) += longjmp.o diff --git a/test/lib/json.c b/test/lib/json.c new file mode 100644 index 00000000000..76afeb9b241 --- /dev/null +++ b/test/lib/json.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for JSON pretty-printer + * + * Copyright (C) 2025 Canonical Ltd + * Written by Simon Glass + */ + +#include +#include +#include +#include + +static int lib_test_json_simple_object(struct unit_test_state *uts) +{ + const char *json = "{\"name\":\"value\"}"; + + json_print_pretty(json, strlen(json)); + ut_assert_nextline("{"); + ut_assert_nextline(" \"name\": \"value\""); + ut_assert_nextline("}"); + ut_assert_console_end(); + + return 0; +} +LIB_TEST(lib_test_json_simple_object, UTF_CONSOLE); + +static int lib_test_json_simple_array(struct unit_test_state *uts) +{ + const char *json = "[1,2,3]"; + + json_print_pretty(json, strlen(json)); + ut_assert_nextline("["); + ut_assert_nextline(" 1,"); + ut_assert_nextline(" 2,"); + ut_assert_nextline(" 3"); + ut_assert_nextline("]"); + ut_assert_console_end(); + + return 0; +} +LIB_TEST(lib_test_json_simple_array, UTF_CONSOLE); + +static int lib_test_json_nested_object(struct unit_test_state *uts) +{ + const char *json = "{\"outer\":{\"inner\":\"value\"}}"; + + json_print_pretty(json, strlen(json)); + ut_assert_nextline("{"); + ut_assert_nextline(" \"outer\": {"); + ut_assert_nextline(" \"inner\": \"value\""); + ut_assert_nextline(" }"); + ut_assert_nextline("}"); + ut_assert_console_end(); + + return 0; +} +LIB_TEST(lib_test_json_nested_object, UTF_CONSOLE); + +static int lib_test_json_nested_array(struct unit_test_state *uts) +{ + const char *json = "[[1,2],[3,4]]"; + + json_print_pretty(json, strlen(json)); + ut_assert_nextline("["); + ut_assert_nextline(" ["); + ut_assert_nextline(" 1,"); + ut_assert_nextline(" 2"); + ut_assert_nextline(" ],"); + ut_assert_nextline(" ["); + ut_assert_nextline(" 3,"); + ut_assert_nextline(" 4"); + ut_assert_nextline(" ]"); + ut_assert_nextline("]"); + ut_assert_console_end(); + + return 0; +} +LIB_TEST(lib_test_json_nested_array, UTF_CONSOLE); + +static int lib_test_json_mixed_nested(struct unit_test_state *uts) +{ + const char *json = "{\"array\":[1,{\"nested\":\"obj\"}]}"; + + json_print_pretty(json, strlen(json)); + ut_assert_nextline("{"); + ut_assert_nextline(" \"array\": ["); + ut_assert_nextline(" 1,"); + ut_assert_nextline(" {"); + ut_assert_nextline(" \"nested\": \"obj\""); + ut_assert_nextline(" }"); + ut_assert_nextline(" ]"); + ut_assert_nextline("}"); + ut_assert_console_end(); + + return 0; +} +LIB_TEST(lib_test_json_mixed_nested, UTF_CONSOLE); + +static int lib_test_json_string_with_colon(struct unit_test_state *uts) +{ + const char *json = "{\"url\":\"http://example.com\"}"; + + json_print_pretty(json, strlen(json)); + ut_assert_nextline("{"); + ut_assert_nextline(" \"url\": \"http://example.com\""); + ut_assert_nextline("}"); + ut_assert_console_end(); + + return 0; +} +LIB_TEST(lib_test_json_string_with_colon, UTF_CONSOLE); + +static int lib_test_json_string_with_comma(struct unit_test_state *uts) +{ + const char *json = "{\"name\":\"last, first\"}"; + + json_print_pretty(json, strlen(json)); + ut_assert_nextline("{"); + ut_assert_nextline(" \"name\": \"last, first\""); + ut_assert_nextline("}"); + ut_assert_console_end(); + + return 0; +} +LIB_TEST(lib_test_json_string_with_comma, UTF_CONSOLE); + +static int lib_test_json_string_with_braces(struct unit_test_state *uts) +{ + const char *json = "{\"text\":\"some {braces} here\"}"; + + json_print_pretty(json, strlen(json)); + ut_assert_nextline("{"); + ut_assert_nextline(" \"text\": \"some {braces} here\""); + ut_assert_nextline("}"); + ut_assert_console_end(); + + return 0; +} +LIB_TEST(lib_test_json_string_with_braces, UTF_CONSOLE); + +static int lib_test_json_escaped_quote(struct unit_test_state *uts) +{ + const char *json = "{\"quote\":\"He said \\\"hello\\\"\"}"; + + json_print_pretty(json, strlen(json)); + ut_assert_nextline("{"); + ut_assert_nextline(" \"quote\": \"He said \\\"hello\\\"\""); + ut_assert_nextline("}"); + ut_assert_console_end(); + + return 0; +} +LIB_TEST(lib_test_json_escaped_quote, UTF_CONSOLE); + +static int lib_test_json_multiple_fields(struct unit_test_state *uts) +{ + const char *json = "{\"name\":\"test\",\"age\":25,\"active\":true}"; + + json_print_pretty(json, strlen(json)); + ut_assert_nextline("{"); + ut_assert_nextline(" \"name\": \"test\","); + ut_assert_nextline(" \"age\": 25,"); + ut_assert_nextline(" \"active\": true"); + ut_assert_nextline("}"); + ut_assert_console_end(); + + return 0; +} +LIB_TEST(lib_test_json_multiple_fields, UTF_CONSOLE); + +static int lib_test_json_empty_object(struct unit_test_state *uts) +{ + const char *json = "{}"; + + json_print_pretty(json, strlen(json)); + ut_assert_nextline("{"); + ut_assert_nextline("}"); + ut_assert_console_end(); + + return 0; +} +LIB_TEST(lib_test_json_empty_object, UTF_CONSOLE); + +static int lib_test_json_empty_array(struct unit_test_state *uts) +{ + const char *json = "[]"; + + json_print_pretty(json, strlen(json)); + ut_assert_nextline("["); + ut_assert_nextline("]"); + ut_assert_console_end(); + + return 0; +} +LIB_TEST(lib_test_json_empty_array, UTF_CONSOLE); + +static int lib_test_json_whitespace(struct unit_test_state *uts) +{ + const char *json = "{ \"name\" : \"value\" , \"num\" : 42 }"; + + json_print_pretty(json, strlen(json)); + ut_assert_nextline("{"); + ut_assert_nextline(" \"name\": \"value\","); + ut_assert_nextline(" \"num\": 42"); + ut_assert_nextline("}"); + ut_assert_console_end(); + + return 0; +} +LIB_TEST(lib_test_json_whitespace, UTF_CONSOLE);