From 689ac4b27c0c3bdc12ef7cadc4922755e4419714 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Tue, 14 Apr 2026 13:45:38 +0000 Subject: [PATCH 1/6] sources: centralize JSON library references into json_api.py Move all YAJL-specific C code fragments (type names, macros, function calls) from sources.py and headers.py into a new json_api.py module. This makes it possible to switch the underlying JSON library by modifying json_api.py only, without touching the rest of the generator. The generated C code is byte-for-byte identical. Co-Authored-By: Claude Opus 4.6 Signed-off-by: Giuseppe Scrivano --- Makefile.am | 1 + src/ocispec/headers.py | 9 +- src/ocispec/json_api.py | 234 +++++++++++++++++++++++++ src/ocispec/sources.py | 371 +++++++++++++++++++--------------------- 4 files changed, 420 insertions(+), 195 deletions(-) create mode 100644 src/ocispec/json_api.py diff --git a/Makefile.am b/Makefile.am index c69fa2a3..36799002 100644 --- a/Makefile.am +++ b/Makefile.am @@ -273,6 +273,7 @@ EXTRA_DIST = autogen.sh \ src/ocispec/generate.py \ src/ocispec/headers.py \ src/ocispec/helpers.py \ + src/ocispec/json_api.py \ src/ocispec/sources.py \ $(HEADER_FILES) \ src/ocispec/read-file.h \ diff --git a/src/ocispec/headers.py b/src/ocispec/headers.py index cb1fc147..6efe2b0c 100755 --- a/src/ocispec/headers.py +++ b/src/ocispec/headers.py @@ -27,6 +27,7 @@ # libocispec output files to be licensed under the GNU General Public # License without this special exception. import helpers +import json_api def append_header_arr(obj, header, prefix): ''' @@ -60,7 +61,7 @@ def append_header_arr(obj, header, prefix): typename = helpers.get_name_substr(obj.name, prefix) header.append(f"}}\n{typename};\n\n") header.append(f"void free_{typename} ({typename} *ptr);\n\n") - header.append(f"{typename} *make_{typename} (yajl_val tree, const struct parser_context *ctx, parser_error *err);\n\n") + header.append(f"{typename} *make_{typename} ({json_api.VAL_TYPE} tree, const struct parser_context *ctx, parser_error *err);\n\n") def append_header_map_str_obj(obj, header, prefix): @@ -164,7 +165,7 @@ def append_type_c_header(obj, header, prefix): else: append_header_child_others(i, header, prefix) if obj.children is not None: - header.append(" yajl_val _residual;\n") + header.append(f" {json_api.VAL_TYPE} _residual;\n") if len(present_tags) > 0: header.append("\n") for tag in present_tags: @@ -173,8 +174,8 @@ def append_type_c_header(obj, header, prefix): header.append(f"}}\n{typename};\n\n") header.append(f"void free_{typename} ({typename} *ptr);\n\n") header.append(f"{typename} *clone_{typename} ({typename} *src);\n") - header.append(f"{typename} *make_{typename} (yajl_val tree, const struct parser_context *ctx, parser_error *err);\n\n") - header.append(f"yajl_gen_status gen_{typename} (yajl_gen g, const {typename} *ptr, const struct parser_context *ctx, parser_error *err);\n\n") + header.append(f"{typename} *make_{typename} ({json_api.VAL_TYPE} tree, const struct parser_context *ctx, parser_error *err);\n\n") + header.append(f"{json_api.GEN_STATUS_TYPE} gen_{typename} ({json_api.GEN_TYPE} g, const {typename} *ptr, const struct parser_context *ctx, parser_error *err);\n\n") def header_reflect_top_array(obj, prefix, header): c_typ = helpers.get_prefixed_pointer(obj.name, obj.subtyp, prefix) or \ diff --git a/src/ocispec/json_api.py b/src/ocispec/json_api.py new file mode 100644 index 00000000..88c2d29a --- /dev/null +++ b/src/ocispec/json_api.py @@ -0,0 +1,234 @@ +# -*- coding: utf-8 -*- +# +# libocispec - a C library for parsing OCI spec files. +# +# Copyright (C) 2017, 2019 Giuseppe Scrivano +# libocispec is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# libocispec is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with libocispec. If not, see . +# +# As a special exception, you may create a larger work that contains +# part or all of the libocispec parser skeleton and distribute that work +# under terms of your choice, so long as that work isn't itself a +# parser generator using the skeleton or a modified version thereof +# as a parser skeleton. Alternatively, if you modify or redistribute +# the parser skeleton itself, you may (at your option) remove this +# special exception, which will cause the skeleton and the resulting +# libocispec output files to be licensed under the GNU General Public +# License without this special exception. + +""" +JSON library abstraction for C code generation. + +All JSON library-specific C code fragments are centralized in this module. +To switch to a different JSON library, modify this module only -- the rest +of the generator uses these names and helpers exclusively. +""" + +# --------------------------------------------------------------------------- +# C type names +# --------------------------------------------------------------------------- +VAL_TYPE = "yajl_val" +GEN_TYPE = "yajl_gen" +GEN_STATUS_TYPE = "yajl_gen_status" +GEN_STATUS_OK = "yajl_gen_status_ok" + +# --------------------------------------------------------------------------- +# Type constants passed to get_val() +# --------------------------------------------------------------------------- +TYPE_STRING = "yajl_t_string" +TYPE_NUMBER = "yajl_t_number" +TYPE_OBJECT = "yajl_t_object" +TYPE_ARRAY = "yajl_t_array" +TYPE_TRUE = "yajl_t_true" +TYPE_FALSE = "yajl_t_false" + +# --------------------------------------------------------------------------- +# Value extraction -- each returns a C expression string +# --------------------------------------------------------------------------- + +def get_string(val): + return f"YAJL_GET_STRING ({val})" + +def get_number(val): + return f"YAJL_GET_NUMBER ({val})" + +def is_number(val): + return f"YAJL_IS_NUMBER ({val})" + +def is_true(val): + return f"YAJL_IS_TRUE ({val})" + +# --------------------------------------------------------------------------- +# Array access -- each returns a C expression string +# --------------------------------------------------------------------------- + +def array_check(val): + return f"YAJL_GET_ARRAY ({val}) != NULL" + +def array_len(val): + return f"YAJL_GET_ARRAY_NO_CHECK ({val})->len" + +def array_get(val, idx): + return f"YAJL_GET_ARRAY_NO_CHECK ({val})->values[{idx}]" + +def array_values(val): + return f"YAJL_GET_ARRAY_NO_CHECK ({val})->values" + +# --------------------------------------------------------------------------- +# Object access -- each returns a C expression string +# --------------------------------------------------------------------------- + +def object_check(val): + return f"YAJL_GET_OBJECT ({val}) != NULL" + +def object_len(val): + return f"YAJL_GET_OBJECT_NO_CHECK ({val})->len" + +def object_keys(val): + return f"YAJL_GET_OBJECT_NO_CHECK ({val})->keys" + +def object_values(val): + return f"YAJL_GET_OBJECT_NO_CHECK ({val})->values" + +# --------------------------------------------------------------------------- +# Internal structure access (for residual handling) +# --------------------------------------------------------------------------- + +def object_type_field(var): + return f"{var}->type" + +def object_type_value(): + return TYPE_OBJECT + +def set_object_type(var): + return f"{var}->type = {TYPE_OBJECT}" + +def object_keys_field(var): + return f"{var}->u.object.keys" + +def object_values_field(var): + return f"{var}->u.object.values" + +def alloc_object_keys(var, count): + return f"{var}->u.object.keys = calloc ({count}, sizeof (const char *))" + +def alloc_object_values(var, count): + return f"{var}->u.object.values = calloc ({count}, sizeof ({VAL_TYPE}))" + +def object_key_direct(var, idx): + return f"{var}->u.object.keys[{idx}]" + +def object_value_direct(var, idx): + return f"{var}->u.object.values[{idx}]" + +def object_len_field(var): + return f"{var}->u.object.len" + +# --------------------------------------------------------------------------- +# Generation -- each returns a C expression string +# --------------------------------------------------------------------------- + +def gen_string(gen, str_expr, len_expr): + return f"yajl_gen_string (({GEN_TYPE}) {gen}, (const unsigned char *)({str_expr}), {len_expr})" + +def gen_bool(gen, val): + return f"yajl_gen_bool (({GEN_TYPE}){gen}, (int)({val}))" + +def gen_double(gen, val): + return f"yajl_gen_double (({GEN_TYPE}){gen}, {val})" + +def gen_map_open(gen): + return f"yajl_gen_map_open (({GEN_TYPE}) {gen})" + +def gen_map_close(gen): + return f"yajl_gen_map_close (({GEN_TYPE}) {gen})" + +def gen_array_open(gen): + return f"yajl_gen_array_open (({GEN_TYPE}) {gen})" + +def gen_array_close(gen): + return f"yajl_gen_array_close (({GEN_TYPE}) {gen})" + +# --------------------------------------------------------------------------- +# Configuration +# --------------------------------------------------------------------------- +GEN_BEAUTIFY = "yajl_gen_beautify" + +def gen_config(gen, option, val): + return f"yajl_gen_config ({gen}, {option}, {val})" + +# --------------------------------------------------------------------------- +# Buffer access and cleanup +# --------------------------------------------------------------------------- + +def gen_get_buf(gen, buf_var, len_var): + return f"yajl_gen_get_buf ({gen}, {buf_var}, {len_var})" + +def gen_clear(gen): + return f"yajl_gen_clear ({gen})" + +def gen_free(gen): + return f"yajl_gen_free ({gen})" + +# --------------------------------------------------------------------------- +# Parsing and lifecycle +# --------------------------------------------------------------------------- + +def tree_parse(data, buf, size): + return f"yajl_tree_parse ({data}, {buf}, {size})" + +TREE_FREE_FUNC = "yajl_tree_free" + +def tree_free(val): + return f"{TREE_FREE_FUNC} ({val})" + +# --------------------------------------------------------------------------- +# Residual serialization +# --------------------------------------------------------------------------- +GEN_RESIDUAL_FUNC = "gen_yajl_object_residual" + +def gen_residual(obj, gen, err): + return f"{GEN_RESIDUAL_FUNC} ({obj}, {gen}, {err})" + +# --------------------------------------------------------------------------- +# Prologue defines needed at the top of each generated .c file +# --------------------------------------------------------------------------- + +def get_prologue_defines(): + return [ + "#define YAJL_GET_ARRAY_NO_CHECK(v) (&(v)->u.array)\n", + "#define YAJL_GET_OBJECT_NO_CHECK(v) (&(v)->u.object)\n", + ] + +# --------------------------------------------------------------------------- +# Cleanup helpers emitted once per generated .c file (epilog) +# --------------------------------------------------------------------------- + +def get_gen_cleanup_block(): + """Return the static cleanup function for the JSON generator.""" + return f"""\ +static void +cleanup_{GEN_TYPE} ({GEN_TYPE} g) +{{ + if (!g) + return; + {gen_clear('g')}; + {gen_free('g')}; +}} + +define_cleaner_function ({GEN_TYPE}, cleanup_{GEN_TYPE}) +""" + +def get_val_cleaner_define(): + """Return the cleaner macro for the JSON value tree.""" + return f"define_cleaner_function ({VAL_TYPE}, {TREE_FREE_FUNC})" diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 0acba8ca..642c3d62 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -31,6 +31,7 @@ from textwrap import dedent import helpers +import json_api def emit(c_file, code, indent=0): @@ -93,14 +94,14 @@ def calloc_with_check(c_file, dest, count, sizeof_expr, indent=0): def check_gen_status(c_file, indent=0): - """Generate yajl_gen status check with error return. + """Generate JSON gen status check with error return. Args: c_file: List to append code lines to indent: Number of 4-space indentation levels """ prefix = ' ' * indent - c_file.append(f"{prefix}if (stat != yajl_gen_status_ok)\n") + c_file.append(f"{prefix}if (stat != {json_api.GEN_STATUS_OK})\n") c_file.append(f"{prefix} GEN_SET_ERROR_AND_RETURN (stat, err);\n") @@ -109,7 +110,7 @@ def do_read_value(c_file, src_expr, dest_expr, typ, origname, obj_typename, inde Args: c_file: Output file list - src_expr: Source expression (e.g., 'get_val (tree, "name", yajl_t_string)') + src_expr: Source expression (e.g., 'get_val (tree, "name", json_api.TYPE_STRING)') dest_expr: Destination expression (e.g., 'ret->field') typ: Field type origname: Original field name from schema @@ -164,16 +165,18 @@ def emit_value_error(c_file, keyname, indent=0): ''', indent=indent) -def emit_invalid_type_check(c_file, yajl_check='YAJL_IS_NUMBER', indent=0): - """Emit YAJL type validation with error return. +def emit_invalid_type_check(c_file, check_expr=None, indent=0): + """Emit JSON type validation with error return. Args: c_file: List to append code lines to - yajl_check: YAJL type check macro (e.g., 'YAJL_IS_NUMBER') + check_expr: Full C check expression (default: json_api.is_number('val')) indent: Number of 4-space indentation levels """ + if check_expr is None: + check_expr = json_api.is_number('val') emit(c_file, f''' - if (! {yajl_check} (val)) + if (! {check_expr}) {{ *err = strdup ("invalid type"); return NULL; @@ -181,10 +184,10 @@ def emit_invalid_type_check(c_file, yajl_check='YAJL_IS_NUMBER', indent=0): ''', indent=indent) -# YAJL generation helpers +# JSON generation helpers def emit_gen_key(c_file, key, indent=0): - """Emit yajl_gen_string for an object key. + """Emit JSON gen_string for an object key. Args: c_file: List to append code lines to @@ -193,66 +196,66 @@ def emit_gen_key(c_file, key, indent=0): """ key_len = len(key) emit(c_file, f''' - stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{key}"), {key_len} /* strlen ("{key}") */); + stat = {json_api.gen_string('g', f'"{key}"', f'{key_len} /* strlen ("{key}") */')}; ''', indent=indent) def emit_gen_key_with_check(c_file, key, indent=0): - """Emit yajl_gen_string for an object key and check status.""" + """Emit JSON gen_string for an object key and check status.""" emit_gen_key(c_file, key, indent=indent) check_gen_status(c_file, indent=indent) def emit_gen_map_open(c_file, indent=0): - """Emit yajl_gen_map_open call. + """Emit gen_map_open call. Args: c_file: List to append code lines to indent: Number of 4-space indentation levels """ - emit(c_file, ''' - stat = yajl_gen_map_open ((yajl_gen) g); + emit(c_file, f''' + stat = {json_api.gen_map_open('g')}; ''', indent=indent) def emit_gen_map_close(c_file, indent=0): - """Emit yajl_gen_map_close call. + """Emit gen_map_close call. Args: c_file: List to append code lines to indent: Number of 4-space indentation levels """ - emit(c_file, ''' - stat = yajl_gen_map_close ((yajl_gen) g); + emit(c_file, f''' + stat = {json_api.gen_map_close('g')}; ''', indent=indent) def emit_gen_array_open(c_file, indent=0): - """Emit yajl_gen_array_open call. + """Emit gen_array_open call. Args: c_file: List to append code lines to indent: Number of 4-space indentation levels """ - emit(c_file, ''' - stat = yajl_gen_array_open ((yajl_gen) g); + emit(c_file, f''' + stat = {json_api.gen_array_open('g')}; ''', indent=indent) def emit_gen_array_close(c_file, indent=0): - """Emit yajl_gen_array_close call. + """Emit gen_array_close call. Args: c_file: List to append code lines to indent: Number of 4-space indentation levels """ - emit(c_file, ''' - stat = yajl_gen_array_close ((yajl_gen) g); + emit(c_file, f''' + stat = {json_api.gen_array_close('g')}; ''', indent=indent) def emit_beautify_off(c_file, condition='!len', indent=0): - """Emit yajl_gen_beautify disable. + """Emit beautify disable. Args: c_file: List to append code lines to @@ -261,12 +264,12 @@ def emit_beautify_off(c_file, condition='!len', indent=0): """ emit(c_file, f''' if ({condition} && !(ctx->options & OPT_GEN_SIMPLIFY)) - yajl_gen_config (g, yajl_gen_beautify, 0); + {json_api.gen_config('g', json_api.GEN_BEAUTIFY, '0')}; ''', indent=indent) def emit_beautify_on(c_file, condition='!len', indent=0): - """Emit yajl_gen_beautify enable. + """Emit beautify enable. Args: c_file: List to append code lines to @@ -275,7 +278,7 @@ def emit_beautify_on(c_file, condition='!len', indent=0): """ emit(c_file, f''' if ({condition} && !(ctx->options & OPT_GEN_SIMPLIFY)) - yajl_gen_config (g, yajl_gen_beautify, 1); + {json_api.gen_config('g', json_api.GEN_BEAUTIFY, '1')}; ''', indent=indent) @@ -324,7 +327,7 @@ class StringType(TypeHandler): """Handler for string type.""" def emit_parse(self, c_file, obj, prefix, obj_typename, indent=1): - do_read_value(c_file, f'get_val (tree, "{obj.origname}", yajl_t_string)', + do_read_value(c_file, f'get_val (tree, "{obj.origname}", {json_api.TYPE_STRING})', f"ret->{obj.fixname}", 'string', obj.origname, obj_typename, indent=indent) def emit_generate(self, c_file, obj, prefix, indent=1): @@ -361,10 +364,10 @@ def emit_clone(self, c_file, obj, prefix, indent=1): def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): emit(c_file, f''' - yajl_val val = {src}; + {json_api.VAL_TYPE} val = {src}; if (val != NULL) {{ - char *str = YAJL_GET_STRING (val); + char *str = {json_api.get_string('val')}; {dest} = strdup (str ? str : ""); if ({dest} == NULL) return NULL; @@ -373,8 +376,8 @@ def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): def emit_json_value(self, c_file, src, dst, ptx, level=1): emit(c_file, f''' - stat = yajl_gen_string ((yajl_gen){dst}, (const unsigned char *)({src}), strlen ({src})); - if (stat != yajl_gen_status_ok) + stat = {json_api.gen_string(dst, src, f'strlen ({src})')}; + if (stat != {json_api.GEN_STATUS_OK}) GEN_SET_ERROR_AND_RETURN (stat, err); ''', indent=level) @@ -383,7 +386,7 @@ class BooleanType(TypeHandler): """Handler for boolean type.""" def emit_parse(self, c_file, obj, prefix, obj_typename, indent=1): - do_read_value(c_file, f'get_val (tree, "{obj.origname}", yajl_t_true)', + do_read_value(c_file, f'get_val (tree, "{obj.origname}", {json_api.TYPE_TRUE})', f"ret->{obj.fixname}", 'boolean', obj.origname, obj_typename, indent=indent) def emit_generate(self, c_file, obj, prefix, indent=1): @@ -414,10 +417,10 @@ def emit_clone(self, c_file, obj, prefix, indent=1): def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): emit(c_file, f''' - yajl_val val = {src}; + {json_api.VAL_TYPE} val = {src}; if (val != NULL) {{ - {dest} = YAJL_IS_TRUE(val); + {dest} = {json_api.is_true('val')}; ''', indent=level) if '[' not in dest: emit(c_file, f''' @@ -425,7 +428,7 @@ def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): }} else {{ - val = {src.replace('yajl_t_true', 'yajl_t_false')}; + val = {src.replace(json_api.TYPE_TRUE, json_api.TYPE_FALSE)}; if (val != NULL) {{ {dest} = 0; @@ -440,8 +443,8 @@ def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): def emit_json_value(self, c_file, src, dst, ptx, level=1): emit(c_file, f''' - stat = yajl_gen_bool ((yajl_gen){dst}, (int)({src})); - if (stat != yajl_gen_status_ok) + stat = {json_api.gen_bool(dst, src)}; + if (stat != {json_api.GEN_STATUS_OK}) GEN_SET_ERROR_AND_RETURN (stat, err); ''', indent=level) @@ -450,7 +453,7 @@ class BooleanPointerType(TypeHandler): """Handler for booleanPointer type.""" def emit_parse(self, c_file, obj, prefix, obj_typename, indent=1): - do_read_value(c_file, f'get_val (tree, "{obj.origname}", yajl_t_true)', + do_read_value(c_file, f'get_val (tree, "{obj.origname}", {json_api.TYPE_TRUE})', f"ret->{obj.fixname}", 'booleanPointer', obj.origname, obj_typename, indent=indent) def emit_generate(self, c_file, obj, prefix, indent=1): @@ -490,31 +493,31 @@ def emit_clone(self, c_file, obj, prefix, indent=1): def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): emit(c_file, f''' - yajl_val val = {src}; + {json_api.VAL_TYPE} val = {src}; if (val != NULL) {{ {dest} = calloc (1, sizeof (bool)); if ({dest} == NULL) return NULL; - *({dest}) = YAJL_IS_TRUE(val); + *({dest}) = {json_api.is_true('val')}; }} else {{ - val = get_val (tree, "{keyname}", yajl_t_false); + val = get_val (tree, "{keyname}", {json_api.TYPE_FALSE}); if (val != NULL) {{ {dest} = calloc (1, sizeof (bool)); if ({dest} == NULL) return NULL; - *({dest}) = YAJL_IS_TRUE(val); + *({dest}) = {json_api.is_true('val')}; }} }} ''', indent=level) def emit_json_value(self, c_file, src, dst, ptx, level=1): emit(c_file, f''' - stat = yajl_gen_bool ((yajl_gen){dst}, (int)({src})); - if (stat != yajl_gen_status_ok) + stat = {json_api.gen_bool(dst, src)}; + if (stat != {json_api.GEN_STATUS_OK}) GEN_SET_ERROR_AND_RETURN (stat, err); ''', indent=level) @@ -545,7 +548,7 @@ def _get_c_numtype(self): return 'long long int' def emit_parse(self, c_file, obj, prefix, obj_typename, indent=1): - do_read_value(c_file, f'get_val (tree, "{obj.origname}", yajl_t_number)', + do_read_value(c_file, f'get_val (tree, "{obj.origname}", {json_api.TYPE_NUMBER})', f"ret->{obj.fixname}", self.typ, obj.origname, obj_typename, indent=indent) def emit_generate(self, c_file, obj, prefix, indent=1): @@ -577,17 +580,17 @@ def emit_clone(self, c_file, obj, prefix, indent=1): def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): conv_func, dest_cast = self._get_conversion_info() emit(c_file, f''' - yajl_val val = {src}; + {json_api.VAL_TYPE} val = {src}; if (val != NULL) {{ int invalid; ''', indent=level) - emit_invalid_type_check(c_file, 'YAJL_IS_NUMBER', indent=level + 1) + emit_invalid_type_check(c_file, json_api.is_number('val'), indent=level + 1) emit(c_file, f''' - invalid = {conv_func} (YAJL_GET_NUMBER (val), {dest_cast}{dest}); + invalid = {conv_func} ({json_api.get_number('val')}, {dest_cast}{dest}); if (invalid) {{ - if (asprintf (err, "Invalid value '%s' with type '{self.typ}' for key '{keyname}': %s", YAJL_GET_NUMBER (val), strerror (-invalid)) < 0) + if (asprintf (err, "Invalid value '%s' with type '{self.typ}' for key '{keyname}': %s", {json_api.get_number('val')}, strerror (-invalid)) < 0) *err = strdup ("error allocating memory"); return NULL; }} @@ -603,7 +606,7 @@ def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): def emit_json_value(self, c_file, src, dst, ptx, level=1): if self.typ == 'double': emit(c_file, f''' - stat = yajl_gen_double ((yajl_gen){dst}, {src}); + stat = {json_api.gen_double(dst, src)}; ''', indent=level) elif self.typ.startswith("uint") or self.typ == 'GID' or self.typ == 'UID': emit(c_file, f''' @@ -614,7 +617,7 @@ def emit_json_value(self, c_file, src, dst, ptx, level=1): stat = map_int ({dst}, {src}); ''', indent=level) emit(c_file, f''' - if (stat != yajl_gen_status_ok) + if (stat != {json_api.GEN_STATUS_OK}) GEN_SET_ERROR_AND_RETURN (stat, err); ''', indent=level) @@ -627,7 +630,7 @@ def __init__(self, typ): self.base_typ = helpers.get_pointer_base_type(typ) def emit_parse(self, c_file, obj, prefix, obj_typename, indent=1): - do_read_value(c_file, f'get_val (tree, "{obj.origname}", yajl_t_number)', + do_read_value(c_file, f'get_val (tree, "{obj.origname}", {json_api.TYPE_NUMBER})', f"ret->{obj.fixname}", self.typ, obj.origname, obj_typename, indent=indent) def emit_generate(self, c_file, obj, prefix, indent=1): @@ -672,7 +675,7 @@ def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): if self.base_typ == "": return emit(c_file, f''' - yajl_val val = {src}; + {json_api.VAL_TYPE} val = {src}; if (val != NULL) {{ {dest} = calloc (1, sizeof ({helpers.get_map_c_types(self.base_typ)})); @@ -680,12 +683,12 @@ def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): return NULL; int invalid; ''', indent=level) - emit_invalid_type_check(c_file, 'YAJL_IS_NUMBER', indent=level + 1) + emit_invalid_type_check(c_file, json_api.is_number('val'), indent=level + 1) emit(c_file, f''' - invalid = common_safe_{self.base_typ} (YAJL_GET_NUMBER (val), {dest}); + invalid = common_safe_{self.base_typ} ({json_api.get_number('val')}, {dest}); if (invalid) {{ - if (asprintf (err, "Invalid value '%s' with type '{self.typ}' for key '{keyname}': %s", YAJL_GET_NUMBER (val), strerror (-invalid)) < 0) + if (asprintf (err, "Invalid value '%s' with type '{self.typ}' for key '{keyname}': %s", {json_api.get_number('val')}, strerror (-invalid)) < 0) *err = strdup ("error allocating memory"); return NULL; }} @@ -699,7 +702,7 @@ class ObjectType(TypeHandler): def emit_parse(self, c_file, obj, prefix, obj_typename, indent=1): typename = obj.subtypname or helpers.get_prefixed_name(obj.name, prefix) emit(c_file, f''' - ret->{obj.fixname} = make_{typename} (get_val (tree, "{obj.origname}", yajl_t_object), ctx, err); + ret->{obj.fixname} = make_{typename} (get_val (tree, "{obj.origname}", {json_api.TYPE_OBJECT}), ctx, err); if (ret->{obj.fixname} == NULL && *err != 0) return NULL; ''', indent=indent) @@ -762,14 +765,14 @@ def emit_make_body(self, c_file, obj, prefix): if obj.children is not None: # O(n^2) complexity, but the objects should not really be big... condition = "\n && ".join( \ - [f'strcmp (tree->u.object.keys[i], "{i.origname}")' for i in obj.children]) + [f'strcmp ({json_api.object_key_direct("tree", "i")}, "{i.origname}")' for i in obj.children]) emit(c_file, f''' - if (tree->type == yajl_t_object) + if ({json_api.object_type_field("tree")} == {json_api.TYPE_OBJECT}) {{ size_t i; size_t j = 0; - size_t cnt = tree->u.object.len; - yajl_val resi = NULL; + size_t cnt = {json_api.object_len_field("tree")}; + {json_api.VAL_TYPE} resi = NULL; if (ctx->options & OPT_PARSE_FULLKEY) {{ @@ -777,31 +780,31 @@ def emit_make_body(self, c_file, obj, prefix): if (resi == NULL) return NULL; - resi->type = yajl_t_object; - resi->u.object.keys = calloc (cnt, sizeof (const char *)); - if (resi->u.object.keys == NULL) + {json_api.set_object_type("resi")}; + {json_api.alloc_object_keys("resi", "cnt")}; + if ({json_api.object_keys_field("resi")} == NULL) {{ - yajl_tree_free (resi); + {json_api.tree_free("resi")}; return NULL; }} - resi->u.object.values = calloc (cnt, sizeof (yajl_val)); - if (resi->u.object.values == NULL) + {json_api.alloc_object_values("resi", "cnt")}; + if ({json_api.object_values_field("resi")} == NULL) {{ - yajl_tree_free (resi); + {json_api.tree_free("resi")}; return NULL; }} }} - for (i = 0; i < tree->u.object.len; i++) + for (i = 0; i < {json_api.object_len_field("tree")}; i++) {{ if ({condition}){{ if (ctx->options & OPT_PARSE_FULLKEY) {{ - resi->u.object.keys[j] = tree->u.object.keys[i]; - tree->u.object.keys[i] = NULL; - resi->u.object.values[j] = tree->u.object.values[i]; - tree->u.object.values[i] = NULL; - resi->u.object.len++; + {json_api.object_key_direct("resi", "j")} = {json_api.object_key_direct("tree", "i")}; + {json_api.object_key_direct("tree", "i")} = NULL; + {json_api.object_value_direct("resi", "j")} = {json_api.object_value_direct("tree", "i")}; + {json_api.object_value_direct("tree", "i")} = NULL; + {json_api.object_len_field("resi")}++; }} j++; }} @@ -828,13 +831,13 @@ def emit_gen_body(self, c_file, obj, prefix): if handler: handler.emit_generate(c_file, i, prefix, indent=1) if obj.children is not None: - emit(c_file, ''' + emit(c_file, f''' if (ptr != NULL && ptr->_residual != NULL) - { - stat = gen_yajl_object_residual (ptr->_residual, g, err); - if (yajl_gen_status_ok != stat) + {{ + stat = {json_api.gen_residual('ptr->_residual', 'g', 'err')}; + if ({json_api.GEN_STATUS_OK} != stat) GEN_SET_ERROR_AND_RETURN (stat, err); - } + }} ''', indent=1) emit_gen_map_close(c_file, indent=1) check_gen_status(c_file, indent=1) @@ -850,8 +853,8 @@ def emit_free_body(self, c_file, obj, prefix): handler.emit_free(c_file, i, prefix, indent=1) if obj.children is not None: - emit(c_file, ''' - yajl_tree_free (ptr->_residual); + emit(c_file, f''' + {json_api.tree_free('ptr->_residual')}; ptr->_residual = NULL; ''', indent=1) @@ -884,7 +887,7 @@ class MapStringObjectType(TypeHandler): def emit_parse(self, c_file, obj, prefix, obj_typename, indent=1): typename = obj.subtypname or helpers.get_prefixed_name(obj.name, prefix) emit(c_file, f''' - ret->{obj.fixname} = make_{typename} (get_val (tree, "{obj.origname}", yajl_t_object), ctx, err); + ret->{obj.fixname} = make_{typename} (get_val (tree, "{obj.origname}", {json_api.TYPE_OBJECT}), ctx, err); if (ret->{obj.fixname} == NULL && *err != 0) return NULL; ''', indent=indent) @@ -955,12 +958,12 @@ def emit_make_body(self, c_file, obj, prefix): childname = helpers.get_prefixed_name(child.name, prefix) emit(c_file, f''' - if (YAJL_GET_OBJECT (tree) != NULL) + if ({json_api.object_check('tree')}) {{ size_t i; - size_t len = YAJL_GET_OBJECT_NO_CHECK (tree)->len; - const char **keys = YAJL_GET_OBJECT_NO_CHECK (tree)->keys; - yajl_val *values = YAJL_GET_OBJECT_NO_CHECK (tree)->values; + size_t len = {json_api.object_len('tree')}; + const char **keys = {json_api.object_keys('tree')}; + {json_api.VAL_TYPE} *values = {json_api.object_values('tree')}; ret->len = len; ''', indent=1) @@ -970,7 +973,7 @@ def emit_make_body(self, c_file, obj, prefix): emit(c_file, f''' for (i = 0; i < len; i++) {{ - yajl_val val; + {json_api.VAL_TYPE} val; const char *tmpkey = keys[i]; ret->keys[i] = strdup (tmpkey ? tmpkey : ""); ''', indent=2) @@ -1013,7 +1016,7 @@ def emit_gen_body(self, c_file, obj, prefix): for (i = 0; i < len; i++) {{ char *str = ptr->keys[i] ? ptr->keys[i] : ""; - stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)str, strlen (str)); + stat = {json_api.gen_string('g', 'str', 'strlen (str)')}; ''', indent=1) check_gen_status(c_file, indent=3) @@ -1115,7 +1118,7 @@ def emit_parse(self, c_file, obj, prefix, obj_typename, indent=1): emit(c_file, f''' do {{ - yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_object); + {json_api.VAL_TYPE} tmp = get_val (tree, "{obj.origname}", {json_api.TYPE_OBJECT}); if (tmp != NULL) {{ ret->{obj.fixname} = make_{self.map_name} (tmp, ctx, err); @@ -1152,7 +1155,7 @@ def emit_free(self, c_file, obj, prefix, indent=1): def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): emit(c_file, f''' - yajl_val val = {src}; + {json_api.VAL_TYPE} val = {src}; if (val != NULL) {{ {dest} = make_{self.map_name} (val, ctx, err); @@ -1168,7 +1171,7 @@ def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): def emit_json_value(self, c_file, src, dst, ptx, level=1): emit(c_file, f''' stat = gen_{self.map_name} ({dst}, {src}, {ptx}, err); - if (stat != yajl_gen_status_ok) + if (stat != {json_api.GEN_STATUS_OK}) GEN_SET_ERROR_AND_RETURN (stat, err); ''', indent=level) @@ -1215,12 +1218,12 @@ def emit_parse(self, c_file, obj, prefix, obj_typename): emit(c_file, f''' do {{ - yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_array); - if (tmp != NULL && YAJL_GET_ARRAY (tmp) != NULL) + {json_api.VAL_TYPE} tmp = get_val (tree, "{obj.origname}", {json_api.TYPE_ARRAY}); + if (tmp != NULL && {json_api.array_check('tmp')}) {{ size_t i; - size_t len = YAJL_GET_ARRAY_NO_CHECK (tmp)->len; - yajl_val *values = YAJL_GET_ARRAY_NO_CHECK (tmp)->values; + size_t len = {json_api.array_len('tmp')}; + {json_api.VAL_TYPE} *values = {json_api.array_values('tmp')}; ret->{obj.fixname}_len = len; ''', indent=1) @@ -1228,22 +1231,22 @@ def emit_parse(self, c_file, obj, prefix, obj_typename): if obj.nested_array: calloc_with_check(c_file, f'ret->{obj.fixname}_item_lens', 'len + 1', 'size_t', indent=3) - emit(c_file, ''' + emit(c_file, f''' for (i = 0; i < len; i++) - { - yajl_val val = values[i]; + {{ + {json_api.VAL_TYPE} val = values[i]; ''', indent=3) if obj.nested_array: emit(c_file, f''' size_t j; - ret->{obj.fixname}[i] = calloc ( YAJL_GET_ARRAY_NO_CHECK(val)->len + 1, sizeof (**ret->{obj.fixname})); + ret->{obj.fixname}[i] = calloc ( {json_api.array_len('val')} + 1, sizeof (**ret->{obj.fixname})); ''', indent=4) null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=4) - emit(c_file, ''' - yajl_val *items = YAJL_GET_ARRAY_NO_CHECK(val)->values; - for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(val)->len; j++) - { + emit(c_file, f''' + {json_api.VAL_TYPE} *items = {json_api.array_values('val')}; + for (j = 0; j < {json_api.array_len('val')}; j++) + {{ ''', indent=4) emit(c_file, f''' ret->{obj.fixname}[i][j] = make_{typename} (items[j], ctx, err); @@ -1397,15 +1400,15 @@ def emit_parse(self, c_file, obj, prefix, obj_typename): emit(c_file, f''' do {{ - yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_string); + {json_api.VAL_TYPE} tmp = get_val (tree, "{obj.origname}", {json_api.TYPE_STRING}); if (tmp != NULL) {{ ''', indent=1) if obj.nested_array: emit(c_file, f''' - yajl_val *items = YAJL_GET_ARRAY_NO_CHECK(tmp)->values; - ret->{obj.fixname}_len = YAJL_GET_ARRAY_NO_CHECK(tmp)->len; + {json_api.VAL_TYPE} *items = {json_api.array_values('tmp')}; + ret->{obj.fixname}_len = {json_api.array_len('tmp')}; ret->{obj.fixname} = calloc (ret->{obj.fixname}_len + 1, sizeof (*ret->{obj.fixname})); ''', indent=4) null_check_return(c_file, f'ret->{obj.fixname}', indent=4) @@ -1413,7 +1416,7 @@ def emit_parse(self, c_file, obj, prefix, obj_typename): size_t j; for (j = 0; j < ret->{obj.fixname}_len; j++) {{ - char *str = YAJL_GET_STRING (items[j]); + char *str = {json_api.get_string('items[j]')}; ''', indent=4) emit(c_file, f''' ret->{obj.fixname}[j] = (uint8_t *)strdup (str ? str : ""); @@ -1423,8 +1426,8 @@ def emit_parse(self, c_file, obj, prefix, obj_typename): } ''', indent=5) else: - emit(c_file, ''' - char *str = YAJL_GET_STRING (tmp); + emit(c_file, f''' + char *str = {json_api.get_string('tmp')}; ''', indent=3) emit(c_file, f''' ret->{obj.fixname} = (uint8_t *)strdup (str ? str : ""); @@ -1461,7 +1464,7 @@ def emit_generate(self, c_file, obj, prefix): str = (const char *)ptr->{obj.fixname}[i]; else str = ""; - stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)str, strlen(str)); + stat = {json_api.gen_string('g', 'str', 'strlen(str)')}; }} }} ''', indent=2) @@ -1473,7 +1476,7 @@ def emit_generate(self, c_file, obj, prefix): str = (const char *)ptr->{obj.fixname}; len = ptr->{obj.fixname}_len; }} - stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)str, len); + stat = {json_api.gen_string('g', 'str', 'len')}; ''', indent=2) check_gen_status(c_file, indent=2) @@ -1500,12 +1503,12 @@ def emit_parse(self, c_file, obj, prefix, obj_typename): emit(c_file, f''' do {{ - yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_array); - if (tmp != NULL && YAJL_GET_ARRAY (tmp) != NULL) + {json_api.VAL_TYPE} tmp = get_val (tree, "{obj.origname}", {json_api.TYPE_ARRAY}); + if (tmp != NULL && {json_api.array_check('tmp')}) {{ size_t i; - size_t len = YAJL_GET_ARRAY_NO_CHECK (tmp)->len; - yajl_val *values = YAJL_GET_ARRAY_NO_CHECK (tmp)->values; + size_t len = {json_api.array_len('tmp')}; + {json_api.VAL_TYPE} *values = {json_api.array_values('tmp')}; ret->{obj.fixname}_len = len; ''', indent=1) @@ -1520,14 +1523,14 @@ def emit_parse(self, c_file, obj, prefix, obj_typename): if obj.nested_array: emit(c_file, f''' - yajl_val *items = YAJL_GET_ARRAY_NO_CHECK(values[i])->values; - ret->{obj.fixname}[i] = calloc ( YAJL_GET_ARRAY_NO_CHECK(values[i])->len + 1, sizeof (**ret->{obj.fixname})); + {json_api.VAL_TYPE} *items = {json_api.array_values('values[i]')}; + ret->{obj.fixname}[i] = calloc ( {json_api.array_len('values[i]')} + 1, sizeof (**ret->{obj.fixname})); ''', indent=4) null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=5) - emit(c_file, ''' + emit(c_file, f''' size_t j; - for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(values[i])->len; j++) - { + for (j = 0; j < {json_api.array_len('values[i]')}; j++) + {{ ''', indent=4) read_val_generator(c_file, 5, 'items[j]', f"ret->{obj.fixname}[i][j]", obj.subtyp, obj.origname, obj_typename) @@ -1701,19 +1704,19 @@ def emit_parse(self, c_file, obj, prefix, obj_typename): emit(c_file, f''' do {{ - yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_array); - if (tmp != NULL && YAJL_GET_ARRAY (tmp) != NULL) + {json_api.VAL_TYPE} tmp = get_val (tree, "{obj.origname}", {json_api.TYPE_ARRAY}); + if (tmp != NULL && {json_api.array_check('tmp')}) {{ size_t i; - size_t len = YAJL_GET_ARRAY_NO_CHECK (tmp)->len; - yajl_val *values = YAJL_GET_ARRAY_NO_CHECK (tmp)->values; + size_t len = {json_api.array_len('tmp')}; + {json_api.VAL_TYPE} *values = {json_api.array_values('tmp')}; ret->{obj.fixname}_len = len; ''', indent=1) calloc_with_check(c_file, f'ret->{obj.fixname}', 'len + 1', f'*ret->{obj.fixname}', indent=3) emit(c_file, f''' for (i = 0; i < len; i++) {{ - yajl_val val = values[i]; + {json_api.VAL_TYPE} val = values[i]; ret->{obj.fixname}[i] = make_{map_func} (val, ctx, err); if (ret->{obj.fixname}[i] == NULL) return NULL; @@ -1742,7 +1745,7 @@ def emit_generate(self, c_file, obj, prefix): for (i = 0; i < len; i++) {{ stat = gen_{map_func} (g, ptr->{obj.fixname}[i], ctx, err); - if (stat != yajl_gen_status_ok) + if (stat != {json_api.GEN_STATUS_OK}) GEN_SET_ERROR_AND_RETURN (stat, err); }} ''', indent=2) @@ -1988,7 +1991,7 @@ def parse_json_to_c(obj, c_file, prefix): emit(c_file, f''' define_cleaner_function ({typename} *, free_{typename}) {typename} * - make_{typename} (yajl_val tree, const struct parser_context *ctx, parser_error *err) + make_{typename} ({json_api.VAL_TYPE} tree, const struct parser_context *ctx, parser_error *err) {{ __auto_cleanup(free_{typename}) {typename} *ret = NULL; *err = NULL; @@ -2024,10 +2027,10 @@ def get_c_json(obj, c_file, prefix): if obj.subtypobj is None: return emit(c_file, f''' - yajl_gen_status - gen_{typename} (yajl_gen g, const {typename} *ptr, const struct parser_context *ctx, parser_error *err) + {json_api.GEN_STATUS_TYPE} + gen_{typename} ({json_api.GEN_TYPE} g, const {typename} *ptr, const struct parser_context *ctx, parser_error *err) {{ - yajl_gen_status stat = yajl_gen_status_ok; + {json_api.GEN_STATUS_TYPE} stat = {json_api.GEN_STATUS_OK}; *err = NULL; (void) ptr; /* Silence compiler warning. */ ''', indent=0) @@ -2036,7 +2039,7 @@ def get_c_json(obj, c_file, prefix): if handler and hasattr(handler, 'emit_gen_body'): handler.emit_gen_body(c_file, obj, prefix) - c_file.append(" return yajl_gen_status_ok;\n") + c_file.append(f" return {json_api.GEN_STATUS_OK};\n") c_file.append("}\n") c_file.append("\n") @@ -2143,10 +2146,9 @@ def src_reflect(structs, schema_info, c_file, root_typ): #include #include #include "ocispec/{schema_info.header.basename}" - - #define YAJL_GET_ARRAY_NO_CHECK(v) (&(v)->u.array) - #define YAJL_GET_OBJECT_NO_CHECK(v) (&(v)->u.object) ''', indent=0) + for define in json_api.get_prologue_defines(): + c_file.append(define) for i in structs: append_c_code(i, c_file, schema_info.prefix) @@ -2166,17 +2168,17 @@ def get_c_epilog_for_array_make_parse(c_file, prefix, typ, obj): define_cleaner_function ({typename} *, free_{typename}) {typename} - *make_{typename} (yajl_val tree, const struct parser_context *ctx, parser_error *err) + *make_{typename} ({json_api.VAL_TYPE} tree, const struct parser_context *ctx, parser_error *err) {{ __auto_cleanup(free_{typename}) {typename} *ptr = NULL; size_t i, alen; (void) ctx; - if (tree == NULL || err == NULL || YAJL_GET_ARRAY (tree) == NULL) + if (tree == NULL || err == NULL || !({json_api.array_check('tree')})) return NULL; *err = NULL; - alen = YAJL_GET_ARRAY_NO_CHECK (tree)->len; + alen = {json_api.array_len('tree')}; if (alen == 0) return NULL; ptr = calloc (1, sizeof ({typename})); @@ -2195,11 +2197,11 @@ def get_c_epilog_for_array_make_parse(c_file, prefix, typ, obj): return NULL; ''', indent=1) - emit(c_file, ''' + emit(c_file, f''' for (i = 0; i < alen; i++) - { - yajl_val work = YAJL_GET_ARRAY_NO_CHECK (tree)->values[i]; + {{ + {json_api.VAL_TYPE} work = {json_api.array_get('tree', 'i')}; ''', indent=1) if obj.subtypobj or obj.subtyp == 'object': @@ -2211,11 +2213,11 @@ def get_c_epilog_for_array_make_parse(c_file, prefix, typ, obj): if obj.nested_array: emit(c_file, f''' size_t j; - ptr->items[i] = calloc ( YAJL_GET_ARRAY_NO_CHECK(work)->len + 1, sizeof (**ptr->items)); + ptr->items[i] = calloc ( {json_api.array_len('work')} + 1, sizeof (**ptr->items)); if (ptr->items[i] == NULL) return NULL; - yajl_val *tmps = YAJL_GET_ARRAY_NO_CHECK(work)->values; - for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(work)->len; j++) + {json_api.VAL_TYPE} *tmps = {json_api.array_values('work')}; + for (j = 0; j < {json_api.array_len('work')}; j++) {{ ptr->items[i][j] = make_{subtypename} (tmps[j], ctx, err); if (ptr->items[i][j] == NULL) @@ -2231,28 +2233,28 @@ def get_c_epilog_for_array_make_parse(c_file, prefix, typ, obj): ''', indent=2) elif obj.subtyp == 'byte': if obj.nested_array: - emit(c_file, ''' - char *str = YAJL_GET_STRING (work); + emit(c_file, f''' + char *str = {json_api.get_string('work')}; ptr->items[j] = (uint8_t *)strdup (str ? str : ""); if (ptr->items[j] == NULL) return NULL; ''', indent=2) else: - emit(c_file, ''' - char *str = YAJL_GET_STRING (tree); + emit(c_file, f''' + char *str = {json_api.get_string('tree')}; memcpy(ptr->items, str ? str : "", strlen(str ? str : "")); break; ''', indent=2) else: if obj.nested_array: - emit(c_file, ''' - ptr->items[i] = calloc ( YAJL_GET_ARRAY_NO_CHECK(work)->len + 1, sizeof (**ptr->items)); + emit(c_file, f''' + ptr->items[i] = calloc ( {json_api.array_len('work')} + 1, sizeof (**ptr->items)); if (ptr->items[i] == NULL) return NULL; size_t j; - yajl_val *tmps = YAJL_GET_ARRAY_NO_CHECK(work)->values; - for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(work)->len; j++) - { + {json_api.VAL_TYPE} *tmps = {json_api.array_values('work')}; + for (j = 0; j < {json_api.array_len('work')}; j++) + {{ ''', indent=2) read_val_generator(c_file, 3, 'tmps[j]', \ "ptr->items[i][j]", obj.subtyp, obj.origname, c_typ) @@ -2387,14 +2389,14 @@ def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): typename = helpers.get_top_array_type_name(obj.name, prefix) emit(c_file, f''' - yajl_gen_status gen_{typename} (yajl_gen g, const {typename} *ptr, const struct parser_context *ctx, + {json_api.GEN_STATUS_TYPE} gen_{typename} ({json_api.GEN_TYPE} g, const {typename} *ptr, const struct parser_context *ctx, parser_error *err) {{ - yajl_gen_status stat; + {json_api.GEN_STATUS_TYPE} stat; size_t i; if (ptr == NULL) - return yajl_gen_status_ok; + return {json_api.GEN_STATUS_OK}; *err = NULL; ''', indent=0) @@ -2422,7 +2424,7 @@ def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): for (j = 0; j < ptr->subitem_lens[i]; j++) {{ stat = gen_{subtypename} (g, ptr->items[i][j], ctx, err); - if (stat != yajl_gen_status_ok) + if (stat != {json_api.GEN_STATUS_OK}) GEN_SET_ERROR_AND_RETURN (stat, err); }} ''', indent=3) @@ -2446,27 +2448,27 @@ def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): if obj.nested_array: emit_gen_array_open(c_file, indent=3) check_gen_status(c_file, indent=3) - emit(c_file, ''' - { + emit(c_file, f''' + {{ size_t i; for (i = 0; i < ptr->len; i++) - { + {{ if (ptr->items[i] != NULL) str = (const char *)ptr->items[i]; else str = ""; - stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)str, strlen(str)); - } - } + stat = {json_api.gen_string('g', 'str', 'strlen(str)')}; + }} + }} ''', indent=3) emit_gen_array_close(c_file, indent=3) else: - emit(c_file, ''' + emit(c_file, f''' if (ptr != NULL && ptr->items != NULL) - { + {{ str = (const char *)ptr->items; - } - stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)str, ptr->len); + }} + stat = {json_api.gen_string('g', 'str', 'ptr->len')}; ''', indent=2) emit(c_file, ''' } @@ -2506,14 +2508,14 @@ def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): emit_gen_array_close(c_file, indent=1) - emit(c_file, ''' + emit(c_file, f''' if (ptr->len > 0 && !(ctx->options & OPT_GEN_SIMPLIFY)) - yajl_gen_config (g, yajl_gen_beautify, 1); - if (stat != yajl_gen_status_ok) + {json_api.gen_config('g', json_api.GEN_BEAUTIFY, '1')}; + if (stat != {json_api.GEN_STATUS_OK}) GEN_SET_ERROR_AND_RETURN (stat, err); ''', indent=1) - c_file.append(" return yajl_gen_status_ok;\n") + c_file.append(f" return {json_api.GEN_STATUS_OK};\n") c_file.append("}\n") c_file.append("\n") @@ -2588,13 +2590,13 @@ def get_c_epilog(c_file, prefix, typ, obj): emit(c_file, f''' - define_cleaner_function (yajl_val, yajl_tree_free) + {json_api.get_val_cleaner_define()} {typename} * {typename}_parse_data (const char *jsondata, const struct parser_context *ctx, parser_error *err) {{ {typename} *ptr = NULL; - __auto_cleanup(yajl_tree_free) yajl_val tree = NULL; + __auto_cleanup({json_api.TREE_FREE_FUNC}) {json_api.VAL_TYPE} tree = NULL; char errbuf[1024]; struct parser_context tmp_ctx = {{ 0 }}; @@ -2605,7 +2607,7 @@ def get_c_epilog(c_file, prefix, typ, obj): if (ctx == NULL) ctx = (const struct parser_context *)(&tmp_ctx); - tree = yajl_tree_parse (jsondata, errbuf, sizeof (errbuf)); + tree = {json_api.tree_parse('jsondata', 'errbuf', 'sizeof (errbuf)')}; if (tree == NULL) {{ if (asprintf (err, "cannot parse the data: %s", errbuf) < 0) @@ -2617,27 +2619,14 @@ def get_c_epilog(c_file, prefix, typ, obj): }} ''', indent=0) - emit(c_file, ''' - - static void - cleanup_yajl_gen (yajl_gen g) - { - if (!g) - return; - yajl_gen_clear (g); - yajl_gen_free (g); - } - - define_cleaner_function (yajl_gen, cleanup_yajl_gen) - - ''', indent=0) + c_file.append(json_api.get_gen_cleanup_block()) emit(c_file, f''' char * {typename}_generate_json (const {typename} *ptr, const struct parser_context *ctx, parser_error *err) {{ - __auto_cleanup(cleanup_yajl_gen) yajl_gen g = NULL; + __auto_cleanup(cleanup_{json_api.GEN_TYPE}) {json_api.GEN_TYPE} g = NULL; struct parser_context tmp_ctx = {{ 0 }}; const unsigned char *gen_buf = NULL; char *json_buf = NULL; @@ -2656,14 +2645,14 @@ def get_c_epilog(c_file, prefix, typ, obj): return json_buf; }} - if (yajl_gen_status_ok != gen_{typename} (g, ptr, ctx, err)) + if ({json_api.GEN_STATUS_OK} != gen_{typename} (g, ptr, ctx, err)) {{ if (*err == NULL) *err = strdup ("Failed to generate json"); return json_buf; }} - yajl_gen_get_buf (g, &gen_buf, &gen_len); + {json_api.gen_get_buf('g', '&gen_buf', '&gen_len')}; if (gen_buf == NULL) {{ *err = strdup ("Error to get generated json"); From 790d3a6e4b6717022d83d392871a52890030bcf2 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Tue, 14 Apr 2026 14:18:51 +0000 Subject: [PATCH 2/6] sources: extract common array parse preamble into helper The do/get_val/array_check/len/values/calloc block was duplicated identically across ObjectArrayHandler, PrimitiveArrayHandler, and BasicMapArrayHandler. Extract it into emit_array_parse_preamble(). Co-Authored-By: Claude Opus 4.6 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 67 ++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 42 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 642c3d62..298c04e2 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -282,6 +282,28 @@ def emit_beautify_on(c_file, condition='!len', indent=0): ''', indent=indent) +def emit_array_parse_preamble(c_file, obj): + """Emit the common preamble for array parsing. + + Emits the do/get_val/array_check/len/values/calloc block shared by + ObjectArrayHandler, PrimitiveArrayHandler, and BasicMapArrayHandler. + """ + emit(c_file, f''' + do + {{ + {json_api.VAL_TYPE} tmp = get_val (tree, "{obj.origname}", {json_api.TYPE_ARRAY}); + if (tmp != NULL && {json_api.array_check('tmp')}) + {{ + size_t i; + size_t len = {json_api.array_len('tmp')}; + {json_api.VAL_TYPE} *values = {json_api.array_values('tmp')}; + ret->{obj.fixname}_len = len; + ''', indent=1) + calloc_with_check(c_file, f'ret->{obj.fixname}', 'len + 1', f'*ret->{obj.fixname}', indent=3) + if obj.nested_array: + calloc_with_check(c_file, f'ret->{obj.fixname}_item_lens', 'len + 1', 'size_t', indent=3) + + def get_compound_children(obj): """Get the children/subtypes for a compound type. @@ -1215,21 +1237,7 @@ class ObjectArrayHandler(ArraySubtypeHandler): def emit_parse(self, c_file, obj, prefix, obj_typename): typename = obj.subtypname if obj.subtypname else helpers.get_name_substr(obj.name, prefix) - emit(c_file, f''' - do - {{ - {json_api.VAL_TYPE} tmp = get_val (tree, "{obj.origname}", {json_api.TYPE_ARRAY}); - if (tmp != NULL && {json_api.array_check('tmp')}) - {{ - size_t i; - size_t len = {json_api.array_len('tmp')}; - {json_api.VAL_TYPE} *values = {json_api.array_values('tmp')}; - ret->{obj.fixname}_len = len; - ''', indent=1) - - calloc_with_check(c_file, f'ret->{obj.fixname}', 'len + 1', f'*ret->{obj.fixname}', indent=3) - if obj.nested_array: - calloc_with_check(c_file, f'ret->{obj.fixname}_item_lens', 'len + 1', 'size_t', indent=3) + emit_array_parse_preamble(c_file, obj) emit(c_file, f''' for (i = 0; i < len; i++) @@ -1500,21 +1508,7 @@ class PrimitiveArrayHandler(ArraySubtypeHandler): """Handler for arrays of primitive types (string, numeric, etc.).""" def emit_parse(self, c_file, obj, prefix, obj_typename): - emit(c_file, f''' - do - {{ - {json_api.VAL_TYPE} tmp = get_val (tree, "{obj.origname}", {json_api.TYPE_ARRAY}); - if (tmp != NULL && {json_api.array_check('tmp')}) - {{ - size_t i; - size_t len = {json_api.array_len('tmp')}; - {json_api.VAL_TYPE} *values = {json_api.array_values('tmp')}; - ret->{obj.fixname}_len = len; - ''', indent=1) - - calloc_with_check(c_file, f'ret->{obj.fixname}', 'len + 1', f'*ret->{obj.fixname}', indent=3) - if obj.nested_array: - calloc_with_check(c_file, f'ret->{obj.fixname}_item_lens', 'len + 1', 'size_t', indent=3) + emit_array_parse_preamble(c_file, obj) emit(c_file, ''' for (i = 0; i < len; i++) @@ -1701,18 +1695,7 @@ class BasicMapArrayHandler(ArraySubtypeHandler): def emit_parse(self, c_file, obj, prefix, obj_typename): map_func = helpers.make_basic_map_name(obj.subtyp) - emit(c_file, f''' - do - {{ - {json_api.VAL_TYPE} tmp = get_val (tree, "{obj.origname}", {json_api.TYPE_ARRAY}); - if (tmp != NULL && {json_api.array_check('tmp')}) - {{ - size_t i; - size_t len = {json_api.array_len('tmp')}; - {json_api.VAL_TYPE} *values = {json_api.array_values('tmp')}; - ret->{obj.fixname}_len = len; - ''', indent=1) - calloc_with_check(c_file, f'ret->{obj.fixname}', 'len + 1', f'*ret->{obj.fixname}', indent=3) + emit_array_parse_preamble(c_file, obj) emit(c_file, f''' for (i = 0; i < len; i++) {{ From 92f2b1d1d6635eb19d7613905e5dc600c1836710 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Tue, 14 Apr 2026 14:24:22 +0000 Subject: [PATCH 3/6] sources: extract common array generate preamble into helper The if-OPT_GEN + gen_key + len setup + beautify_off + array_open + check_gen_status block was duplicated across ObjectArrayHandler, PrimitiveArrayHandler, and BasicMapArrayHandler. Extract it into emit_array_gen_preamble(). Co-Authored-By: Claude Opus 4.6 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 69 +++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 41 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 298c04e2..11d21966 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -304,6 +304,31 @@ def emit_array_parse_preamble(c_file, obj): calloc_with_check(c_file, f'ret->{obj.fixname}_item_lens', 'len + 1', 'size_t', indent=3) +def emit_array_gen_preamble(c_file, obj, len_indent=' '): + """Emit the common preamble for array generation. + + Emits the if-OPT_GEN + gen_key + len setup + beautify_off + array_open + + check_gen_status block shared by ObjectArrayHandler, PrimitiveArrayHandler, + and BasicMapArrayHandler. + + len_indent controls indentation of the ``len = ...`` assignment: + callers at different nesting depths need different alignment. + """ + emit(c_file, f''' + if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) + {{ + size_t len = 0, i; + ''', indent=1) + emit_gen_key_with_check(c_file, obj.origname, indent=2) + emit(c_file, f''' + if (ptr != NULL && ptr->{obj.fixname} != NULL) + {len_indent}len = ptr->{obj.fixname}_len; + ''', indent=2) + emit_beautify_off(c_file, '!len', indent=2) + emit_gen_array_open(c_file, indent=2) + check_gen_status(c_file, indent=2) + + def get_compound_children(obj): """Get the children/subtypes for a compound type. @@ -1280,20 +1305,7 @@ def emit_parse(self, c_file, obj, prefix, obj_typename): def emit_generate(self, c_file, obj, prefix): typename = obj.subtypname if obj.subtypname else helpers.get_name_substr(obj.name, prefix) - emit(c_file, f''' - if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) - {{ - size_t len = 0, i; - ''', indent=1) - emit_gen_key_with_check(c_file, obj.origname, indent=2) - - emit(c_file, f''' - if (ptr != NULL && ptr->{obj.fixname} != NULL) - len = ptr->{obj.fixname}_len; - ''', indent=2) - emit_beautify_off(c_file, '!len', indent=2) - emit_gen_array_open(c_file, indent=2) - check_gen_status(c_file, indent=2) + emit_array_gen_preamble(c_file, obj) emit(c_file, ''' for (i = 0; i < len; i++) @@ -1544,20 +1556,7 @@ def emit_parse(self, c_file, obj, prefix, obj_typename): ''', indent=1) def emit_generate(self, c_file, obj, prefix): - emit(c_file, f''' - if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) - {{ - size_t len = 0, i; - ''', indent=1) - emit_gen_key_with_check(c_file, obj.origname, indent=2) - - emit(c_file, f''' - if (ptr != NULL && ptr->{obj.fixname} != NULL) - len = ptr->{obj.fixname}_len; - ''', indent=2) - emit_beautify_off(c_file, '!len', indent=2) - emit_gen_array_open(c_file, indent=2) - check_gen_status(c_file, indent=2) + emit_array_gen_preamble(c_file, obj, len_indent=' ') emit(c_file, ''' for (i = 0; i < len; i++) @@ -1711,19 +1710,7 @@ def emit_parse(self, c_file, obj, prefix, obj_typename): def emit_generate(self, c_file, obj, prefix): map_func = helpers.make_basic_map_name(obj.subtyp) - emit(c_file, f''' - if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) - {{ - size_t len = 0, i; - ''', indent=1) - emit_gen_key_with_check(c_file, obj.origname, indent=2) - emit(c_file, f''' - if (ptr != NULL && ptr->{obj.fixname} != NULL) - len = ptr->{obj.fixname}_len; - ''', indent=2) - emit_beautify_off(c_file, '!len', indent=2) - emit_gen_array_open(c_file, indent=2) - check_gen_status(c_file, indent=2) + emit_array_gen_preamble(c_file, obj) emit(c_file, f''' for (i = 0; i < len; i++) {{ From be6fbb4c06cdb33ad54fdff46379c8993014462a Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Tue, 14 Apr 2026 14:46:58 +0000 Subject: [PATCH 4/6] sources: use free_and_null() in pointer type emit_free methods StringType, BooleanPointerType, and NumericPointerType all manually inlined the free()/NULL= pattern instead of using the existing free_and_null() helper. Co-Authored-By: Claude Opus 4.6 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 11d21966..4817e62e 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -394,10 +394,7 @@ def emit_generate(self, c_file, obj, prefix, indent=1): ''', indent=indent) def emit_free(self, c_file, obj, prefix, indent=1): - emit(c_file, f''' - free (ptr->{obj.fixname}); - ptr->{obj.fixname} = NULL; - ''', indent=indent) + free_and_null(c_file, "ptr", obj.fixname, indent=indent) def emit_clone(self, c_file, obj, prefix, indent=1): emit(c_file, f''' @@ -522,10 +519,7 @@ def emit_generate(self, c_file, obj, prefix, indent=1): ''', indent=indent) def emit_free(self, c_file, obj, prefix, indent=1): - emit(c_file, f''' - free (ptr->{obj.fixname}); - ptr->{obj.fixname} = NULL; - ''', indent=indent) + free_and_null(c_file, "ptr", obj.fixname, indent=indent) def emit_clone(self, c_file, obj, prefix, indent=1): emit(c_file, f''' @@ -701,10 +695,7 @@ def emit_generate(self, c_file, obj, prefix, indent=1): ''', indent=indent) def emit_free(self, c_file, obj, prefix, indent=1): - emit(c_file, f''' - free (ptr->{obj.fixname}); - ptr->{obj.fixname} = NULL; - ''', indent=indent) + free_and_null(c_file, "ptr", obj.fixname, indent=indent) def emit_clone(self, c_file, obj, prefix, indent=1): c_typ = helpers.get_map_c_types(self.base_typ) From 37bba6c8248368a1fae1beded03a51c70c7e6079 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Tue, 14 Apr 2026 14:48:11 +0000 Subject: [PATCH 5/6] sources: extract pointer clone pattern into emit_pointer_clone() BooleanPointerType and NumericPointerType had identical clone logic (calloc + NULL check + dereference copy), differing only in sizeof type. Extract into a shared helper. Co-Authored-By: Claude Opus 4.6 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 4817e62e..883ce96d 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -329,6 +329,19 @@ def emit_array_gen_preamble(c_file, obj, len_indent=' '): check_gen_status(c_file, indent=2) +def emit_pointer_clone(c_file, obj, sizeof_type, indent=1): + """Emit clone code for a heap-allocated scalar (bool* or numeric*).""" + emit(c_file, f''' + if (src->{obj.fixname} != NULL) + {{ + ret->{obj.fixname} = calloc (1, sizeof ({sizeof_type})); + if (ret->{obj.fixname} == NULL) + return NULL; + *(ret->{obj.fixname}) = *(src->{obj.fixname}); + }} + ''', indent=indent) + + def get_compound_children(obj): """Get the children/subtypes for a compound type. @@ -522,15 +535,7 @@ def emit_free(self, c_file, obj, prefix, indent=1): free_and_null(c_file, "ptr", obj.fixname, indent=indent) def emit_clone(self, c_file, obj, prefix, indent=1): - emit(c_file, f''' - if (src->{obj.fixname} != NULL) - {{ - ret->{obj.fixname} = calloc (1, sizeof (bool)); - if (ret->{obj.fixname} == NULL) - return NULL; - *(ret->{obj.fixname}) = *(src->{obj.fixname}); - }} - ''', indent=indent) + emit_pointer_clone(c_file, obj, 'bool', indent=indent) def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): emit(c_file, f''' @@ -698,16 +703,7 @@ def emit_free(self, c_file, obj, prefix, indent=1): free_and_null(c_file, "ptr", obj.fixname, indent=indent) def emit_clone(self, c_file, obj, prefix, indent=1): - c_typ = helpers.get_map_c_types(self.base_typ) - emit(c_file, f''' - if (src->{obj.fixname} != NULL) - {{ - ret->{obj.fixname} = calloc (1, sizeof ({c_typ})); - if (ret->{obj.fixname} == NULL) - return NULL; - *(ret->{obj.fixname}) = *(src->{obj.fixname}); - }} - ''', indent=indent) + emit_pointer_clone(c_file, obj, helpers.get_map_c_types(self.base_typ), indent=indent) def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): if self.base_typ == "": From 015bb87fa29aea0e26eaffeb0f6c4baf5c82caf0 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Tue, 14 Apr 2026 14:50:00 +0000 Subject: [PATCH 6/6] sources: extract compound field generate pattern into emit_compound_gen() ObjectType, MapStringObjectType, and BasicMapType all had the same if-OPT_GEN + gen_key + stat=gen_{type} + check_gen_status structure in their emit_generate methods. Extract into a shared helper. Co-Authored-By: Claude Opus 4.6 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 55 +++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 883ce96d..c1f20c3a 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -329,6 +329,22 @@ def emit_array_gen_preamble(c_file, obj, len_indent=' '): check_gen_status(c_file, indent=2) +def emit_compound_gen(c_file, obj, key_name, gen_name, ptr_check='ptr != NULL', indent=1): + """Emit generate code for a compound field (object, mapStringObject, basicMap).""" + emit(c_file, f''' + if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) + {{ + ''', indent=indent) + emit_gen_key_with_check(c_file, key_name, indent=indent + 1) + emit(c_file, f''' + stat = gen_{gen_name} (g, {ptr_check} ? ptr->{obj.fixname} : NULL, ctx, err); + ''', indent=indent + 1) + check_gen_status(c_file, indent=indent + 1) + emit(c_file, ''' + } + ''', indent=indent) + + def emit_pointer_clone(c_file, obj, sizeof_type, indent=1): """Emit clone code for a heap-allocated scalar (bool* or numeric*).""" emit(c_file, f''' @@ -743,18 +759,7 @@ def emit_parse(self, c_file, obj, prefix, obj_typename, indent=1): def emit_generate(self, c_file, obj, prefix, indent=1): typename = obj.subtypname or helpers.get_prefixed_name(obj.name, prefix) - emit(c_file, f''' - if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) - {{ - ''', indent=indent) - emit_gen_key_with_check(c_file, obj.origname, indent=indent + 1) - emit(c_file, f''' - stat = gen_{typename} (g, ptr != NULL ? ptr->{obj.fixname} : NULL, ctx, err); - ''', indent=indent + 1) - check_gen_status(c_file, indent=indent + 1) - emit(c_file, ''' - } - ''', indent=indent) + emit_compound_gen(c_file, obj, obj.origname, typename, indent=indent) def emit_free(self, c_file, obj, prefix, indent=1): typename = obj.subtypname or helpers.get_prefixed_name(obj.name, prefix) @@ -928,18 +933,7 @@ def emit_parse(self, c_file, obj, prefix, obj_typename, indent=1): def emit_generate(self, c_file, obj, prefix, indent=1): typename = obj.subtypname or helpers.get_prefixed_name(obj.name, prefix) - emit(c_file, f''' - if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) - {{ - ''', indent=indent) - emit_gen_key_with_check(c_file, obj.origname, indent=indent + 1) - emit(c_file, f''' - stat = gen_{typename} (g, ptr != NULL ? ptr->{obj.fixname} : NULL, ctx, err); - ''', indent=indent + 1) - check_gen_status(c_file, indent=indent + 1) - emit(c_file, ''' - } - ''', indent=indent) + emit_compound_gen(c_file, obj, obj.origname, typename, indent=indent) def emit_free(self, c_file, obj, prefix, indent=1): free_func = obj.subtypname or helpers.get_prefixed_name(obj.name, prefix) @@ -1168,18 +1162,7 @@ def emit_parse(self, c_file, obj, prefix, obj_typename, indent=1): ''', indent=indent) def emit_generate(self, c_file, obj, prefix, indent=1): - emit(c_file, f''' - if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) - {{ - ''', indent=indent) - emit_gen_key_with_check(c_file, obj.fixname, indent=indent + 1) - emit(c_file, f''' - stat = gen_{self.map_name} (g, ptr ? ptr->{obj.fixname} : NULL, ctx, err); - ''', indent=indent + 1) - check_gen_status(c_file, indent=indent + 1) - emit(c_file, ''' - } - ''', indent=indent) + emit_compound_gen(c_file, obj, obj.fixname, self.map_name, ptr_check='ptr', indent=indent) def emit_free(self, c_file, obj, prefix, indent=1): emit(c_file, f'''