From c8a5437c7a42efa6eb3664cfa2592eb17f1eee75 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 14:11:35 +0000 Subject: [PATCH 01/51] gitignore: update Signed-off-by: Giuseppe Scrivano --- .gitignore | 120 +++++++++++++++++++---------------------------------- 1 file changed, 42 insertions(+), 78 deletions(-) diff --git a/.gitignore b/.gitignore index e65c58f1..50a39501 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,23 @@ *.a aclocal.m4 autom4te.cache/ + +# Generated files in root directory +/config_*.c +/config_*.h +/defs*.c +/defs*.h +/features_*.c +/features_*.h +/state_*.c +/state_*.h +/basic_test_* +/test_* +/*_schema.c +/*_schema.h +/libocispec-*.tar.* +/libocispec-*/ +/target/ build-aux/compile build-aux/config.guess build-aux/config.sub @@ -24,87 +41,34 @@ Makefile Makefile.in *.o ocispec.pc -src/basic_test_double_array.c -src/basic_test_double_array.h -src/basic_test_double_array_item.c -src/basic_test_double_array_item.h -src/basic-test_stamp -src/basic_test_top_array_int.c -src/basic_test_top_array_int.h -src/basic_test_top_array_string.c -src/basic_test_top_array_string.h -src/basic_test_top_double_array_int.c -src/basic_test_top_double_array_int.h -src/basic_test_top_double_array_obj.c -src/basic_test_top_double_array_obj.h -src/basic_test_top_double_array_refobj.c -src/basic_test_top_double_array_refobj.h -src/basic_test_top_double_array_string.c -src/basic_test_top_double_array_string.h -src/.deps/ -src/.dirstamp -src/image_manifest_items_image_manifest_items_schema.c -src/image_manifest_items_image_manifest_items_schema.h -src/image_manifest_stamp -src/image_spec_schema_config_schema.c -src/image_spec_schema_config_schema.h -src/image_spec_schema_content_descriptor.c -src/image_spec_schema_content_descriptor.h -src/image_spec_schema_defs.c -src/image_spec_schema_defs_descriptor.c -src/image_spec_schema_defs_descriptor.h -src/image_spec_schema_defs.h -src/image_spec_schema_image_index_schema.c -src/image_spec_schema_image_index_schema.h -src/image_spec_schema_image_layout_schema.c -src/image_spec_schema_image_layout_schema.h -src/image_spec_schema_image_manifest_schema.c -src/image_spec_schema_image_manifest_schema.h -src/image_spec_stamp -src/*.lo -src/__pycache__/ -src/runtime_spec_schema_config_linux.c -src/runtime_spec_schema_config_linux.h -src/runtime_spec_schema_config_zos.c -src/runtime_spec_schema_config_zos.h -src/runtime_spec_schema_config_schema.c -src/runtime_spec_schema_config_schema.h -src/runtime_spec_schema_config_solaris.c -src/runtime_spec_schema_config_solaris.h -src/runtime_spec_schema_config_vm.c -src/runtime_spec_schema_config_vm.h -src/runtime_spec_schema_config_windows.c -src/runtime_spec_schema_config_windows.h -src/runtime_spec_schema_defs.c -src/runtime_spec_schema_defs.h -src/runtime_spec_schema_defs_linux.c -src/runtime_spec_schema_defs_linux.h -src/runtime_spec_schema_defs_zos.c -src/runtime_spec_schema_defs_zos.h -src/runtime_spec_schema_defs_vm.c -src/runtime_spec_schema_defs_vm.h -src/runtime_spec_schema_defs_windows.c -src/runtime_spec_schema_defs_windows.h -src/runtime_spec_schema_state_schema.c -src/runtime_spec_schema_state_schema.h -src/runtime_spec_stamp -src/validate stamp-h1 +*_stamp + +# Generated files in src/ocispec/ +src/ocispec/.dirstamp +src/ocispec/.deps/ +src/ocispec/__pycache__/ +src/ocispec/*.lo +src/ocispec/*.o +src/ocispec/*_stamp +src/ocispec/basic_test_* +src/ocispec/test_* +src/ocispec/image_manifest_* +src/ocispec/image_spec_* +src/ocispec/runtime_spec_* +src/ocispec/*_schema.* +src/ocispec/config_* +src/ocispec/defs_* +src/ocispec/defs.* +src/ocispec/features_* +src/ocispec/state_* +src/ocispec/validate + tests/.deps/ tests/.dirstamp -tests/test-1 -tests/test-10 -tests/test-11 -tests/test-2 -tests/test-3 -tests/test-4 -tests/test-5 -tests/test-6 -tests/test-7 -tests/test-8 -tests/test-9 -tests/test-*.log -tests/test-*.trs +tests/test-[0-9]* +tests/*.log +tests/*.trs test-suite.log build-aux/config.guess~ build-aux/config.sub~ From 7579c0386ec9a984dd7f669e0f2f0c0f7df9b0ee Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 10:46:24 +0000 Subject: [PATCH 02/51] source: add emit() helper function for code generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce emit() helper to simplify emitting multi-line code blocks with proper indentation. This function uses textwrap.dedent() to allow writing code blocks naturally in Python while handling C indentation automatically. This is preparation for refactoring individual c_file.append() calls into consolidated multi-line f-strings. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 05aa7a3f..22532872 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -28,8 +28,26 @@ # libocispec output files to be licensed under the GNU General Public # License without this special exception. +from textwrap import dedent + import helpers + +def emit(c_file, code, indent=0): + """Emit code with proper indentation. + + Args: + c_file: List to append code lines to + code: Multi-line string (will be dedented) + indent: Number of 4-space indentation levels + """ + prefix = ' ' * indent + for line in dedent(code).strip().split('\n'): + if line: + c_file.append(prefix + line + '\n') + else: + c_file.append('\n') + def append_c_code(obj, c_file, prefix): """ Description: append c language code to file From 064fcbb321b89cf943bea41672ad5216623f560a Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 10:48:31 +0000 Subject: [PATCH 03/51] source: extract free_and_null() helper pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add free_and_null() helper function to consolidate the common pattern of freeing a pointer and setting it to NULL. This reduces code duplication and makes the intent more explicit. Replaced ~15 occurrences across the codebase where free() is followed immediately by setting the pointer to NULL. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 57 +++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 22532872..18d237f7 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -48,6 +48,21 @@ def emit(c_file, code, indent=0): else: c_file.append('\n') + +def free_and_null(c_file, ptr, field, indent=0): + """Generate code to free a pointer and set it to NULL. + + Args: + c_file: List to append code lines to + ptr: Pointer variable name + field: Field name (can include array indexing like '[i]') + indent: Number of 4-space indentation levels + """ + prefix = ' ' * indent + c_file.append(f"{prefix}free ({ptr}->{field});\n") + c_file.append(f"{prefix}{ptr}->{field} = NULL;\n") + + def append_c_code(obj, c_file, prefix): """ Description: append c language code to file @@ -1054,8 +1069,7 @@ def make_c_array_free (i, c_file, prefix): c_file.append(f" ptr->{i.fixname}[i] = NULL;\n") c_file.append(" }\n") c_file.append(" }\n") - c_file.append(f" free (ptr->{i.fixname});\n") - c_file.append(f" ptr->{i.fixname} = NULL;\n") + free_and_null(c_file, "ptr", i.fixname, indent=2) c_file.append(" }\n") elif i.subtyp == 'string': c_file_str(c_file, i) @@ -1065,13 +1079,10 @@ def make_c_array_free (i, c_file, prefix): c_file.append(" size_t i;\n") c_file.append(f" for (i = 0; i < ptr->{i.fixname}_len; i++)\n") c_file.append(" {\n") - c_file.append(f" free (ptr->{i.fixname}[i]);\n") - c_file.append(f" ptr->{i.fixname}[i] = NULL;\n") + free_and_null(c_file, "ptr", f"{i.fixname}[i]", indent=4) c_file.append(" }\n") - c_file.append(f" free (ptr->{i.fixname}_item_lens);\n") - c_file.append(f" ptr->{i.fixname}_item_lens = NULL;\n") - c_file.append(f" free (ptr->{i.fixname});\n") - c_file.append(f" ptr->{i.fixname} = NULL;\n") + free_and_null(c_file, "ptr", f"{i.fixname}_item_lens", indent=3) + free_and_null(c_file, "ptr", i.fixname, indent=2) c_file.append(" }\n") elif i.subtyp == 'object' or i.subtypobj is not None: if i.subtypname is not None: @@ -1090,8 +1101,7 @@ def make_c_array_free (i, c_file, prefix): c_file.append(f" free_{free_func} (ptr->{i.fixname}[i][j]);\n") c_file.append(f" ptr->{i.fixname}[i][j] = NULL;\n") c_file.append(" }\n") - c_file.append(f" free (ptr->{i.fixname}[i]);\n") - c_file.append(f" ptr->{i.fixname}[i] = NULL;\n") + free_and_null(c_file, "ptr", f"{i.fixname}[i]", indent=2) else: c_file.append(f" if (ptr->{i.fixname}[i] != NULL)\n") c_file.append(" {\n") @@ -1100,11 +1110,9 @@ def make_c_array_free (i, c_file, prefix): c_file.append(" }\n") c_file.append(" }\n") if i.doublearray: - c_file.append(f" free (ptr->{i.fixname}_item_lens);\n") - c_file.append(f" ptr->{i.fixname}_item_lens = NULL;\n") + free_and_null(c_file, "ptr", f"{i.fixname}_item_lens", indent=2) - c_file.append(f" free (ptr->{i.fixname});\n") - c_file.append(f" ptr->{i.fixname} = NULL;\n") + free_and_null(c_file, "ptr", i.fixname, indent=2) c_file.append(" }\n") c_typ = helpers.obtain_pointer(i.name, i.subtypobj, prefix) if c_typ == "": @@ -1196,15 +1204,12 @@ def c_file_map_str(c_file, child, childname): c_file.append(" size_t i;\n") c_file.append(" for (i = 0; i < ptr->len; i++)\n") c_file.append(" {\n") - c_file.append(" free (ptr->keys[i]);\n") - c_file.append(" ptr->keys[i] = NULL;\n") + free_and_null(c_file, "ptr", "keys[i]", indent=3) c_file.append(f" free_{childname} (ptr->{child.fixname}[i]);\n") c_file.append(f" ptr->{child.fixname}[i] = NULL;\n") c_file.append(" }\n") - c_file.append(" free (ptr->keys);\n") - c_file.append(" ptr->keys = NULL;\n") - c_file.append(f" free (ptr->{child.fixname});\n") - c_file.append(f" ptr->{child.fixname} = NULL;\n") + free_and_null(c_file, "ptr", "keys", indent=2) + free_and_null(c_file, "ptr", child.fixname, indent=2) c_file.append(" }\n") def c_file_str(c_file, i): @@ -1222,20 +1227,16 @@ def c_file_str(c_file, i): c_file.append(" size_t j;\n") c_file.append(f" for (j = 0; j < ptr->{i.fixname}_item_lens[i]; j++)\n") c_file.append(" {\n") - c_file.append(f" free (ptr->{i.fixname}[i][j]);\n") - c_file.append(f" ptr->{i.fixname}[i][j] = NULL;\n") + free_and_null(c_file, "ptr", f"{i.fixname}[i][j]", indent=4) c_file.append(" }\n") c_file.append(f" if (ptr->{i.fixname}[i] != NULL)\n") c_file.append(" {\n") - c_file.append(f" free (ptr->{i.fixname}[i]);\n") - c_file.append(f" ptr->{i.fixname}[i] = NULL;\n") + free_and_null(c_file, "ptr", f"{i.fixname}[i]", indent=4) c_file.append(" }\n") c_file.append(" }\n") if i.doublearray: - c_file.append(f" free (ptr->{i.fixname}_item_lens);\n") - c_file.append(f" ptr->{i.fixname}_item_lens = NULL;\n") - c_file.append(f" free (ptr->{i.fixname});\n") - c_file.append(f" ptr->{i.fixname} = NULL;\n") + free_and_null(c_file, "ptr", f"{i.fixname}_item_lens", indent=2) + free_and_null(c_file, "ptr", i.fixname, indent=2) c_file.append(" }\n") From 419526ad995082c557fa0b928fb1df2a2534b156 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 10:50:22 +0000 Subject: [PATCH 04/51] source: extract null_check_return() helper pattern (partial) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add null_check_return() helper function to consolidate the common pattern of checking if a variable is NULL and returning NULL. This reduces code duplication and makes the intent more explicit. Replaced several occurrences in parse_map_string_obj() and parse_obj_type_array() functions. More occurrences remain to be replaced in subsequent commits. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 46 ++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 18d237f7..ed7c1dde 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -63,6 +63,19 @@ def free_and_null(c_file, ptr, field, indent=0): c_file.append(f"{prefix}{ptr}->{field} = NULL;\n") +def null_check_return(c_file, var, indent=0): + """Generate NULL check with return NULL. + + Args: + c_file: List to append code lines to + var: Variable to check (can be expression like 'ret->field' or 'ret->field[i]') + indent: Number of 4-space indentation levels + """ + prefix = ' ' * indent + c_file.append(f"{prefix}if ({var} == NULL)\n") + c_file.append(f"{prefix} return NULL;\n") + + def append_c_code(obj, c_file, prefix): """ Description: append c language code to file @@ -96,22 +109,18 @@ def parse_map_string_obj(obj, c_file, prefix, obj_typename): c_file.append(' yajl_val *values = YAJL_GET_OBJECT_NO_CHECK (tree)->values;\n') c_file.append(' ret->len = len;\n') c_file.append(' ret->keys = calloc (len + 1, sizeof (*ret->keys));\n') - c_file.append(' if (ret->keys == NULL)\n') - c_file.append(' return NULL;\n') + null_check_return(c_file, 'ret->keys', indent=2) c_file.append(f' ret->{child.fixname} = calloc (len + 1, sizeof (*ret->{child.fixname}));\n') - c_file.append(f' if (ret->{child.fixname} == NULL)\n') - c_file.append(' return NULL;\n') + null_check_return(c_file, f'ret->{child.fixname}', indent=2) c_file.append(' for (i = 0; i < len; i++)\n') c_file.append(' {\n') c_file.append(' yajl_val val;\n') c_file.append(' const char *tmpkey = keys[i];\n') c_file.append(' ret->keys[i] = strdup (tmpkey ? tmpkey : "");\n') - c_file.append(' if (ret->keys[i] == NULL)\n') - c_file.append(" return NULL;\n") + null_check_return(c_file, 'ret->keys[i]', indent=3) c_file.append(' val = values[i];\n') c_file.append(f' ret->{child.fixname}[i] = make_{childname} (val, ctx, err);\n') - c_file.append(f' if (ret->{child.fixname}[i] == NULL)\n') - c_file.append(" return NULL;\n") + null_check_return(c_file, f'ret->{child.fixname}[i]', indent=3) c_file.append(' }\n') c_file.append(' }\n') @@ -132,32 +141,27 @@ def parse_obj_type_array(obj, c_file, prefix, obj_typename): c_file.append(' yajl_val *values = YAJL_GET_ARRAY_NO_CHECK (tmp)->values;\n') c_file.append(f' ret->{obj.fixname}_len = len;\n') c_file.append(f' ret->{obj.fixname} = calloc (len + 1, sizeof (*ret->{obj.fixname}));\n') - c_file.append(f' if (ret->{obj.fixname} == NULL)\n') - c_file.append(' return NULL;\n') + null_check_return(c_file, f'ret->{obj.fixname}', indent=3) if obj.doublearray: c_file.append(f' ret->{obj.fixname}_item_lens = calloc ( len + 1, sizeof (size_t));\n') - c_file.append(f' if (ret->{obj.fixname}_item_lens == NULL)\n') - c_file.append(' return NULL;\n') + null_check_return(c_file, f'ret->{obj.fixname}_item_lens', indent=4) c_file.append(' for (i = 0; i < len; i++)\n') c_file.append(' {\n') c_file.append(' yajl_val val = values[i];\n') if obj.doublearray: c_file.append(' size_t j;\n') c_file.append(f' ret->{obj.fixname}[i] = calloc ( YAJL_GET_ARRAY_NO_CHECK(val)->len + 1, sizeof (**ret->{obj.fixname}));\n') - c_file.append(f' if (ret->{obj.fixname}[i] == NULL)\n') - c_file.append(' return NULL;\n') + null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=4) c_file.append(' yajl_val *items = YAJL_GET_ARRAY_NO_CHECK(val)->values;\n') c_file.append(' for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(val)->len; j++)\n') c_file.append(' {\n') c_file.append(f' ret->{obj.fixname}[i][j] = make_{typename} (items[j], ctx, err);\n') - c_file.append(f' if (ret->{obj.fixname}[i][j] == NULL)\n') - c_file.append(" return NULL;\n") + null_check_return(c_file, f'ret->{obj.fixname}[i][j]', indent=5) c_file.append(f' ret->{obj.fixname}_item_lens[i] += 1;\n') c_file.append(' };\n') else: c_file.append(f' ret->{obj.fixname}[i] = make_{typename} (val, ctx, err);\n') - c_file.append(f' if (ret->{obj.fixname}[i] == NULL)\n') - c_file.append(" return NULL;\n") + null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=4) c_file.append(' }\n') c_file.append(' }\n') c_file.append(' }\n') @@ -201,12 +205,10 @@ def parse_obj_type_array(obj, c_file, prefix, obj_typename): c_file.append(' yajl_val *values = YAJL_GET_ARRAY_NO_CHECK (tmp)->values;\n') c_file.append(f' ret->{obj.fixname}_len = len;\n') c_file.append(f' ret->{obj.fixname} = calloc (len + 1, sizeof (*ret->{obj.fixname}));\n') - c_file.append(f' if (ret->{obj.fixname} == NULL)\n') - c_file.append(' return NULL;\n') + null_check_return(c_file, f'ret->{obj.fixname}', indent=3) if obj.doublearray: c_file.append(f' ret->{obj.fixname}_item_lens = calloc ( len + 1, sizeof (size_t));\n') - c_file.append(f' if (ret->{obj.fixname}_item_lens == NULL)\n') - c_file.append(' return NULL;\n') + null_check_return(c_file, f'ret->{obj.fixname}_item_lens', indent=4) c_file.append(' for (i = 0; i < len; i++)\n') c_file.append(' {\n') if obj.doublearray: From cc817ed3d53000a072d7a4aa748cb09cd4afe80e Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 10:50:59 +0000 Subject: [PATCH 05/51] source: continue null_check_return() pattern extraction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace more occurrences of the NULL check pattern in parse_obj_type_array() for byte type handling. More replacements remain to be done in subsequent commits for full coverage. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index ed7c1dde..8023e26b 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -175,21 +175,18 @@ def parse_obj_type_array(obj, c_file, prefix, obj_typename): if obj.doublearray: c_file.append(' yajl_val *items = YAJL_GET_ARRAY_NO_CHECK(tmp)->values;\n') c_file.append(f' ret->{obj.fixname} = calloc ( YAJL_GET_ARRAY_NO_CHECK(tmp)->len + 1, sizeof (*ret->{obj.fixname}));\n') - c_file.append(f' if (ret->{obj.fixname}[i] == NULL)\n') - c_file.append(' return NULL;\n') + null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=4) c_file.append(' size_t j;\n') c_file.append(' for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(tmp)->len; j++)\n') c_file.append(' {\n') c_file.append(' char *str = YAJL_GET_STRING (itmes[j]);\n') c_file.append(f' ret->{obj.fixname}[j] = (uint8_t *)strdup (str ? str : "");\n') - c_file.append(f' if (ret->{obj.fixname}[j] == NULL)\n') - c_file.append(" return NULL;\n") + null_check_return(c_file, f'ret->{obj.fixname}[j]', indent=5) c_file.append(' };\n') else: c_file.append(' char *str = YAJL_GET_STRING (tmp);\n') c_file.append(f' ret->{obj.fixname} = (uint8_t *)strdup (str ? str : "");\n') - c_file.append(f' if (ret->{obj.fixname} == NULL)\n') - c_file.append(' return NULL;\n') + null_check_return(c_file, f'ret->{obj.fixname}', indent=3) c_file.append(f' ret->{obj.fixname}_len = str != NULL ? strlen (str) : 0;\n') c_file.append(' }\n') c_file.append(' }\n') From 46ce29782c621baaa7db306519a3d84056ed83f1 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 10:52:08 +0000 Subject: [PATCH 06/51] source: extract calloc_with_check() helper pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add calloc_with_check() helper function to consolidate the common pattern of calling calloc() followed by NULL checking and returning NULL on failure. This combines the calloc allocation with error handling in a single call, reducing code duplication and making the intent clearer. Replaced several occurrences in parse_map_string_obj() and parse_obj_type_array() functions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 8023e26b..a86e8197 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -76,6 +76,22 @@ def null_check_return(c_file, var, indent=0): c_file.append(f"{prefix} return NULL;\n") +def calloc_with_check(c_file, dest, count, sizeof_expr, indent=0): + """Generate calloc call with NULL check. + + Args: + c_file: List to append code lines to + dest: Destination variable + count: Count expression for calloc + sizeof_expr: sizeof expression (the content inside sizeof()) + indent: Number of 4-space indentation levels + """ + prefix = ' ' * indent + c_file.append(f"{prefix}{dest} = calloc ({count}, sizeof ({sizeof_expr}));\n") + c_file.append(f"{prefix}if ({dest} == NULL)\n") + c_file.append(f"{prefix} return NULL;\n") + + def append_c_code(obj, c_file, prefix): """ Description: append c language code to file @@ -108,10 +124,8 @@ def parse_map_string_obj(obj, c_file, prefix, obj_typename): c_file.append(' const char **keys = YAJL_GET_OBJECT_NO_CHECK (tree)->keys;\n') c_file.append(' yajl_val *values = YAJL_GET_OBJECT_NO_CHECK (tree)->values;\n') c_file.append(' ret->len = len;\n') - c_file.append(' ret->keys = calloc (len + 1, sizeof (*ret->keys));\n') - null_check_return(c_file, 'ret->keys', indent=2) - c_file.append(f' ret->{child.fixname} = calloc (len + 1, sizeof (*ret->{child.fixname}));\n') - null_check_return(c_file, f'ret->{child.fixname}', indent=2) + calloc_with_check(c_file, 'ret->keys', 'len + 1', '*ret->keys', indent=2) + calloc_with_check(c_file, f'ret->{child.fixname}', 'len + 1', f'*ret->{child.fixname}', indent=2) c_file.append(' for (i = 0; i < len; i++)\n') c_file.append(' {\n') c_file.append(' yajl_val val;\n') @@ -140,11 +154,9 @@ def parse_obj_type_array(obj, c_file, prefix, obj_typename): c_file.append(' size_t len = YAJL_GET_ARRAY_NO_CHECK (tmp)->len;\n') c_file.append(' yajl_val *values = YAJL_GET_ARRAY_NO_CHECK (tmp)->values;\n') c_file.append(f' ret->{obj.fixname}_len = len;\n') - c_file.append(f' ret->{obj.fixname} = calloc (len + 1, sizeof (*ret->{obj.fixname}));\n') - null_check_return(c_file, f'ret->{obj.fixname}', indent=3) + calloc_with_check(c_file, f'ret->{obj.fixname}', 'len + 1', f'*ret->{obj.fixname}', indent=3) if obj.doublearray: - c_file.append(f' ret->{obj.fixname}_item_lens = calloc ( len + 1, sizeof (size_t));\n') - null_check_return(c_file, f'ret->{obj.fixname}_item_lens', indent=4) + calloc_with_check(c_file, f'ret->{obj.fixname}_item_lens', 'len + 1', 'size_t', indent=3) c_file.append(' for (i = 0; i < len; i++)\n') c_file.append(' {\n') c_file.append(' yajl_val val = values[i];\n') @@ -201,11 +213,9 @@ def parse_obj_type_array(obj, c_file, prefix, obj_typename): c_file.append(' size_t len = YAJL_GET_ARRAY_NO_CHECK (tmp)->len;\n') c_file.append(' yajl_val *values = YAJL_GET_ARRAY_NO_CHECK (tmp)->values;\n') c_file.append(f' ret->{obj.fixname}_len = len;\n') - c_file.append(f' ret->{obj.fixname} = calloc (len + 1, sizeof (*ret->{obj.fixname}));\n') - null_check_return(c_file, f'ret->{obj.fixname}', indent=3) + calloc_with_check(c_file, f'ret->{obj.fixname}', 'len + 1', f'*ret->{obj.fixname}', indent=3) if obj.doublearray: - c_file.append(f' ret->{obj.fixname}_item_lens = calloc ( len + 1, sizeof (size_t));\n') - null_check_return(c_file, f'ret->{obj.fixname}_item_lens', indent=4) + calloc_with_check(c_file, f'ret->{obj.fixname}_item_lens', 'len + 1', 'size_t', indent=3) c_file.append(' for (i = 0; i < len; i++)\n') c_file.append(' {\n') if obj.doublearray: From e80d3629863e55f7a590359470f16213e41a463b Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 10:59:01 +0000 Subject: [PATCH 07/51] source: extract check_gen_status() helper pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add check_gen_status() helper function to consolidate the common pattern of checking yajl_gen status and returning error on failure. This pattern appears ~50+ times in the codebase. Replaced the majority of occurrences across generate functions in get_map_string_obj(), get_obj_arr_obj_array(), and get_c_json(). A few occurrences with dynamic indentation (using level variable) remain and will be handled separately. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 105 +++++++++++++++++------------------------ 1 file changed, 43 insertions(+), 62 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index a86e8197..cce6cada 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -92,6 +92,18 @@ def calloc_with_check(c_file, dest, count, sizeof_expr, indent=0): c_file.append(f"{prefix} return NULL;\n") +def check_gen_status(c_file, indent=0): + """Generate yajl_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} GEN_SET_ERROR_AND_RETURN (stat, err);\n") + + def append_c_code(obj, c_file, prefix): """ Description: append c language code to file @@ -448,8 +460,7 @@ def get_map_string_obj(obj, c_file, prefix): c_file.append(" if (!len && !(ctx->options & OPT_GEN_SIMPLIFY))\n") c_file.append(' yajl_gen_config (g, yajl_gen_beautify, 0);\n') c_file.append(" stat = yajl_gen_map_open ((yajl_gen) g);\n") - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=1) c_file.append(f' if (len || (ptr != NULL && ptr->keys != NULL && ptr->{child.fixname} != NULL))\n') c_file.append(' {\n') c_file.append(' for (i = 0; i < len; i++)\n') @@ -457,16 +468,13 @@ def get_map_string_obj(obj, c_file, prefix): c_file.append(' char *str = ptr->keys[i] ? ptr->keys[i] : "";\n') c_file.append(' stat = yajl_gen_string ((yajl_gen) g, \ (const unsigned char *)str, strlen (str));\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=3) c_file.append(f' stat = gen_{childname} (g, ptr->{child.fixname}[i], ctx, err);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=3) c_file.append(' }\n') c_file.append(' }\n') c_file.append(" stat = yajl_gen_map_close ((yajl_gen) g);\n") - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=1) c_file.append(" if (!len && !(ctx->options & OPT_GEN_SIMPLIFY))\n") c_file.append(' yajl_gen_config (g, yajl_gen_beautify, 1);\n') @@ -481,39 +489,33 @@ def get_obj_arr_obj_array(obj, c_file, prefix): c_file.append(' {\n') c_file.append(' size_t len = 0, i;\n') c_file.append(f' stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {int(l)} /* strlen ("{obj.origname}") */);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=2) c_file.append(f" if (ptr != NULL && ptr->{obj.fixname} != NULL)\n") c_file.append(f" len = ptr->{obj.fixname}_len;\n") c_file.append(" if (!len && !(ctx->options & OPT_GEN_SIMPLIFY))\n") c_file.append(' yajl_gen_config (g, yajl_gen_beautify, 0);\n') c_file.append(' stat = yajl_gen_array_open ((yajl_gen) g);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=2) c_file.append(' for (i = 0; i < len; i++)\n') c_file.append(' {\n') if obj.doublearray: c_file.append(' stat = yajl_gen_array_open ((yajl_gen) g);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=3) c_file.append(" size_t j;\n") c_file.append(f' for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++)\n') c_file.append(' {\n') c_file.append(f' stat = gen_{typename} (g, ptr->{obj.fixname}[i][j], ctx, err);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=4) c_file.append(' }\n') c_file.append(' stat = yajl_gen_array_close ((yajl_gen) g);\n') else: c_file.append(f' stat = gen_{typename} (g, ptr->{obj.fixname}[i], ctx, err);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=3) c_file.append(' }\n') c_file.append(' stat = yajl_gen_array_close ((yajl_gen) g);\n') c_file.append(" if (!len && !(ctx->options & OPT_GEN_SIMPLIFY))\n") c_file.append(' yajl_gen_config (g, yajl_gen_beautify, 1);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=2) c_file.append(' }\n') elif obj.subtyp == 'byte': l = len(obj.origname) @@ -522,12 +524,10 @@ def get_obj_arr_obj_array(obj, c_file, prefix): c_file.append(' const char *str = "";\n') c_file.append(' size_t len = 0;\n') c_file.append(f' stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=2) if obj.doublearray: c_file.append(' stat = yajl_gen_array_open ((yajl_gen) g);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=3) c_file.append(" {\n") c_file.append(" size_t i;\n") c_file.append(f" for (i = 0; i < ptr->{obj.fixname}_len; i++)\n") @@ -549,8 +549,7 @@ def get_obj_arr_obj_array(obj, c_file, prefix): c_file.append(" }\n") c_file.append(' stat = yajl_gen_string ((yajl_gen) g, \ (const unsigned char *)str, len);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=2) c_file.append(" }\n") else: l = len(obj.origname) @@ -558,23 +557,20 @@ def get_obj_arr_obj_array(obj, c_file, prefix): c_file.append(' {\n') c_file.append(' size_t len = 0, i;\n') c_file.append(f' stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=2) c_file.append(f" if (ptr != NULL && ptr->{obj.fixname} != NULL)\n") c_file.append(f" len = ptr->{obj.fixname}_len;\n") c_file.append(" if (!len && !(ctx->options & OPT_GEN_SIMPLIFY))\n") c_file.append(' yajl_gen_config (g, yajl_gen_beautify, 0);\n') c_file.append(' stat = yajl_gen_array_open ((yajl_gen) g);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=2) c_file.append(' for (i = 0; i < len; i++)\n') c_file.append(' {\n') if obj.doublearray: typename = helpers.get_map_c_types(obj.subtyp) c_file.append(' stat = yajl_gen_array_open ((yajl_gen) g);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=3) c_file.append(" size_t j;\n") c_file.append(f' for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++)\n') c_file.append(' {\n') @@ -586,8 +582,7 @@ def get_obj_arr_obj_array(obj, c_file, prefix): c_file.append(' }\n') c_file.append(' stat = yajl_gen_array_close ((yajl_gen) g);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=2) c_file.append(" if (!len && !(ctx->options & OPT_GEN_SIMPLIFY))\n") c_file.append(' yajl_gen_config (g, yajl_gen_beautify, 1);\n') c_file.append(' }\n') @@ -604,8 +599,7 @@ def get_obj_arr_obj(obj, c_file, prefix): c_file.append(' {\n') c_file.append(' char *str = "";\n') c_file.append(f' stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=2) c_file.append(f" if (ptr != NULL && ptr->{obj.fixname} != NULL)\n") c_file.append(f" str = ptr->{obj.fixname};\n") json_value_generator(c_file, 2, "str", 'g', 'ctx', obj.typ) @@ -622,8 +616,7 @@ def get_obj_arr_obj(obj, c_file, prefix): l = len(obj.origname) c_file.append(f' {numtyp} num = 0;\n') c_file.append(f' stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=2) c_file.append(f" if (ptr != NULL && ptr->{obj.fixname})\n") c_file.append(f" num = ({numtyp})ptr->{obj.fixname};\n") json_value_generator(c_file, 2, "num", 'g', 'ctx', obj.typ) @@ -638,8 +631,7 @@ def get_obj_arr_obj(obj, c_file, prefix): c_file.append(f' {helpers.get_map_c_types(numtyp)} num = 0;\n') c_file.append(f' stat = yajl_gen_string ((yajl_gen) g, \ (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=2) c_file.append(f" if (ptr != NULL && ptr->{obj.fixname} != NULL)\n") c_file.append(" {\n") c_file.append(f" num = ({helpers.get_map_c_types(numtyp)})*(ptr->{obj.fixname});\n") @@ -652,8 +644,7 @@ def get_obj_arr_obj(obj, c_file, prefix): c_file.append(' bool b = false;\n') l = len(obj.origname) c_file.append(f' stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=2) c_file.append(f" if (ptr != NULL && ptr->{obj.fixname})\n") c_file.append(f" b = ptr->{obj.fixname};\n") c_file.append(" \n") @@ -668,11 +659,9 @@ def get_obj_arr_obj(obj, c_file, prefix): c_file.append(f' if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL))\n') c_file.append(" {\n") c_file.append(f' stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=2) c_file.append(f' stat = gen_{typename} (g, ptr != NULL ? ptr->{obj.fixname} : NULL, ctx, err);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=2) c_file.append(" }\n") elif obj.typ == 'array': get_obj_arr_obj_array(obj, c_file, prefix) @@ -681,11 +670,9 @@ def get_obj_arr_obj(obj, c_file, prefix): c_file.append(f' if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL))\n') c_file.append(' {\n') c_file.append(f' stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.fixname}"), {l} /* strlen ("{obj.fixname}") */);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=2) c_file.append(f' stat = gen_{helpers.make_basic_map_name(obj.typ)} (g, ptr ? ptr->{obj.fixname} : NULL, ctx, err);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=2) c_file.append(" }\n") @@ -720,8 +707,7 @@ def get_c_json(obj, c_file, prefix): c_file.append(' yajl_gen_config (g, yajl_gen_beautify, 0);\n') c_file.append(" stat = yajl_gen_map_open ((yajl_gen) g);\n") - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=1) for i in nodes or []: get_obj_arr_obj(i, c_file, prefix) if obj.typ == 'object': @@ -733,8 +719,7 @@ def get_c_json(obj, c_file, prefix): c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") c_file.append(" }\n") c_file.append(" stat = yajl_gen_map_close ((yajl_gen) g);\n") - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=1) if nodes is None: c_file.append(' if (!(ctx->options & OPT_GEN_SIMPLIFY))\n') c_file.append(' yajl_gen_config (g, yajl_gen_beautify, 1);\n') @@ -1494,8 +1479,7 @@ def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): c_file.append(' {\n') if obj.doublearray: c_file.append(' stat = yajl_gen_array_open ((yajl_gen) g);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=3) c_file.append(" size_t j;\n") c_file.append(' for (j = 0; j < ptr->subitem_lens[i]; j++)\n') c_file.append(' {\n') @@ -1506,8 +1490,7 @@ def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): c_file.append(' stat = yajl_gen_array_close ((yajl_gen) g);\n') else: c_file.append(f' stat = gen_{subtypename} (g, ptr->items[i], ctx, err);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=3) c_file.append("""\n } } @@ -1518,8 +1501,7 @@ def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): c_file.append(' const char *str = NULL;\n') if obj.doublearray: c_file.append(' stat = yajl_gen_array_open ((yajl_gen) g);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=3) c_file.append(" {\n") c_file.append(" size_t i;\n") c_file.append(" for (i = 0; i < ptr->len; i++)\n") @@ -1552,8 +1534,7 @@ def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): c_file.append(' {\n') if obj.doublearray: c_file.append(' stat = yajl_gen_array_open ((yajl_gen) g);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") + check_gen_status(c_file, indent=3) c_file.append(" size_t j;\n") c_file.append(' for (j = 0; j < ptr->subitem_lens[i]; j++)\n') c_file.append(' {\n') From 002af18bc0fc3d179acaa51e8dfffc9f26118273 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 11:02:53 +0000 Subject: [PATCH 08/51] source: convert parse_map_string_obj() to multi-line f-strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace individual c_file.append() calls with consolidated multi-line f-strings using the emit() helper. This makes the C code generation more readable by showing the structure of the generated code directly. The helper functions (calloc_with_check, null_check_return) are retained for their specific patterns. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index cce6cada..16fd6f6d 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -129,24 +129,37 @@ def parse_map_string_obj(obj, c_file, prefix, obj_typename): childname = child.subtypname else: childname = helpers.get_prefixed_name(child.name, prefix) - c_file.append(' if (YAJL_GET_OBJECT (tree) != NULL)\n') - c_file.append(' {\n') - c_file.append(' size_t i;\n') - c_file.append(' size_t len = YAJL_GET_OBJECT_NO_CHECK (tree)->len;\n') - c_file.append(' const char **keys = YAJL_GET_OBJECT_NO_CHECK (tree)->keys;\n') - c_file.append(' yajl_val *values = YAJL_GET_OBJECT_NO_CHECK (tree)->values;\n') - c_file.append(' ret->len = len;\n') + + emit(c_file, f''' + if (YAJL_GET_OBJECT (tree) != NULL) + {{ + 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; + ret->len = len; + ''', indent=1) + calloc_with_check(c_file, 'ret->keys', 'len + 1', '*ret->keys', indent=2) calloc_with_check(c_file, f'ret->{child.fixname}', 'len + 1', f'*ret->{child.fixname}', indent=2) - c_file.append(' for (i = 0; i < len; i++)\n') - c_file.append(' {\n') - c_file.append(' yajl_val val;\n') - c_file.append(' const char *tmpkey = keys[i];\n') - c_file.append(' ret->keys[i] = strdup (tmpkey ? tmpkey : "");\n') + + emit(c_file, f''' + for (i = 0; i < len; i++) + {{ + yajl_val val; + const char *tmpkey = keys[i]; + ret->keys[i] = strdup (tmpkey ? tmpkey : ""); + ''', indent=2) + null_check_return(c_file, 'ret->keys[i]', indent=3) - c_file.append(' val = values[i];\n') - c_file.append(f' ret->{child.fixname}[i] = make_{childname} (val, ctx, err);\n') + + emit(c_file, f''' + val = values[i]; + ret->{child.fixname}[i] = make_{childname} (val, ctx, err); + ''', indent=3) + null_check_return(c_file, f'ret->{child.fixname}[i]', indent=3) + c_file.append(' }\n') c_file.append(' }\n') From ae231756a829b51d21ba343f7120c19e2c9068f7 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 11:04:32 +0000 Subject: [PATCH 09/51] source: convert parse_obj_type_array() to multi-line f-strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace individual c_file.append() calls with consolidated multi-line f-strings using the emit() helper. This function has three branches (object type, byte type, and other types), all now converted. The generated C code structure is now directly visible in the Python code, making it easier to understand and maintain. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 192 ++++++++++++++++++++++++++--------------- 1 file changed, 124 insertions(+), 68 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 16fd6f6d..f3826bdf 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -170,98 +170,154 @@ def parse_obj_type_array(obj, c_file, prefix, obj_typename): typename = obj.subtypname else: typename = helpers.get_name_substr(obj.name, prefix) - c_file.append(' do\n') - c_file.append(' {\n') - c_file.append(f' yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_array);\n') - c_file.append(' if (tmp != NULL && YAJL_GET_ARRAY (tmp) != NULL)\n') - c_file.append(' {\n') - c_file.append(' size_t i;\n') - c_file.append(' size_t len = YAJL_GET_ARRAY_NO_CHECK (tmp)->len;\n') - c_file.append(' yajl_val *values = YAJL_GET_ARRAY_NO_CHECK (tmp)->values;\n') - c_file.append(f' ret->{obj.fixname}_len = len;\n') + + emit(c_file, f''' + do + {{ + yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_array); + if (tmp != NULL && YAJL_GET_ARRAY (tmp) != NULL) + {{ + size_t i; + size_t len = YAJL_GET_ARRAY_NO_CHECK (tmp)->len; + yajl_val *values = YAJL_GET_ARRAY_NO_CHECK (tmp)->values; + 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.doublearray: calloc_with_check(c_file, f'ret->{obj.fixname}_item_lens', 'len + 1', 'size_t', indent=3) - c_file.append(' for (i = 0; i < len; i++)\n') - c_file.append(' {\n') - c_file.append(' yajl_val val = values[i];\n') + + emit(c_file, ''' + for (i = 0; i < len; i++) + { + yajl_val val = values[i]; + ''', indent=3) + if obj.doublearray: - c_file.append(' size_t j;\n') - c_file.append(f' ret->{obj.fixname}[i] = calloc ( YAJL_GET_ARRAY_NO_CHECK(val)->len + 1, sizeof (**ret->{obj.fixname}));\n') + emit(c_file, f''' + size_t j; + ret->{obj.fixname}[i] = calloc ( YAJL_GET_ARRAY_NO_CHECK(val)->len + 1, sizeof (**ret->{obj.fixname})); + ''', indent=4) null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=4) - c_file.append(' yajl_val *items = YAJL_GET_ARRAY_NO_CHECK(val)->values;\n') - c_file.append(' for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(val)->len; j++)\n') - c_file.append(' {\n') - c_file.append(f' ret->{obj.fixname}[i][j] = make_{typename} (items[j], ctx, err);\n') + 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++) + { + ''', indent=4) + emit(c_file, f''' + ret->{obj.fixname}[i][j] = make_{typename} (items[j], ctx, err); + ''', indent=5) null_check_return(c_file, f'ret->{obj.fixname}[i][j]', indent=5) - c_file.append(f' ret->{obj.fixname}_item_lens[i] += 1;\n') - c_file.append(' };\n') + emit(c_file, f''' + ret->{obj.fixname}_item_lens[i] += 1; + }}; + ''', indent=5) else: - c_file.append(f' ret->{obj.fixname}[i] = make_{typename} (val, ctx, err);\n') + emit(c_file, f''' + ret->{obj.fixname}[i] = make_{typename} (val, ctx, err); + ''', indent=4) null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=4) - c_file.append(' }\n') - c_file.append(' }\n') - c_file.append(' }\n') - c_file.append(' while (0);\n') + + emit(c_file, ''' + } + } + } + while (0); + ''', indent=1) elif obj.subtyp == 'byte': - c_file.append(' do\n') - c_file.append(' {\n') - c_file.append(f' yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_string);\n') - c_file.append(' if (tmp != NULL)\n') - c_file.append(' {\n') + emit(c_file, f''' + do + {{ + yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_string); + if (tmp != NULL) + {{ + ''', indent=1) + if obj.doublearray: - c_file.append(' yajl_val *items = YAJL_GET_ARRAY_NO_CHECK(tmp)->values;\n') - c_file.append(f' ret->{obj.fixname} = calloc ( YAJL_GET_ARRAY_NO_CHECK(tmp)->len + 1, sizeof (*ret->{obj.fixname}));\n') + emit(c_file, f''' + yajl_val *items = YAJL_GET_ARRAY_NO_CHECK(tmp)->values; + ret->{obj.fixname} = calloc ( YAJL_GET_ARRAY_NO_CHECK(tmp)->len + 1, sizeof (*ret->{obj.fixname})); + ''', indent=4) null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=4) - c_file.append(' size_t j;\n') - c_file.append(' for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(tmp)->len; j++)\n') - c_file.append(' {\n') - c_file.append(' char *str = YAJL_GET_STRING (itmes[j]);\n') - c_file.append(f' ret->{obj.fixname}[j] = (uint8_t *)strdup (str ? str : "");\n') + emit(c_file, ''' + size_t j; + for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(tmp)->len; j++) + { + char *str = YAJL_GET_STRING (itmes[j]); + ''', indent=4) + emit(c_file, f''' + ret->{obj.fixname}[j] = (uint8_t *)strdup (str ? str : ""); + ''', indent=5) null_check_return(c_file, f'ret->{obj.fixname}[j]', indent=5) - c_file.append(' };\n') + emit(c_file, ''' + }; + ''', indent=5) else: - c_file.append(' char *str = YAJL_GET_STRING (tmp);\n') - c_file.append(f' ret->{obj.fixname} = (uint8_t *)strdup (str ? str : "");\n') + emit(c_file, ''' + char *str = YAJL_GET_STRING (tmp); + ''', indent=3) + emit(c_file, f''' + ret->{obj.fixname} = (uint8_t *)strdup (str ? str : ""); + ''', indent=3) null_check_return(c_file, f'ret->{obj.fixname}', indent=3) - c_file.append(f' ret->{obj.fixname}_len = str != NULL ? strlen (str) : 0;\n') - c_file.append(' }\n') - c_file.append(' }\n') - c_file.append(' while (0);\n') + emit(c_file, f''' + ret->{obj.fixname}_len = str != NULL ? strlen (str) : 0; + ''', indent=3) + + emit(c_file, ''' + } + } + while (0); + ''', indent=1) else: - c_file.append(' do\n') - c_file.append(' {\n') - c_file.append(f' yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_array);\n') - c_file.append(' if (tmp != NULL && YAJL_GET_ARRAY (tmp) != NULL)\n') - c_file.append(' {\n') - c_file.append(' size_t i;\n') - c_file.append(' size_t len = YAJL_GET_ARRAY_NO_CHECK (tmp)->len;\n') - c_file.append(' yajl_val *values = YAJL_GET_ARRAY_NO_CHECK (tmp)->values;\n') - c_file.append(f' ret->{obj.fixname}_len = len;\n') + emit(c_file, f''' + do + {{ + yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_array); + if (tmp != NULL && YAJL_GET_ARRAY (tmp) != NULL) + {{ + size_t i; + size_t len = YAJL_GET_ARRAY_NO_CHECK (tmp)->len; + yajl_val *values = YAJL_GET_ARRAY_NO_CHECK (tmp)->values; + 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.doublearray: calloc_with_check(c_file, f'ret->{obj.fixname}_item_lens', 'len + 1', 'size_t', indent=3) - c_file.append(' for (i = 0; i < len; i++)\n') - c_file.append(' {\n') + + emit(c_file, ''' + for (i = 0; i < len; i++) + { + ''', indent=3) + if obj.doublearray: - c_file.append(' yajl_val *items = YAJL_GET_ARRAY_NO_CHECK(values[i])->values;\n') - c_file.append(f' ret->{obj.fixname}[i] = calloc ( YAJL_GET_ARRAY_NO_CHECK(values[i])->len + 1, sizeof (**ret->{obj.fixname}));\n') - c_file.append(f' if (ret->{obj.fixname}[i] == NULL)\n') - c_file.append(' return NULL;\n') - c_file.append(' size_t j;\n') - c_file.append(' for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(values[i])->len; j++)\n') - c_file.append(' {\n') + 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})); + ''', indent=4) + null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=5) + emit(c_file, ''' + size_t j; + for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(values[i])->len; j++) + { + ''', indent=4) read_val_generator(c_file, 5, 'items[j]', \ f"ret->{obj.fixname}[i][j]", obj.subtyp, obj.origname, obj_typename) - c_file.append(f' ret->{obj.fixname}_item_lens[i] += 1;\n') - c_file.append(' };\n') + emit(c_file, f''' + ret->{obj.fixname}_item_lens[i] += 1; + }}; + ''', indent=5) else: read_val_generator(c_file, 4, 'values[i]', \ f"ret->{obj.fixname}[i]", obj.subtyp, obj.origname, obj_typename) - c_file.append(' }\n') - c_file.append(' }\n') - c_file.append(' }\n') - c_file.append(' while (0);\n') + + emit(c_file, ''' + } + } + } + while (0); + ''', indent=1) def parse_obj_type(obj, c_file, prefix, obj_typename): """ From 47e7dc5ab80528da90adc63d95ba07d7c91f6db0 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 11:05:16 +0000 Subject: [PATCH 10/51] source: convert get_map_string_obj() to multi-line f-strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace individual c_file.append() calls with consolidated multi-line f-strings using the emit() helper. The check_gen_status() helper is strategically placed between code blocks. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 56 ++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index f3826bdf..3d9ff24e 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -523,29 +523,47 @@ def get_map_string_obj(obj, c_file, prefix): childname = child.subtypname else: childname = helpers.get_prefixed_name(child.name, prefix) - c_file.append(' size_t len = 0, i;\n') - c_file.append(" if (ptr != NULL)\n") - c_file.append(" len = ptr->len;\n") - c_file.append(" if (!len && !(ctx->options & OPT_GEN_SIMPLIFY))\n") - c_file.append(' yajl_gen_config (g, yajl_gen_beautify, 0);\n') - c_file.append(" stat = yajl_gen_map_open ((yajl_gen) g);\n") + + emit(c_file, f''' + size_t len = 0, i; + if (ptr != NULL) + len = ptr->len; + if (!len && !(ctx->options & OPT_GEN_SIMPLIFY)) + yajl_gen_config (g, yajl_gen_beautify, 0); + stat = yajl_gen_map_open ((yajl_gen) g); + ''', indent=1) + check_gen_status(c_file, indent=1) - c_file.append(f' if (len || (ptr != NULL && ptr->keys != NULL && ptr->{child.fixname} != NULL))\n') - c_file.append(' {\n') - c_file.append(' for (i = 0; i < len; i++)\n') - c_file.append(' {\n') - c_file.append(' char *str = ptr->keys[i] ? ptr->keys[i] : "";\n') - c_file.append(' stat = yajl_gen_string ((yajl_gen) g, \ -(const unsigned char *)str, strlen (str));\n') + + emit(c_file, f''' + if (len || (ptr != NULL && ptr->keys != NULL && ptr->{child.fixname} != NULL)) + {{ + 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)); + ''', indent=1) + check_gen_status(c_file, indent=3) - c_file.append(f' stat = gen_{childname} (g, ptr->{child.fixname}[i], ctx, err);\n') + + emit(c_file, f''' + stat = gen_{childname} (g, ptr->{child.fixname}[i], ctx, err); + ''', indent=3) + check_gen_status(c_file, indent=3) - c_file.append(' }\n') - c_file.append(' }\n') - c_file.append(" stat = yajl_gen_map_close ((yajl_gen) g);\n") + + emit(c_file, ''' + } + } + stat = yajl_gen_map_close ((yajl_gen) g); + ''', indent=2) + check_gen_status(c_file, indent=1) - c_file.append(" if (!len && !(ctx->options & OPT_GEN_SIMPLIFY))\n") - c_file.append(' yajl_gen_config (g, yajl_gen_beautify, 1);\n') + + emit(c_file, ''' + if (!len && !(ctx->options & OPT_GEN_SIMPLIFY)) + yajl_gen_config (g, yajl_gen_beautify, 1); + ''', indent=1) def get_obj_arr_obj_array(obj, c_file, prefix): if obj.subtypobj or obj.subtyp == 'object': From 66e333d61893615c6f5a479af05d17ecaa673634 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 11:06:05 +0000 Subject: [PATCH 11/51] source: convert get_obj_arr_obj_array() to f-strings (object branch) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace individual c_file.append() calls with consolidated multi-line f-strings in the object/subtypobj branch of get_obj_arr_obj_array(). The byte and else branches remain to be converted in subsequent commits. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 75 ++++++++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 3d9ff24e..1930efff 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -572,38 +572,65 @@ def get_obj_arr_obj_array(obj, c_file, prefix): typename = obj.subtypname else: typename = helpers.get_name_substr(obj.name, prefix) - c_file.append(f' if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL))\n') - c_file.append(' {\n') - c_file.append(' size_t len = 0, i;\n') - c_file.append(f' stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {int(l)} /* strlen ("{obj.origname}") */);\n') + + emit(c_file, f''' + if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) + {{ + size_t len = 0, i; + stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {int(l)} /* strlen ("{obj.origname}") */); + ''', indent=1) + check_gen_status(c_file, indent=2) - c_file.append(f" if (ptr != NULL && ptr->{obj.fixname} != NULL)\n") - c_file.append(f" len = ptr->{obj.fixname}_len;\n") - c_file.append(" if (!len && !(ctx->options & OPT_GEN_SIMPLIFY))\n") - c_file.append(' yajl_gen_config (g, yajl_gen_beautify, 0);\n') - c_file.append(' stat = yajl_gen_array_open ((yajl_gen) g);\n') + + emit(c_file, f''' + if (ptr != NULL && ptr->{obj.fixname} != NULL) + len = ptr->{obj.fixname}_len; + if (!len && !(ctx->options & OPT_GEN_SIMPLIFY)) + yajl_gen_config (g, yajl_gen_beautify, 0); + stat = yajl_gen_array_open ((yajl_gen) g); + ''', indent=2) + check_gen_status(c_file, indent=2) - c_file.append(' for (i = 0; i < len; i++)\n') - c_file.append(' {\n') + + emit(c_file, ''' + for (i = 0; i < len; i++) + { + ''', indent=2) + if obj.doublearray: - c_file.append(' stat = yajl_gen_array_open ((yajl_gen) g);\n') + emit(c_file, ''' + stat = yajl_gen_array_open ((yajl_gen) g); + ''', indent=3) check_gen_status(c_file, indent=3) - c_file.append(" size_t j;\n") - c_file.append(f' for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++)\n') - c_file.append(' {\n') - c_file.append(f' stat = gen_{typename} (g, ptr->{obj.fixname}[i][j], ctx, err);\n') + emit(c_file, f''' + size_t j; + for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++) + {{ + stat = gen_{typename} (g, ptr->{obj.fixname}[i][j], ctx, err); + ''', indent=3) check_gen_status(c_file, indent=4) - c_file.append(' }\n') - c_file.append(' stat = yajl_gen_array_close ((yajl_gen) g);\n') + emit(c_file, ''' + } + stat = yajl_gen_array_close ((yajl_gen) g); + ''', indent=4) else: - c_file.append(f' stat = gen_{typename} (g, ptr->{obj.fixname}[i], ctx, err);\n') + emit(c_file, f''' + stat = gen_{typename} (g, ptr->{obj.fixname}[i], ctx, err); + ''', indent=3) check_gen_status(c_file, indent=3) - c_file.append(' }\n') - c_file.append(' stat = yajl_gen_array_close ((yajl_gen) g);\n') - c_file.append(" if (!len && !(ctx->options & OPT_GEN_SIMPLIFY))\n") - c_file.append(' yajl_gen_config (g, yajl_gen_beautify, 1);\n') + + emit(c_file, ''' + } + stat = yajl_gen_array_close ((yajl_gen) g); + if (!len && !(ctx->options & OPT_GEN_SIMPLIFY)) + yajl_gen_config (g, yajl_gen_beautify, 1); + ''', indent=2) + check_gen_status(c_file, indent=2) - c_file.append(' }\n') + + emit(c_file, ''' + } + ''', indent=1) elif obj.subtyp == 'byte': l = len(obj.origname) c_file.append(f' if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL && ptr->{obj.fixname}_len))\n') From a2fd73f290057b73f58958bfbf33eefc48fe11c8 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 11:07:14 +0000 Subject: [PATCH 12/51] source: complete get_obj_arr_obj_array() conversion to f-strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the remaining byte and else branches of get_obj_arr_obj_array() to use multi-line f-strings with the emit() helper. This completes the refactoring of this large, complex function that handles three different type branches. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 132 ++++++++++++++++++++++++++--------------- 1 file changed, 83 insertions(+), 49 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 1930efff..26a5be12 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -633,73 +633,107 @@ def get_obj_arr_obj_array(obj, c_file, prefix): ''', indent=1) elif obj.subtyp == 'byte': l = len(obj.origname) - c_file.append(f' if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL && ptr->{obj.fixname}_len))\n') - c_file.append(' {\n') - c_file.append(' const char *str = "";\n') - c_file.append(' size_t len = 0;\n') - c_file.append(f' stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */);\n') + emit(c_file, f''' + if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL && ptr->{obj.fixname}_len)) + {{ + const char *str = ""; + size_t len = 0; + stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */); + ''', indent=1) + check_gen_status(c_file, indent=2) + if obj.doublearray: - c_file.append(' stat = yajl_gen_array_open ((yajl_gen) g);\n') + emit(c_file, ''' + stat = yajl_gen_array_open ((yajl_gen) g); + ''', indent=3) check_gen_status(c_file, indent=3) - c_file.append(" {\n") - c_file.append(" size_t i;\n") - c_file.append(f" for (i = 0; i < ptr->{obj.fixname}_len; i++)\n") - c_file.append(" {\n") - c_file.append(f" if (ptr->{obj.fixname}[i] != NULL)\n") - c_file.append(f" str = (const char *)ptr->{obj.fixname}[i];\n") - c_file.append(" else ()\n") - c_file.append(" str = "";\n") - c_file.append(' stat = yajl_gen_string ((yajl_gen) g, \ - (const unsigned char *)str, strlen(str));\n') - c_file.append(" }\n") - c_file.append(" }\n") - c_file.append(' stat = yajl_gen_array_close ((yajl_gen) g);\n') + emit(c_file, f''' + {{ + size_t i; + for (i = 0; i < ptr->{obj.fixname}_len; i++) + {{ + if (ptr->{obj.fixname}[i] != NULL) + str = (const char *)ptr->{obj.fixname}[i]; + else () + str = ""; + stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)str, strlen(str)); + }} + }} + stat = yajl_gen_array_close ((yajl_gen) g); + ''', indent=2) else: - c_file.append(f" if (ptr != NULL && ptr->{obj.fixname} != NULL)\n") - c_file.append(" {\n") - c_file.append(f" str = (const char *)ptr->{obj.fixname};\n") - c_file.append(f" len = ptr->{obj.fixname}_len;\n") - c_file.append(" }\n") - c_file.append(' stat = yajl_gen_string ((yajl_gen) g, \ - (const unsigned char *)str, len);\n') + emit(c_file, f''' + if (ptr != NULL && ptr->{obj.fixname} != NULL) + {{ + str = (const char *)ptr->{obj.fixname}; + len = ptr->{obj.fixname}_len; + }} + stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)str, len); + ''', indent=2) + check_gen_status(c_file, indent=2) - c_file.append(" }\n") + + emit(c_file, ''' + } + ''', indent=1) else: l = len(obj.origname) - c_file.append(f' if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL))\n') - c_file.append(' {\n') - c_file.append(' size_t len = 0, i;\n') - c_file.append(f' stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */);\n') + emit(c_file, f''' + if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) + {{ + size_t len = 0, i; + stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */); + ''', indent=1) + check_gen_status(c_file, indent=2) - c_file.append(f" if (ptr != NULL && ptr->{obj.fixname} != NULL)\n") - c_file.append(f" len = ptr->{obj.fixname}_len;\n") - c_file.append(" if (!len && !(ctx->options & OPT_GEN_SIMPLIFY))\n") - c_file.append(' yajl_gen_config (g, yajl_gen_beautify, 0);\n') - c_file.append(' stat = yajl_gen_array_open ((yajl_gen) g);\n') + + emit(c_file, f''' + if (ptr != NULL && ptr->{obj.fixname} != NULL) + len = ptr->{obj.fixname}_len; + if (!len && !(ctx->options & OPT_GEN_SIMPLIFY)) + yajl_gen_config (g, yajl_gen_beautify, 0); + stat = yajl_gen_array_open ((yajl_gen) g); + ''', indent=2) + check_gen_status(c_file, indent=2) - c_file.append(' for (i = 0; i < len; i++)\n') - c_file.append(' {\n') + + emit(c_file, ''' + for (i = 0; i < len; i++) + { + ''', indent=2) if obj.doublearray: typename = helpers.get_map_c_types(obj.subtyp) - c_file.append(' stat = yajl_gen_array_open ((yajl_gen) g);\n') + emit(c_file, ''' + stat = yajl_gen_array_open ((yajl_gen) g); + ''', indent=3) check_gen_status(c_file, indent=3) - c_file.append(" size_t j;\n") - c_file.append(f' for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++)\n') - c_file.append(' {\n') + emit(c_file, f''' + size_t j; + for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++) + {{ + ''', indent=3) json_value_generator(c_file, 4, f"ptr->{obj.fixname}[i][j]", 'g', 'ctx', obj.subtyp) - c_file.append(' }\n') - c_file.append(' stat = yajl_gen_array_close ((yajl_gen) g);\n') + emit(c_file, ''' + } + stat = yajl_gen_array_close ((yajl_gen) g); + ''', indent=4) else: json_value_generator(c_file, 3, f"ptr->{obj.fixname}[i]", 'g', 'ctx', obj.subtyp) - c_file.append(' }\n') - c_file.append(' stat = yajl_gen_array_close ((yajl_gen) g);\n') + emit(c_file, ''' + } + stat = yajl_gen_array_close ((yajl_gen) g); + ''', indent=2) + check_gen_status(c_file, indent=2) - c_file.append(" if (!len && !(ctx->options & OPT_GEN_SIMPLIFY))\n") - c_file.append(' yajl_gen_config (g, yajl_gen_beautify, 1);\n') - c_file.append(' }\n') + + emit(c_file, ''' + if (!len && !(ctx->options & OPT_GEN_SIMPLIFY)) + yajl_gen_config (g, yajl_gen_beautify, 1); + } + ''', indent=2) def get_obj_arr_obj(obj, c_file, prefix): """ From 41596b9be6dc19faf91dc3254af3d8d17c72bea5 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 11:07:55 +0000 Subject: [PATCH 13/51] source: convert c_file_map_str() to multi-line f-strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace individual c_file.append() calls with consolidated multi-line f-strings. Helper functions free_and_null() are retained for their specific patterns. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 26a5be12..8baa64de 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -1341,18 +1341,28 @@ def c_file_map_str(c_file, child, childname): Interface: None History: 2019-10-31 """ - c_file.append(f" if (ptr->keys != NULL && ptr->{child.fixname} != NULL)\n") - c_file.append(" {\n") - c_file.append(" size_t i;\n") - c_file.append(" for (i = 0; i < ptr->len; i++)\n") - c_file.append(" {\n") + emit(c_file, f''' + if (ptr->keys != NULL && ptr->{child.fixname} != NULL) + {{ + size_t i; + for (i = 0; i < ptr->len; i++) + {{ + ''', indent=1) + free_and_null(c_file, "ptr", "keys[i]", indent=3) - c_file.append(f" free_{childname} (ptr->{child.fixname}[i]);\n") - c_file.append(f" ptr->{child.fixname}[i] = NULL;\n") - c_file.append(" }\n") + + emit(c_file, f''' + free_{childname} (ptr->{child.fixname}[i]); + ptr->{child.fixname}[i] = NULL; + }} + ''', indent=3) + free_and_null(c_file, "ptr", "keys", indent=2) free_and_null(c_file, "ptr", child.fixname, indent=2) - c_file.append(" }\n") + + emit(c_file, ''' + } + ''', indent=1) def c_file_str(c_file, i): """ From 112ab7f2681db508c8b3193beeacda76b00344b5 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 11:09:40 +0000 Subject: [PATCH 14/51] source: convert c_file_str() to multi-line f-strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace individual c_file.append() calls with consolidated multi-line f-strings. Handles both single and double array cases cleanly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 47 +++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 8baa64de..4eb76c61 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -1370,26 +1370,45 @@ def c_file_str(c_file, i): Interface: None History: 2019-10-31 """ - c_file.append(f" if (ptr->{i.fixname} != NULL)\n") - c_file.append(" {\n") - c_file.append(" size_t i;\n") - c_file.append(f" for (i = 0; i < ptr->{i.fixname}_len; i++)\n") - c_file.append(" {\n") + emit(c_file, f''' + if (ptr->{i.fixname} != NULL) + {{ + size_t i; + for (i = 0; i < ptr->{i.fixname}_len; i++) + {{ + ''', indent=1) + if i.doublearray: - c_file.append(" size_t j;\n") - c_file.append(f" for (j = 0; j < ptr->{i.fixname}_item_lens[i]; j++)\n") - c_file.append(" {\n") + emit(c_file, f''' + size_t j; + for (j = 0; j < ptr->{i.fixname}_item_lens[i]; j++) + {{ + ''', indent=3) free_and_null(c_file, "ptr", f"{i.fixname}[i][j]", indent=4) - c_file.append(" }\n") - c_file.append(f" if (ptr->{i.fixname}[i] != NULL)\n") - c_file.append(" {\n") + emit(c_file, ''' + } + ''', indent=3) + + emit(c_file, f''' + if (ptr->{i.fixname}[i] != NULL) + {{ + ''', indent=3) + free_and_null(c_file, "ptr", f"{i.fixname}[i]", indent=4) - c_file.append(" }\n") - c_file.append(" }\n") + + emit(c_file, ''' + } + } + ''', indent=3) + if i.doublearray: free_and_null(c_file, "ptr", f"{i.fixname}_item_lens", indent=2) + free_and_null(c_file, "ptr", i.fixname, indent=2) - c_file.append(" }\n") + + emit(c_file, ''' + } + ''', indent=1) def src_reflect(structs, schema_info, c_file, root_typ): From d62a55fb12a8f7703ac50bbf1c03f3db813368d2 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 11:10:48 +0000 Subject: [PATCH 15/51] source: convert make_c_array_free() to multi-line f-strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace individual c_file.append() calls with consolidated multi-line f-strings. Handles multiple branches for basic maps, strings, compound types, and objects with both single and double array cases. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 108 ++++++++++++++++++++++++++--------------- 1 file changed, 70 insertions(+), 38 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 4eb76c61..9051039a 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -1200,69 +1200,101 @@ def json_value_generator(c_file, level, src, dst, ptx, typ): def make_c_array_free (i, c_file, prefix): if helpers.valid_basic_map_name(i.subtyp): free_func = helpers.make_basic_map_name(i.subtyp) - c_file.append(f" if (ptr->{i.fixname} != NULL)\n") - c_file.append(" {\n") - c_file.append(" size_t i;\n") - c_file.append(f" for (i = 0; i < ptr->{i.fixname}_len; i++)\n") - c_file.append(" {\n") - c_file.append(f" if (ptr->{i.fixname}[i] != NULL)\n") - c_file.append(" {\n") - c_file.append(f" free_{free_func} (ptr->{i.fixname}[i]);\n") - c_file.append(f" ptr->{i.fixname}[i] = NULL;\n") - c_file.append(" }\n") - c_file.append(" }\n") + emit(c_file, f''' + if (ptr->{i.fixname} != NULL) + {{ + size_t i; + for (i = 0; i < ptr->{i.fixname}_len; i++) + {{ + if (ptr->{i.fixname}[i] != NULL) + {{ + free_{free_func} (ptr->{i.fixname}[i]); + ptr->{i.fixname}[i] = NULL; + }} + }} + ''', indent=1) free_and_null(c_file, "ptr", i.fixname, indent=2) - c_file.append(" }\n") + emit(c_file, ''' + } + ''', indent=1) elif i.subtyp == 'string': c_file_str(c_file, i) elif not helpers.is_compound_type(i.subtyp): - c_file.append(" {\n") + emit(c_file, ''' + { + ''', indent=0) if i.doublearray: - c_file.append(" size_t i;\n") - c_file.append(f" for (i = 0; i < ptr->{i.fixname}_len; i++)\n") - c_file.append(" {\n") + emit(c_file, f''' + size_t i; + for (i = 0; i < ptr->{i.fixname}_len; i++) + {{ + ''', indent=3) free_and_null(c_file, "ptr", f"{i.fixname}[i]", indent=4) - c_file.append(" }\n") + emit(c_file, ''' + } + ''', indent=3) free_and_null(c_file, "ptr", f"{i.fixname}_item_lens", indent=3) free_and_null(c_file, "ptr", i.fixname, indent=2) - c_file.append(" }\n") + emit(c_file, ''' + } + ''', indent=1) elif i.subtyp == 'object' or i.subtypobj is not None: if i.subtypname is not None: free_func = i.subtypname else: free_func = helpers.get_name_substr(i.name, prefix) - c_file.append(f" if (ptr->{i.fixname} != NULL)") - c_file.append(" {\n") - c_file.append(" size_t i;\n") - c_file.append(f" for (i = 0; i < ptr->{i.fixname}_len; i++)\n") - c_file.append(" {\n") + + emit(c_file, f''' + if (ptr->{i.fixname} != NULL) + {{ + size_t i; + for (i = 0; i < ptr->{i.fixname}_len; i++) + {{ + ''', indent=1) + if i.doublearray: - c_file.append(" size_t j;\n") - c_file.append(f" for (j = 0; j < ptr->{i.fixname}_item_lens[i]; j++)\n") - c_file.append(" {\n") - c_file.append(f" free_{free_func} (ptr->{i.fixname}[i][j]);\n") - c_file.append(f" ptr->{i.fixname}[i][j] = NULL;\n") - c_file.append(" }\n") + emit(c_file, f''' + size_t j; + for (j = 0; j < ptr->{i.fixname}_item_lens[i]; j++) + {{ + free_{free_func} (ptr->{i.fixname}[i][j]); + ptr->{i.fixname}[i][j] = NULL; + }} + ''', indent=2) free_and_null(c_file, "ptr", f"{i.fixname}[i]", indent=2) else: - c_file.append(f" if (ptr->{i.fixname}[i] != NULL)\n") - c_file.append(" {\n") - c_file.append(f" free_{free_func} (ptr->{i.fixname}[i]);\n") - c_file.append(f" ptr->{i.fixname}[i] = NULL;\n") - c_file.append(" }\n") - c_file.append(" }\n") + emit(c_file, f''' + if (ptr->{i.fixname}[i] != NULL) + {{ + free_{free_func} (ptr->{i.fixname}[i]); + ptr->{i.fixname}[i] = NULL; + }} + ''', indent=2) + + emit(c_file, ''' + } + ''', indent=2) + if i.doublearray: free_and_null(c_file, "ptr", f"{i.fixname}_item_lens", indent=2) free_and_null(c_file, "ptr", i.fixname, indent=2) - c_file.append(" }\n") + + emit(c_file, ''' + } + ''', indent=1) + c_typ = helpers.obtain_pointer(i.name, i.subtypobj, prefix) if c_typ == "": return True if i.subtypname is not None: c_typ = c_typ + "_element" - c_file.append(f" free_{c_typ} (ptr->{i.fixname});\n") - c_file.append(f" ptr->{i.fixname} = NULL;\n") + + emit(c_file, f''' + free_{c_typ} (ptr->{i.fixname}); + ptr->{i.fixname} = NULL; + ''', indent=1) + return False def make_c_free (obj, c_file, prefix): From 39fa66d088da25b7b0c6df694ced4337413e1fd9 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 11:11:45 +0000 Subject: [PATCH 16/51] source: convert make_c_free() to multi-line f-strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace individual c_file.append() calls with consolidated multi-line f-strings. This function handles free() generation for different object types including mapStringObject, arrays, strings, and objects. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 57 ++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 9051039a..93f17108 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -1316,10 +1316,14 @@ def make_c_free (obj, c_file, prefix): return else: typename = helpers.get_name_substr(obj.name, prefix) - c_file.append(f"void\nfree_{typename} ({typename} *ptr)\n") - c_file.append("{\n") - c_file.append(" if (ptr == NULL)\n") - c_file.append(" return;\n") + + emit(c_file, f''' + void + free_{typename} ({typename} *ptr) + {{ + if (ptr == NULL) + return; + ''', indent=0) if obj.typ == 'mapStringObject': child = obj.children[0] if helpers.valid_basic_map_name(child.typ): @@ -1333,15 +1337,19 @@ def make_c_free (obj, c_file, prefix): for i in objs or []: if helpers.valid_basic_map_name(i.typ): free_func = helpers.make_basic_map_name(i.typ) - c_file.append(f" free_{free_func} (ptr->{i.fixname});\n") - c_file.append(f" ptr->{i.fixname} = NULL;\n") + emit(c_file, f''' + free_{free_func} (ptr->{i.fixname}); + ptr->{i.fixname} = NULL; + ''', indent=1) if i.typ == 'mapStringObject': if i.subtypname: free_func = i.subtypname else: free_func = helpers.get_prefixed_name(i.name, prefix) - c_file.append(f" free_{free_func} (ptr->{i.fixname});\n") - c_file.append(f" ptr->{i.fixname} = NULL;\n") + emit(c_file, f''' + free_{free_func} (ptr->{i.fixname}); + ptr->{i.fixname} = NULL; + ''', indent=1) elif i.typ == 'array': if make_c_array_free (i, c_file, prefix): continue @@ -1349,22 +1357,33 @@ def make_c_free (obj, c_file, prefix): typename = helpers.get_prefixed_name(i.name, prefix) if i.typ == 'string' or i.typ == 'booleanPointer' or \ helpers.judge_data_pointer_type(i.typ): - c_file.append(f" free (ptr->{i.fixname});\n") - c_file.append(f" ptr->{i.fixname} = NULL;\n") + emit(c_file, f''' + free (ptr->{i.fixname}); + ptr->{i.fixname} = NULL; + ''', indent=1) elif i.typ == 'object': if i.subtypname is not None: typename = i.subtypname - c_file.append(f" if (ptr->{i.fixname} != NULL)\n") - c_file.append(" {\n") - c_file.append(f" free_{typename} (ptr->{i.fixname});\n") - c_file.append(f" ptr->{i.fixname} = NULL;\n") - c_file.append(" }\n") + emit(c_file, f''' + if (ptr->{i.fixname} != NULL) + {{ + free_{typename} (ptr->{i.fixname}); + ptr->{i.fixname} = NULL; + }} + ''', indent=1) + if obj.typ == 'object': if obj.children is not None: - c_file.append(" yajl_tree_free (ptr->_residual);\n") - c_file.append(" ptr->_residual = NULL;\n") - c_file.append(" free (ptr);\n") - c_file.append("}\n\n") + emit(c_file, ''' + yajl_tree_free (ptr->_residual); + ptr->_residual = NULL; + ''', indent=1) + + emit(c_file, ''' + free (ptr); + } + + ''', indent=1) def c_file_map_str(c_file, child, childname): From d37383d89510c88b520c9161d46074123854005b Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 15:06:03 +0100 Subject: [PATCH 17/51] source: convert parse_obj_type() to multi-line f-strings Replace individual c_file.append() calls with emit() helper using multi-line f-strings for better readability. This function handles parsing of different object types: string, number, boolean, object, array, and mapStringObject types. Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 1701 +++++++++++++++++++++++----------------- 1 file changed, 971 insertions(+), 730 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 93f17108..f32aea8a 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -326,71 +326,93 @@ def parse_obj_type(obj, c_file, prefix, obj_typename): History: 2019-06-17 """ if obj.typ == 'string': - c_file.append(' do\n') - c_file.append(' {\n') + emit(c_file, f''' + do + {{ + ''', indent=1) read_val_generator(c_file, 2, f'get_val (tree, "{obj.origname}", yajl_t_string)', \ f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename) - c_file.append(' }\n') - c_file.append(' while (0);\n') + emit(c_file, ''' + } + while (0); + ''', indent=1) elif helpers.judge_data_type(obj.typ): - c_file.append(' do\n') - c_file.append(' {\n') + emit(c_file, f''' + do + {{ + ''', indent=1) read_val_generator(c_file, 2, f'get_val (tree, "{obj.origname}", yajl_t_number)', \ f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename) - c_file.append(' }\n') - c_file.append(' while (0);\n') + emit(c_file, ''' + } + while (0); + ''', indent=1) elif helpers.judge_data_pointer_type(obj.typ): - c_file.append(' do\n') - c_file.append(' {\n') + emit(c_file, f''' + do + {{ + ''', indent=1) read_val_generator(c_file, 2, f'get_val (tree, "{obj.origname}", yajl_t_number)', \ f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename) - c_file.append(' }\n') - c_file.append(' while (0);\n') + emit(c_file, ''' + } + while (0); + ''', indent=1) if obj.typ == 'boolean': - c_file.append(' do\n') - c_file.append(' {\n') + emit(c_file, f''' + do + {{ + ''', indent=1) read_val_generator(c_file, 2, f'get_val (tree, "{obj.origname}", yajl_t_true)', \ f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename) - c_file.append(' }\n') - c_file.append(' while (0);\n') + emit(c_file, ''' + } + while (0); + ''', indent=1) if obj.typ == 'booleanPointer': - c_file.append(' do\n') - c_file.append(' {\n') + emit(c_file, f''' + do + {{ + ''', indent=1) read_val_generator(c_file, 2, f'get_val (tree, "{obj.origname}", yajl_t_true)', \ f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename) - c_file.append(' }\n') - c_file.append(' while (0);\n') + emit(c_file, ''' + } + while (0); + ''', indent=1) elif obj.typ == 'object' or obj.typ == 'mapStringObject': if obj.subtypname is not None: typename = obj.subtypname else: typename = helpers.get_prefixed_name(obj.name, prefix) - c_file.append( - f' ret->{obj.fixname} = make_{typename} (get_val (tree, "{obj.origname}", yajl_t_object), ctx, err);\n') - c_file.append(f" if (ret->{obj.fixname} == NULL && *err != 0)\n") - c_file.append(" return NULL;\n") + emit(c_file, f''' + ret->{obj.fixname} = make_{typename} (get_val (tree, "{obj.origname}", yajl_t_object), ctx, err); + if (ret->{obj.fixname} == NULL && *err != 0) + return NULL; + ''', indent=1) elif obj.typ == 'array': parse_obj_type_array(obj, c_file, prefix, obj_typename) elif helpers.valid_basic_map_name(obj.typ): - c_file.append(' do\n') - c_file.append(' {\n') - c_file.append(f' yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_object);\n') - c_file.append(' if (tmp != NULL)\n') - c_file.append(' {\n') - c_file.append(f' ret->{obj.fixname} = make_{helpers.make_basic_map_name(obj.typ)} (tmp, ctx, err);\n') - c_file.append(f' if (ret->{obj.fixname} == NULL)\n') - c_file.append(' {\n') - c_file.append(' char *new_error = NULL;\n') - c_file.append(f" if (asprintf (&new_error, \"Value error for key '{obj.origname}': %s\", *err ? *err : \"null\") < 0)\n") - c_file.append(' new_error = strdup (' \ - '"error allocating memory");\n') - c_file.append(' free (*err);\n') - c_file.append(' *err = new_error;\n') - c_file.append(' return NULL;\n') - c_file.append(' }\n') - c_file.append(' }\n') - c_file.append(' }\n') - c_file.append(' while (0);\n') + emit(c_file, f''' + do + {{ + yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_object); + if (tmp != NULL) + {{ + ret->{obj.fixname} = make_{helpers.make_basic_map_name(obj.typ)} (tmp, ctx, err); + if (ret->{obj.fixname} == NULL) + {{ + char *new_error = NULL; + if (asprintf (&new_error, "Value error for key '{obj.origname}': %s", *err ? *err : "null") < 0) + new_error = strdup ("error allocating memory"); + free (*err); + *err = new_error; + return NULL; + }} + }} + }} + while (0); + ''', indent=1) def parse_obj_arr_obj(obj, c_file, prefix, obj_typename): """ @@ -407,69 +429,70 @@ def parse_obj_arr_obj(obj, c_file, prefix, obj_typename): parse_obj_type(i, c_file, prefix, obj_typename) for i in required_to_check: - c_file.append(f' if (ret->{i.fixname} == NULL)\n') - c_file.append(' {\n') - c_file.append(f' if (asprintf (err, "Required field \'%s\' not present", "{i.origname}") < 0)\n') - c_file.append(' *err = strdup ("error allocating memory");\n') - c_file.append(" return NULL;\n") - c_file.append(' }\n') + emit(c_file, f''' + if (ret->{i.fixname} == NULL) + {{ + if (asprintf (err, "Required field '%s' not present", "{i.origname}") < 0) + *err = strdup ("error allocating memory"); + return NULL; + }} + ''', indent=1) if obj.typ == 'object' and 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]) - c_file.append(""" - if (tree->type == yajl_t_object) - { - size_t i; - size_t j = 0; - size_t cnt = tree->u.object.len; - yajl_val resi = NULL; - - if (ctx->options & OPT_PARSE_FULLKEY) - { - resi = calloc (1, sizeof(*tree)); - 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) - { - yajl_tree_free (resi); - return NULL; - } - resi->u.object.values = calloc (cnt, sizeof (yajl_val)); - if (resi->u.object.values == NULL) - { - yajl_tree_free (resi); - return NULL; - } - } + emit(c_file, f''' + if (tree->type == yajl_t_object) + {{ + size_t i; + size_t j = 0; + size_t cnt = tree->u.object.len; + yajl_val resi = NULL; - for (i = 0; i < tree->u.object.len; i++) - {\n""" \ - f" 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++; - } - j++; - } - } + {{ + resi = calloc (1, sizeof(*tree)); + 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) + {{ + yajl_tree_free (resi); + return NULL; + }} + resi->u.object.values = calloc (cnt, sizeof (yajl_val)); + if (resi->u.object.values == NULL) + {{ + yajl_tree_free (resi); + return NULL; + }} + }} - if ((ctx->options & OPT_PARSE_STRICT) && j > 0 && ctx->errfile != NULL) - (void) fprintf (ctx->errfile, "WARNING: unknown key found\\n"); + for (i = 0; i < tree->u.object.len; 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++; + }} + j++; + }} + }} - if (ctx->options & OPT_PARSE_FULLKEY) - ret->_residual = resi; - } -""") + if ((ctx->options & OPT_PARSE_STRICT) && j > 0 && ctx->errfile != NULL) + (void) fprintf (ctx->errfile, "WARNING: unknown key found\\n"); + + if (ctx->options & OPT_PARSE_FULLKEY) + ret->_residual = resi; + }} + ''', indent=1) def parse_json_to_c(obj, c_file, prefix): @@ -489,24 +512,30 @@ def parse_json_to_c(obj, c_file, prefix): objs = obj.subtypobj if objs is None or obj.subtypname: return - c_file.append(f"define_cleaner_function ({typename} *, free_{typename})\n") - c_file.append(f"{typename} *\nmake_{typename} (yajl_val tree, const struct parser_context *ctx, parser_error *err)\n") - c_file.append("{\n") - c_file.append(f" __auto_cleanup(free_{typename}) {typename} *ret = NULL;\n") - c_file.append(" *err = NULL;\n") - c_file.append(" (void) ctx; /* Silence compiler warning. */\n") - c_file.append(" if (tree == NULL)\n") - c_file.append(" return NULL;\n") - c_file.append(" ret = calloc (1, sizeof (*ret));\n") - c_file.append(" if (ret == NULL)\n") - c_file.append(" return NULL;\n") + emit(c_file, f''' + define_cleaner_function ({typename} *, free_{typename}) + {typename} * + make_{typename} (yajl_val tree, const struct parser_context *ctx, parser_error *err) + {{ + __auto_cleanup(free_{typename}) {typename} *ret = NULL; + *err = NULL; + (void) ctx; /* Silence compiler warning. */ + if (tree == NULL) + return NULL; + ret = calloc (1, sizeof (*ret)); + if (ret == NULL) + return NULL; + ''', indent=0) if obj.typ == 'mapStringObject': parse_map_string_obj(obj, c_file, prefix, obj_typename) if obj.typ == 'object' or (obj.typ == 'array' and obj.subtypobj): parse_obj_arr_obj(obj, c_file, prefix, obj_typename) - c_file.append(' return move_ptr (ret);\n') - c_file.append("}\n\n") + emit(c_file, ''' + return move_ptr (ret); + } + + ''', indent=1) def get_map_string_obj(obj, c_file, prefix): @@ -743,18 +772,22 @@ def get_obj_arr_obj(obj, c_file, prefix): """ if obj.typ == 'string': l = len(obj.origname) - c_file.append(f' if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL))\n' ) - c_file.append(' {\n') - c_file.append(' char *str = "";\n') - c_file.append(f' stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */);\n') + emit(c_file, f''' + if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) + {{ + char *str = ""; + stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */); + ''', indent=1) check_gen_status(c_file, indent=2) - c_file.append(f" if (ptr != NULL && ptr->{obj.fixname} != NULL)\n") - c_file.append(f" str = ptr->{obj.fixname};\n") + emit(c_file, f''' + if (ptr != NULL && ptr->{obj.fixname} != NULL) + str = ptr->{obj.fixname}; + ''', indent=2) json_value_generator(c_file, 2, "str", 'g', 'ctx', obj.typ) - c_file.append(" }\n") + emit(c_file, ''' + } + ''', indent=1) elif helpers.judge_data_type(obj.typ): - c_file.append(f' if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname}_present))\n') - c_file.append(' {\n') if obj.typ == 'double': numtyp = 'double' elif obj.typ.startswith("uint") or obj.typ == 'GID' or obj.typ == 'UID': @@ -762,66 +795,97 @@ def get_obj_arr_obj(obj, c_file, prefix): else: numtyp = 'long long int' l = len(obj.origname) - c_file.append(f' {numtyp} num = 0;\n') - c_file.append(f' stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */);\n') + emit(c_file, f''' + if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname}_present)) + {{ + {numtyp} num = 0; + stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */); + ''', indent=1) check_gen_status(c_file, indent=2) - c_file.append(f" if (ptr != NULL && ptr->{obj.fixname})\n") - c_file.append(f" num = ({numtyp})ptr->{obj.fixname};\n") + emit(c_file, f''' + if (ptr != NULL && ptr->{obj.fixname}) + num = ({numtyp})ptr->{obj.fixname}; + ''', indent=2) json_value_generator(c_file, 2, "num", 'g', 'ctx', obj.typ) - c_file.append(" }\n") + emit(c_file, ''' + } + ''', indent=1) elif helpers.judge_data_pointer_type(obj.typ): - c_file.append(f' if ((ptr != NULL && ptr->{obj.fixname} != NULL))\n') - c_file.append(' {\n') numtyp = helpers.obtain_data_pointer_type(obj.typ) if numtyp == "": return l = len(obj.origname) - c_file.append(f' {helpers.get_map_c_types(numtyp)} num = 0;\n') - c_file.append(f' stat = yajl_gen_string ((yajl_gen) g, \ -(const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */);\n') + emit(c_file, f''' + if ((ptr != NULL && ptr->{obj.fixname} != NULL)) + {{ + {helpers.get_map_c_types(numtyp)} num = 0; + stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */); + ''', indent=1) check_gen_status(c_file, indent=2) - c_file.append(f" if (ptr != NULL && ptr->{obj.fixname} != NULL)\n") - c_file.append(" {\n") - c_file.append(f" num = ({helpers.get_map_c_types(numtyp)})*(ptr->{obj.fixname});\n") - c_file.append(" }\n") + emit(c_file, f''' + if (ptr != NULL && ptr->{obj.fixname} != NULL) + {{ + num = ({helpers.get_map_c_types(numtyp)})*(ptr->{obj.fixname}); + }} + ''', indent=2) json_value_generator(c_file, 2, "num", 'g', 'ctx', numtyp) - c_file.append(" }\n") + emit(c_file, ''' + } + ''', indent=1) elif obj.typ == 'boolean': - c_file.append(f' if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname}_present))\n') - c_file.append(' {\n') - c_file.append(' bool b = false;\n') l = len(obj.origname) - c_file.append(f' stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */);\n') + emit(c_file, f''' + if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname}_present)) + {{ + bool b = false; + stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */); + ''', indent=1) check_gen_status(c_file, indent=2) - c_file.append(f" if (ptr != NULL && ptr->{obj.fixname})\n") - c_file.append(f" b = ptr->{obj.fixname};\n") - c_file.append(" \n") + emit(c_file, f''' + if (ptr != NULL && ptr->{obj.fixname}) + b = ptr->{obj.fixname}; + + ''', indent=2) json_value_generator(c_file, 2, "b", 'g', 'ctx', obj.typ) - c_file.append(" }\n") + emit(c_file, ''' + } + ''', indent=1) elif obj.typ == 'object' or obj.typ == 'mapStringObject': l = len(obj.origname) if obj.subtypname: typename = obj.subtypname else: typename = helpers.get_prefixed_name(obj.name, prefix) - c_file.append(f' if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL))\n') - c_file.append(" {\n") - c_file.append(f' stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */);\n') + emit(c_file, f''' + if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) + {{ + stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */); + ''', indent=1) check_gen_status(c_file, indent=2) - c_file.append(f' stat = gen_{typename} (g, ptr != NULL ? ptr->{obj.fixname} : NULL, ctx, err);\n') + emit(c_file, f''' + stat = gen_{typename} (g, ptr != NULL ? ptr->{obj.fixname} : NULL, ctx, err); + ''', indent=2) check_gen_status(c_file, indent=2) - c_file.append(" }\n") + emit(c_file, ''' + } + ''', indent=1) elif obj.typ == 'array': get_obj_arr_obj_array(obj, c_file, prefix) elif helpers.valid_basic_map_name(obj.typ): l = len(obj.origname) - c_file.append(f' if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL))\n') - c_file.append(' {\n') - c_file.append(f' stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.fixname}"), {l} /* strlen ("{obj.fixname}") */);\n') + emit(c_file, f''' + if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) + {{ + stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.fixname}"), {l} /* strlen ("{obj.fixname}") */); + ''', indent=1) check_gen_status(c_file, indent=2) - c_file.append(f' stat = gen_{helpers.make_basic_map_name(obj.typ)} (g, ptr ? ptr->{obj.fixname} : NULL, ctx, err);\n') + emit(c_file, f''' + stat = gen_{helpers.make_basic_map_name(obj.typ)} (g, ptr ? ptr->{obj.fixname} : NULL, ctx, err); + ''', indent=2) check_gen_status(c_file, indent=2) - c_file.append(" }\n") + emit(c_file, ''' + } + ''', indent=1) def get_c_json(obj, c_file, prefix): @@ -839,40 +903,52 @@ def get_c_json(obj, c_file, prefix): objs = obj.subtypobj if objs is None: return - c_file.append( - f"yajl_gen_status\ngen_{typename} (yajl_gen g, const {typename} *ptr, const struct parser_context " \ - "*ctx, parser_error *err)\n") - c_file.append("{\n") - c_file.append(" yajl_gen_status stat = yajl_gen_status_ok;\n") - c_file.append(" *err = NULL;\n") - c_file.append(" (void) ptr; /* Silence compiler warning. */\n") + emit(c_file, f''' + yajl_gen_status + gen_{typename} (yajl_gen g, const {typename} *ptr, const struct parser_context *ctx, parser_error *err) + {{ + yajl_gen_status stat = yajl_gen_status_ok; + *err = NULL; + (void) ptr; /* Silence compiler warning. */ + ''', indent=0) if obj.typ == 'mapStringObject': get_map_string_obj(obj, c_file, prefix) elif obj.typ == 'object' or (obj.typ == 'array' and obj.subtypobj): nodes = obj.children if obj.typ == 'object' else obj.subtypobj if nodes is None: - c_file.append(' if (!(ctx->options & OPT_GEN_SIMPLIFY))\n') - c_file.append(' yajl_gen_config (g, yajl_gen_beautify, 0);\n') + emit(c_file, ''' + if (!(ctx->options & OPT_GEN_SIMPLIFY)) + yajl_gen_config (g, yajl_gen_beautify, 0); + ''', indent=1) - c_file.append(" stat = yajl_gen_map_open ((yajl_gen) g);\n") + emit(c_file, ''' + stat = yajl_gen_map_open ((yajl_gen) g); + ''', indent=1) check_gen_status(c_file, indent=1) for i in nodes or []: get_obj_arr_obj(i, c_file, prefix) if obj.typ == 'object': if obj.children is not None: - c_file.append(" if (ptr != NULL && ptr->_residual != NULL)\n") - c_file.append(" {\n") - c_file.append(" stat = gen_yajl_object_residual (ptr->_residual, g, err);\n") - c_file.append(" if (yajl_gen_status_ok != stat)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") - c_file.append(" }\n") - c_file.append(" stat = yajl_gen_map_close ((yajl_gen) g);\n") + emit(c_file, ''' + if (ptr != NULL && ptr->_residual != NULL) + { + stat = gen_yajl_object_residual (ptr->_residual, g, err); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN (stat, err); + } + ''', indent=1) + emit(c_file, ''' + stat = yajl_gen_map_close ((yajl_gen) g); + ''', indent=1) check_gen_status(c_file, indent=1) if nodes is None: - c_file.append(' if (!(ctx->options & OPT_GEN_SIMPLIFY))\n') - c_file.append(' yajl_gen_config (g, yajl_gen_beautify, 1);\n') - c_file.append(' return yajl_gen_status_ok;\n') - c_file.append("}\n\n") + emit(c_file, ''' + if (!(ctx->options & OPT_GEN_SIMPLIFY)) + yajl_gen_config (g, yajl_gen_beautify, 1); + ''', indent=1) + c_file.append(" return yajl_gen_status_ok;\n") + c_file.append("}\n") + c_file.append("\n") def read_val_generator(c_file, level, src, dest, typ, keyname, obj_typename): @@ -882,129 +958,158 @@ def read_val_generator(c_file, level, src, dest, typ, keyname, obj_typename): History: 2019-06-17 """ if helpers.valid_basic_map_name(typ): - c_file.append(f"{' ' * level}yajl_val val = {src};\n") - c_file.append(f"{' ' * level}if (val != NULL)\n") - c_file.append(f'{" " * level} {{\n') - c_file.append(f'{" " * (level + 1)}{dest} = make_{helpers.make_basic_map_name(typ)} (val, ctx, err);\n') - c_file.append(f"{' ' * (level + 1)}if ({dest} == NULL)\n") - c_file.append(f'{" " * (level + 1)} {{\n') - c_file.append(f"{' ' * (level + 1)} char *new_error = NULL;\n") - c_file.append(f"{' ' * (level + 1)} if (asprintf (&new_error, \"Value error for key '{keyname}': %s\", *err ? *err : \"null\") < 0)\n") - c_file.append(f'{" " * (level + 1)} new_error = strdup ("error allocating memory");\n') - c_file.append(f"{' ' * (level + 1)} free (*err);\n") - c_file.append(f"{' ' * (level + 1)} *err = new_error;\n") - c_file.append(f"{' ' * (level + 1)} return NULL;\n") - c_file.append(f'{" " * (level + 1)} }}\n') - c_file.append(f'{" " * (level)}}}\n') + emit(c_file, f''' + yajl_val val = {src}; + if (val != NULL) + {{ + {dest} = make_{helpers.make_basic_map_name(typ)} (val, ctx, err); + if ({dest} == NULL) + {{ + char *new_error = NULL; + if (asprintf (&new_error, "Value error for key '{keyname}': %s", *err ? *err : "null") < 0) + new_error = strdup ("error allocating memory"); + free (*err); + *err = new_error; + return NULL; + }} + }} + ''', indent=level) elif typ == 'string': - c_file.append(f"{' ' * level}yajl_val val = {src};\n") - c_file.append(f"{' ' * level}if (val != NULL)\n") - c_file.append(f"{' ' * (level)} {{\n") - c_file.append(f"{' ' * (level + 1)}char *str = YAJL_GET_STRING (val);\n") - c_file.append(f"{' ' * (level + 1)}{dest} = strdup (str ? str : \"\");\n") - c_file.append(f"{' ' * (level + 1)}if ({dest} == NULL)\n") - c_file.append(f"{' ' * (level + 1)} return NULL;\n") - c_file.append(f'{" " * level} }}\n') + emit(c_file, f''' + yajl_val val = {src}; + if (val != NULL) + {{ + char *str = YAJL_GET_STRING (val); + {dest} = strdup (str ? str : ""); + if ({dest} == NULL) + return NULL; + }} + ''', indent=level) elif helpers.judge_data_type(typ): - c_file.append(f"{' ' * level}yajl_val val = {src};\n") - c_file.append(f"{' ' * level}if (val != NULL)\n") - c_file.append(f'{" " * (level)} {{\n') + emit(c_file, f''' + yajl_val val = {src}; + if (val != NULL) + {{ + ''', indent=level) if typ.startswith("uint") or \ (typ.startswith("int") and typ != "integer") or typ == "double": - c_file.append(f"{' ' * (level + 1)}int invalid;\n") - c_file.append(f"{' ' * (level + 1)}if (! YAJL_IS_NUMBER (val))\n") - c_file.append(f'{" " * (level + 1)} {{\n') - c_file.append(f"{' ' * (level + 1)} *err = strdup (\"invalid type\");\n") - c_file.append(f"{' ' * (level + 1)} return NULL;\n") - c_file.append(f'{" " * (level + 1)} }}\n') - c_file.append(f'{" " * (level + 1)}invalid = common_safe_{typ} (YAJL_GET_NUMBER (val), &{dest});\n') + emit(c_file, f''' + int invalid; + if (! YAJL_IS_NUMBER (val)) + {{ + *err = strdup ("invalid type"); + return NULL; + }} + invalid = common_safe_{typ} (YAJL_GET_NUMBER (val), &{dest}); + ''', indent=level + 1) elif typ == "integer": - c_file.append(f"{' ' * (level + 1)}int invalid;\n") - c_file.append(f"{' ' * (level + 1)}if (! YAJL_IS_NUMBER (val))\n") - c_file.append(f'{" " * (level + 1)} {{\n') - c_file.append(f"{' ' * (level + 1)} *err = strdup (\"invalid type\");\n") - c_file.append(f"{' ' * (level + 1)} return NULL;\n") - c_file.append(f'{" " * (level + 1)} }}\n') - c_file.append(f'{" " * (level + 1)}invalid = common_safe_int (YAJL_GET_NUMBER (val), (int *)&{dest});\n') + emit(c_file, f''' + int invalid; + if (! YAJL_IS_NUMBER (val)) + {{ + *err = strdup ("invalid type"); + return NULL; + }} + invalid = common_safe_int (YAJL_GET_NUMBER (val), (int *)&{dest}); + ''', indent=level + 1) elif typ == "UID" or typ == "GID": - c_file.append(f"{' ' * (level + 1)}int invalid;\n") - c_file.append(f"{' ' * (level + 1)}if (! YAJL_IS_NUMBER (val))\n") - c_file.append(f'{" " * (level + 1)} {{\n') - c_file.append(f"{' ' * (level + 1)} *err = strdup (\"invalid type\");\n") - c_file.append(f"{' ' * (level + 1)} return NULL;\n") - c_file.append(f'{" " * (level + 1)} }}\n') - c_file.append(f'{" " * (level + 1)}invalid = common_safe_uint (YAJL_GET_NUMBER (val), (unsigned int *)&{dest});\n') - c_file.append(f"{' ' * (level + 1)}if (invalid)\n") - c_file.append(f'{" " * (level + 1)} {{\n') - c_file.append(f'{" " * (level + 1)} if (asprintf (err, "Invalid value \'%s\' with type \'{typ}\' for key \'{keyname}\': %s", YAJL_GET_NUMBER (val), strerror (-invalid)) < 0)\n') - c_file.append(f'{" " * (level + 1)} *err = strdup ("error allocating memory");\n') - c_file.append(f"{' ' * (level + 1)} return NULL;\n") - c_file.append(f'{" " * (level + 1)}}}\n') + emit(c_file, f''' + int invalid; + if (! YAJL_IS_NUMBER (val)) + {{ + *err = strdup ("invalid type"); + return NULL; + }} + invalid = common_safe_uint (YAJL_GET_NUMBER (val), (unsigned int *)&{dest}); + ''', indent=level + 1) + emit(c_file, f''' + if (invalid) + {{ + if (asprintf (err, "Invalid value '%s' with type '{typ}' for key '{keyname}': %s", YAJL_GET_NUMBER (val), strerror (-invalid)) < 0) + *err = strdup ("error allocating memory"); + return NULL; + }} + ''', indent=level + 1) if '[' not in dest: - c_file.append(f"{' ' * (level + 1)}{dest}_present = 1;\n") - c_file.append(f'{" " * (level)}}}\n') + emit(c_file, f''' + {dest}_present = 1; + ''', indent=level + 1) + emit(c_file, f''' + }} + ''', indent=level) elif helpers.judge_data_pointer_type(typ): num_type = helpers.obtain_data_pointer_type(typ) if num_type == "": return - c_file.append(f"{' ' * level}yajl_val val = {src};\n") - c_file.append(f"{' ' * level}if (val != NULL)\n") - c_file.append(f'{" " * (level)} {{\n') - c_file.append(f'{" " * (level + 1)}{dest} = calloc (1, sizeof ({helpers.get_map_c_types(num_type)}));\n') - c_file.append(f"{' ' * (level + 1)}if ({dest} == NULL)\n") - c_file.append(f"{' ' * (level + 1)} return NULL;\n") - c_file.append(f"{' ' * (level + 1)}int invalid;\n") - c_file.append(f"{' ' * (level + 1)}if (! YAJL_IS_NUMBER (val))\n") - c_file.append(f'{" " * (level + 1)} {{\n') - c_file.append(f"{' ' * (level + 1)} *err = strdup (\"invalid type\");\n") - c_file.append(f"{' ' * (level + 1)} return NULL;\n") - c_file.append(f'{" " * (level + 1)}}}\n') - c_file.append(f'{" " * (level + 1)}sinvalid = common_safe_{num_type} (YAJL_GET_NUMBER (val), {dest});\n') - c_file.append(f"{' ' * (level + 1)}if (invalid)\n") - c_file.append(f'{" " * (level + 1)} {{\n') - c_file.append(f'{" " * (level + 1)} if (asprintf (err, "Invalid value \'%s\' with type \'{typ}\' ' \ - f'for key \'{keyname}\': %s", YAJL_GET_NUMBER (val), strerror (-invalid)) < 0)\n') - c_file.append(f'{" " * (level + 1)} *err = strdup ("error allocating memory");\n') - c_file.append(f"{' ' * (level + 1)} return NULL;\n") - c_file.append(f'{" " * (level + 1)}}}\n') - c_file.append(f'{" " * (level)}}}\n') + emit(c_file, f''' + yajl_val val = {src}; + if (val != NULL) + {{ + {dest} = calloc (1, sizeof ({helpers.get_map_c_types(num_type)})); + if ({dest} == NULL) + return NULL; + int invalid; + if (! YAJL_IS_NUMBER (val)) + {{ + *err = strdup ("invalid type"); + return NULL; + }} + invalid = common_safe_{num_type} (YAJL_GET_NUMBER (val), {dest}); + if (invalid) + {{ + if (asprintf (err, "Invalid value '%s' with type '{typ}' for key '{keyname}': %s", YAJL_GET_NUMBER (val), strerror (-invalid)) < 0) + *err = strdup ("error allocating memory"); + return NULL; + }} + }} + ''', indent=level) elif typ == 'boolean': - c_file.append(f"{' ' * level}yajl_val val = {src};\n") - c_file.append(f"{' ' * level}if (val != NULL)\n") - c_file.append(f'{" " * (level)} {{\n') - c_file.append(f"{' ' * (level + 1)}{dest} = YAJL_IS_TRUE(val);\n") + emit(c_file, f''' + yajl_val val = {src}; + if (val != NULL) + {{ + {dest} = YAJL_IS_TRUE(val); + ''', indent=level) if '[' not in dest: - c_file.append(f"{' ' * (level + 1)}{dest}_present = 1;\n") - c_file.append(f'{" " * (level)} }}\n') - c_file.append(f"{' ' * level}else\n") - c_file.append(f'{" " * (level)} {{\n') - c_file.append(f"{' ' * (level + 1)}val = {src.replace('yajl_t_true', 'yajl_t_false')};\n") - c_file.append(f"{' ' * (level + 1)}if (val != NULL)\n") - c_file.append(f'{" " * (level+1)} {{\n') - c_file.append(f"{' ' * (level + 2)}{dest} = 0;\n") - c_file.append(f"{' ' * (level + 2)}{dest}_present = 1;\n") - c_file.append(f'{" " * (level+1)} }}\n') - c_file.append(f'{" " * (level)} }}\n') + emit(c_file, f''' + {dest}_present = 1; + }} + else + {{ + val = {src.replace('yajl_t_true', 'yajl_t_false')}; + if (val != NULL) + {{ + {dest} = 0; + {dest}_present = 1; + }} + }} + ''', indent=level + 1) + else: + emit(c_file, f''' + }} + ''', indent=level) elif typ == 'booleanPointer': - c_file.append(f"{' ' * level}yajl_val val = {src};\n") - c_file.append(f"{' ' * level}if (val != NULL)\n") - c_file.append(f'{" " * (level)} {{\n') - c_file.append(f"{' ' * (level + 1)}{dest} = calloc (1, sizeof (bool));\n") - c_file.append(f"{' ' * (level + 1)}if ({dest} == NULL)\n") - c_file.append(f"{' ' * (level + 1)} return NULL;\n") - c_file.append(f"{' ' * (level + 1)}*({dest}) = YAJL_IS_TRUE(val);\n") - c_file.append(f'{" " * (level)} }}\n') - c_file.append(f"{' ' * level}else\n") - c_file.append(f'{" " * (level)} {{\n') - c_file.append(f'{" " * (level + 1)}val = get_val (tree, "{keyname}", yajl_t_false);\n') - c_file.append(f"{' ' * (level + 1)}if (val != NULL)\n") - c_file.append(f'{" " * (level + 1)} {{\n') - c_file.append(f"{' ' * (level + 2)}{dest} = calloc (1, sizeof (bool));\n") - c_file.append(f"{' ' * (level + 2)}if ({dest} == NULL)\n") - c_file.append(f"{' ' * (level + 2)} return NULL;\n") - c_file.append(f"{' ' * (level + 2)}*({dest}) = YAJL_IS_TRUE(val);\n") - c_file.append(f'{" " * (level + 1)}}}\n') - c_file.append(f'{" " * (level)}}}\n') + emit(c_file, f''' + yajl_val val = {src}; + if (val != NULL) + {{ + {dest} = calloc (1, sizeof (bool)); + if ({dest} == NULL) + return NULL; + *({dest}) = YAJL_IS_TRUE(val); + }} + else + {{ + val = get_val (tree, "{keyname}", yajl_t_false); + if (val != NULL) + {{ + {dest} = calloc (1, sizeof (bool)); + if ({dest} == NULL) + return NULL; + *({dest}) = YAJL_IS_TRUE(val); + }} + }} + ''', indent=level) def make_clone(obj, c_file, prefix): @@ -1028,110 +1133,142 @@ def make_clone(obj, c_file, prefix): else: typename = helpers.get_name_substr(obj.name, prefix) - c_file.append(f"{typename} *\nclone_{typename} ({typename} *src)\n") - c_file.append("{\n") - c_file.append(" (void) src; /* Silence compiler warning. */\n") - c_file.append(f" __auto_cleanup(free_{typename}) {typename} *ret = NULL;\n") + emit(c_file, f''' + {typename} * + clone_{typename} ({typename} *src) + {{ + (void) src; /* Silence compiler warning. */ + __auto_cleanup(free_{typename}) {typename} *ret = NULL; - c_file.append(" ret = calloc (1, sizeof (*ret));\n") - c_file.append(" if (ret == NULL)\n") - c_file.append(" return NULL;\n") + ret = calloc (1, sizeof (*ret)); + if (ret == NULL) + return NULL; + ''', indent=0) nodes = obj.children if obj.subtypobj is None else obj.subtypobj for i in nodes or []: if helpers.judge_data_type(i.typ) or i.typ == 'boolean': - c_file.append(f" ret->{i.fixname} = src->{i.fixname};\n") - c_file.append(f" ret->{i.fixname}_present = src->{i.fixname}_present;\n") + emit(c_file, f''' + ret->{i.fixname} = src->{i.fixname}; + ret->{i.fixname}_present = src->{i.fixname}_present; + ''', indent=1) elif i.typ == 'object': node_name = i.subtypname or helpers.get_prefixed_name(i.name, prefix) - c_file.append(f" if (src->{i.fixname})\n") - c_file.append(f" {{\n") if obj.typ != 'mapStringObject': - c_file.append(f" ret->{i.fixname} = clone_{node_name} (src->{i.fixname});\n") - c_file.append(f" if (ret->{i.fixname} == NULL)\n") - c_file.append(f" return NULL;\n") + emit(c_file, f''' + if (src->{i.fixname}) + {{ + ret->{i.fixname} = clone_{node_name} (src->{i.fixname}); + if (ret->{i.fixname} == NULL) + return NULL; + }} + ''', indent=1) else: - c_file.append(f" size_t i;\n") - c_file.append(f" ret->{i.fixname} = calloc (src->len + 1, sizeof (*ret->{i.fixname}));\n") - c_file.append(f" for (i = 0; i < src->len; i++)\n") - c_file.append(" {\n") - c_file.append(f" ret->{i.fixname}[i] = clone_{node_name} (src->{i.fixname}[i]);\n") - c_file.append(f" if (ret->{i.fixname}[i] == NULL)\n") - c_file.append(f" return NULL;\n") - c_file.append(" }\n") - c_file.append(f" }}\n") + emit(c_file, f''' + if (src->{i.fixname}) + {{ + size_t i; + ret->{i.fixname} = calloc (src->len + 1, sizeof (*ret->{i.fixname})); + for (i = 0; i < src->len; i++) + {{ + ret->{i.fixname}[i] = clone_{node_name} (src->{i.fixname}[i]); + if (ret->{i.fixname}[i] == NULL) + return NULL; + }} + }} + ''', indent=1) elif i.typ == 'string': - c_file.append(f" if (src->{i.fixname})\n") - c_file.append(f" {{\n") - c_file.append(f" ret->{i.fixname} = strdup (src->{i.fixname});\n") - c_file.append(f" if (ret->{i.fixname} == NULL)\n") - c_file.append(f" return NULL;\n") - c_file.append(f" }}\n") + emit(c_file, f''' + if (src->{i.fixname}) + {{ + ret->{i.fixname} = strdup (src->{i.fixname}); + if (ret->{i.fixname} == NULL) + return NULL; + }} + ''', indent=1) elif i.typ == 'array': - c_file.append(f" if (src->{i.fixname})\n") - c_file.append(f" {{\n") - c_file.append(f" ret->{i.fixname}_len = src->{i.fixname}_len;\n") - c_file.append(f" ret->{i.fixname} = calloc (src->{i.fixname}_len + 1, sizeof (*ret->{i.fixname}));\n") - c_file.append(f" if (ret->{i.fixname} == NULL)\n") - c_file.append(f" return NULL;\n") - c_file.append(f" for (size_t i = 0; i < src->{i.fixname}_len; i++)\n") - c_file.append(f" {{\n") + emit(c_file, f''' + if (src->{i.fixname}) + {{ + ret->{i.fixname}_len = src->{i.fixname}_len; + ret->{i.fixname} = calloc (src->{i.fixname}_len + 1, sizeof (*ret->{i.fixname})); + if (ret->{i.fixname} == NULL) + return NULL; + for (size_t i = 0; i < src->{i.fixname}_len; i++) + {{ + ''', indent=1) if helpers.judge_data_type(i.subtyp) or i.subtyp == 'boolean': - c_file.append(f" ret->{i.fixname}[i] = src->{i.fixname}[i];\n") + emit(c_file, f''' + ret->{i.fixname}[i] = src->{i.fixname}[i]; + ''', indent=3) elif i.subtyp == 'object': subnode_name = i.subtypname or helpers.get_prefixed_name(i.name, prefix) if False: # i.subtypname is not None: typename = i.subtypname - c_file.append(f" ret->{i.fixname}[i] = clone_{typename} (src->{i.fixname}[i]);\n") - c_file.append(f" if (ret->{i.fixname}[i] == NULL)\n") - c_file.append(f" return NULL;\n") + emit(c_file, f''' + ret->{i.fixname}[i] = clone_{typename} (src->{i.fixname}[i]); + if (ret->{i.fixname}[i] == NULL) + return NULL; + ''', indent=3) else: typename = helpers.get_prefixed_name(i.name, prefix) if i.subtypname is not None: typename = i.subtypname maybe_element = "_element" if i.subtypname is None else "" if i.doublearray: - c_file.append(f" ret->{i.fixname}_item_lens[i] = src->{i.fixname}_item_lens[i];\n") - c_file.append(f" ret->{i.fixname}[i] = calloc (ret->{i.fixname}_item_lens[i] + 1, sizeof (**ret->{i.fixname}[i]));\n") - c_file.append(f" if (ret->{i.fixname}[i] == NULL)\n") - c_file.append(f" return NULL;\n") - c_file.append(f" for (size_t j = 0; j < src->{i.fixname}_item_lens[i]; j++)\n") - c_file.append(f" {{\n") - c_file.append(f" ret->{i.fixname}[i][j] = clone_{typename}{maybe_element} (src->{i.fixname}[i][j]);\n") - c_file.append(f" if (ret->{i.fixname}[i][j] == NULL)\n") - c_file.append(f" return NULL;\n") - c_file.append(f" }}\n") + emit(c_file, f''' + ret->{i.fixname}_item_lens[i] = src->{i.fixname}_item_lens[i]; + ret->{i.fixname}[i] = calloc (ret->{i.fixname}_item_lens[i] + 1, sizeof (**ret->{i.fixname}[i])); + if (ret->{i.fixname}[i] == NULL) + return NULL; + for (size_t j = 0; j < src->{i.fixname}_item_lens[i]; j++) + {{ + ret->{i.fixname}[i][j] = clone_{typename}{maybe_element} (src->{i.fixname}[i][j]); + if (ret->{i.fixname}[i][j] == NULL) + return NULL; + }} + ''', indent=3) else: - c_file.append(f" ret->{i.fixname}[i] = clone_{typename}{maybe_element} (src->{i.fixname}[i]);\n") - c_file.append(f" if (ret->{i.fixname}[i] == NULL)\n") - c_file.append(f" return NULL;\n") + emit(c_file, f''' + ret->{i.fixname}[i] = clone_{typename}{maybe_element} (src->{i.fixname}[i]); + if (ret->{i.fixname}[i] == NULL) + return NULL; + ''', indent=3) elif i.subtyp == 'string': if i.doublearray: - c_file.append(f" ret->{i.fixname}[i] = calloc (ret->{i.fixname}_item_lens[i] + 1, sizeof (**ret->{i.fixname}[i]));\n") - c_file.append(f" if (ret->{i.fixname}[i] == NULL)\n") - c_file.append(f" return NULL;\n") - c_file.append(f" for (size_t j = 0; j < src->{i.fixname}_item_lens[i]; j++)\n") - c_file.append(f" {{\n") - c_file.append(f" ret->{i.fixname}[i][j] = strdup (src->{i.fixname}[i][j]);\n") - c_file.append(f" if (ret->{i.fixname}[i][j] == NULL)\n") - c_file.append(f" return NULL;\n") - c_file.append(f" }}\n") + emit(c_file, f''' + ret->{i.fixname}[i] = calloc (ret->{i.fixname}_item_lens[i] + 1, sizeof (**ret->{i.fixname}[i])); + if (ret->{i.fixname}[i] == NULL) + return NULL; + for (size_t j = 0; j < src->{i.fixname}_item_lens[i]; j++) + {{ + ret->{i.fixname}[i][j] = strdup (src->{i.fixname}[i][j]); + if (ret->{i.fixname}[i][j] == NULL) + return NULL; + }} + ''', indent=3) else: - c_file.append(f" if (src->{i.fixname}[i])\n") - c_file.append(f" {{\n") - c_file.append(f" ret->{i.fixname}[i] = strdup (src->{i.fixname}[i]);\n") - c_file.append(f" if (ret->{i.fixname}[i] == NULL)\n") - c_file.append(f" return NULL;\n") - c_file.append(f" }}\n") + emit(c_file, f''' + if (src->{i.fixname}[i]) + {{ + ret->{i.fixname}[i] = strdup (src->{i.fixname}[i]); + if (ret->{i.fixname}[i] == NULL) + return NULL; + }} + ''', indent=3) else: raise Exception("Unimplemented type for array clone: %s (%s)" % (i.subtyp, i.subtypname)) - c_file.append(f" }}\n") - c_file.append(f" }}\n") + emit(c_file, f''' + }} + }} + ''', indent=2) elif i.typ == 'mapStringString': - c_file.append(f" ret->{i.fixname} = clone_map_string_string (src->{i.fixname});\n") - c_file.append(f" if (ret->{i.fixname} == NULL)\n") - c_file.append(f" return NULL;\n") + emit(c_file, f''' + ret->{i.fixname} = clone_map_string_string (src->{i.fixname}); + if (ret->{i.fixname} == NULL) + return NULL; + ''', indent=1) elif i.typ == 'mapStringObject': if i.subtypname is not None: subtypname = i.subtypname @@ -1140,33 +1277,38 @@ def make_clone(obj, c_file, prefix): subtypname = i.children[0].subtypname maybe_element = "" - c_file.append(f" if (src->{i.fixname})\n") - c_file.append(f" {{\n") - c_file.append(f" ret->{i.fixname} = calloc (1, sizeof (*ret->{i.fixname}));\n") - c_file.append(f" if (ret->{i.fixname} == NULL)\n") - c_file.append(f" return NULL;\n") - c_file.append(f" ret->{i.fixname}->len = src->{i.fixname}->len;\n") - c_file.append(f" ret->{i.fixname}->keys = calloc (src->{i.fixname}->len + 1, sizeof (char *));\n") - c_file.append(f" if (ret->{i.fixname}->keys == NULL)\n") - c_file.append(f" return NULL;\n") - c_file.append(f" ret->{i.fixname}->values = calloc (src->{i.fixname}->len + 1, sizeof (*ret->{i.fixname}->values));\n") - c_file.append(f" if (ret->{i.fixname}->values == NULL)\n") - c_file.append(f" return NULL;\n") - c_file.append(f" for (size_t i = 0; i < ret->{i.fixname}->len; i++)\n") - c_file.append(f" {{\n") - c_file.append(f" ret->{i.fixname}->keys[i] = strdup (src->{i.fixname}->keys[i]);\n") - c_file.append(f" if (ret->{i.fixname}->keys[i] == NULL)\n") - c_file.append(f" return NULL;\n") - c_file.append(f" ret->{i.fixname}->values[i] = clone_{subtypname}{maybe_element} (src->{i.fixname}->values[i]);\n") - c_file.append(f" if (ret->{i.fixname}->values[i] == NULL)\n") - c_file.append(f" return NULL;\n") - c_file.append(f" }}\n") - c_file.append(f" }}\n") + emit(c_file, f''' + if (src->{i.fixname}) + {{ + ret->{i.fixname} = calloc (1, sizeof (*ret->{i.fixname})); + if (ret->{i.fixname} == NULL) + return NULL; + ret->{i.fixname}->len = src->{i.fixname}->len; + ret->{i.fixname}->keys = calloc (src->{i.fixname}->len + 1, sizeof (char *)); + if (ret->{i.fixname}->keys == NULL) + return NULL; + ret->{i.fixname}->values = calloc (src->{i.fixname}->len + 1, sizeof (*ret->{i.fixname}->values)); + if (ret->{i.fixname}->values == NULL) + return NULL; + for (size_t i = 0; i < ret->{i.fixname}->len; i++) + {{ + ret->{i.fixname}->keys[i] = strdup (src->{i.fixname}->keys[i]); + if (ret->{i.fixname}->keys[i] == NULL) + return NULL; + ret->{i.fixname}->values[i] = clone_{subtypname}{maybe_element} (src->{i.fixname}->values[i]); + if (ret->{i.fixname}->values[i] == NULL) + return NULL; + }} + }} + ''', indent=1) else: raise Exception("Unimplemented type for clone: %s" % i.typ) - c_file.append(f" return move_ptr (ret);\n") - c_file.append("}\n\n") + emit(c_file, ''' + return move_ptr (ret); + } + + ''', indent=1) def json_value_generator(c_file, level, src, dst, ptx, typ): @@ -1176,26 +1318,40 @@ def json_value_generator(c_file, level, src, dst, ptx, typ): History: 2019-06-17 """ if helpers.valid_basic_map_name(typ): - c_file.append(f'{" " * (level)}stat = gen_{helpers.make_basic_map_name(typ)} ({dst}, {src}, {ptx}, err);\n') - c_file.append(f"{' ' * level}if (stat != yajl_gen_status_ok)\n") - c_file.append(f"{' ' * (level + 1)}GEN_SET_ERROR_AND_RETURN (stat, err);\n") + emit(c_file, f''' + stat = gen_{helpers.make_basic_map_name(typ)} ({dst}, {src}, {ptx}, err); + if (stat != yajl_gen_status_ok) + GEN_SET_ERROR_AND_RETURN (stat, err); + ''', indent=level) elif typ == 'string': - c_file.append(f'{" " * (level)}stat = yajl_gen_string ((yajl_gen){dst}, (const unsigned char *)({src}), strlen ({src}));\n') - c_file.append(f"{' ' * level}if (stat != yajl_gen_status_ok)\n") - c_file.append(f"{' ' * (level + 1)}GEN_SET_ERROR_AND_RETURN (stat, err);\n") + emit(c_file, f''' + stat = yajl_gen_string ((yajl_gen){dst}, (const unsigned char *)({src}), strlen ({src})); + if (stat != yajl_gen_status_ok) + GEN_SET_ERROR_AND_RETURN (stat, err); + ''', indent=level) elif helpers.judge_data_type(typ): if typ == 'double': - c_file.append(f'{" " * (level)}stat = yajl_gen_double ((yajl_gen){dst}, {src});\n') + emit(c_file, f''' + stat = yajl_gen_double ((yajl_gen){dst}, {src}); + ''', indent=level) elif typ.startswith("uint") or typ == 'GID' or typ == 'UID': - c_file.append(f"{' ' * level}stat = map_uint ({dst}, {src});\n") + emit(c_file, f''' + stat = map_uint ({dst}, {src}); + ''', indent=level) else: - c_file.append(f"{' ' * level}stat = map_int ({dst}, {src});\n") - c_file.append(f"{' ' * level}if (stat != yajl_gen_status_ok)\n") - c_file.append(f"{' ' * (level + 1)}GEN_SET_ERROR_AND_RETURN (stat, err);\n") + emit(c_file, f''' + stat = map_int ({dst}, {src}); + ''', indent=level) + emit(c_file, f''' + if (stat != yajl_gen_status_ok) + GEN_SET_ERROR_AND_RETURN (stat, err); + ''', indent=level) elif typ == 'boolean': - c_file.append(f'{" " * (level)}stat = yajl_gen_bool ((yajl_gen){dst}, (int)({src}));\n') - c_file.append(f"{' ' * level}if (stat != yajl_gen_status_ok)\n") - c_file.append(f"{' ' * (level + 1)}GEN_SET_ERROR_AND_RETURN (stat, err);\n") + emit(c_file, f''' + stat = yajl_gen_bool ((yajl_gen){dst}, (int)({src})); + if (stat != yajl_gen_status_ok) + GEN_SET_ERROR_AND_RETURN (stat, err); + ''', indent=level) def make_c_array_free (i, c_file, prefix): if helpers.valid_basic_map_name(i.subtyp): @@ -1468,15 +1624,19 @@ def src_reflect(structs, schema_info, c_file, root_typ): Interface: None History: 2019-06-17 """ - c_file.append(f"/* Generated from {schema_info.name.basename}. Do not edit! */\n\n") - c_file.append("#ifndef _GNU_SOURCE\n") - c_file.append("#define _GNU_SOURCE\n") - c_file.append("#endif\n") - c_file.append('#include \n') - c_file.append('#include \n') - c_file.append(f'#include "ocispec/{schema_info.header.basename}"\n\n') - c_file.append('#define YAJL_GET_ARRAY_NO_CHECK(v) (&(v)->u.array)\n') - c_file.append('#define YAJL_GET_OBJECT_NO_CHECK(v) (&(v)->u.object)\n') + emit(c_file, f''' + /* Generated from {schema_info.name.basename}. Do not edit! */ + + #ifndef _GNU_SOURCE + #define _GNU_SOURCE + #endif + #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 i in structs: append_c_code(i, c_file, schema_info.prefix) @@ -1492,40 +1652,45 @@ def get_c_epilog_for_array_make_parse(c_file, prefix, typ, obj): return typename = helpers.get_top_array_type_name(obj.name, prefix) - c_file.append(f"\ndefine_cleaner_function ({typename} *, free_{typename})\n" + - f"{typename}\n" + - f"*make_{typename} (yajl_val tree, const struct parser_context *ctx, parser_error *err)\n" + - "{\n" + - f" __auto_cleanup(free_{typename}) {typename} *ptr = NULL;\n" + - f" size_t i, alen;\n" + - f" "+ - f" (void) ctx;\n" + - f" "+ - f" if (tree == NULL || err == NULL || YAJL_GET_ARRAY (tree) == NULL)\n" + - f" return NULL;\n" + - f" *err = NULL;\n" + - f" alen = YAJL_GET_ARRAY_NO_CHECK (tree)->len;\n" + - f" if (alen == 0)\n" + - f" return NULL;\n" + - f" ptr = calloc (1, sizeof ({typename}));\n" + - f" if (ptr == NULL)\n" + - f" return NULL;\n" + - f" ptr->items = calloc (alen + 1, sizeof(*ptr->items));\n" + - f" if (ptr->items == NULL)\n" + - f" return NULL;\n" + - f" ptr->len = alen;\n" - ) + emit(c_file, f''' + + define_cleaner_function ({typename} *, free_{typename}) + {typename} + *make_{typename} (yajl_val 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) + return NULL; + *err = NULL; + alen = YAJL_GET_ARRAY_NO_CHECK (tree)->len; + if (alen == 0) + return NULL; + ptr = calloc (1, sizeof ({typename})); + if (ptr == NULL) + return NULL; + ptr->items = calloc (alen + 1, sizeof(*ptr->items)); + if (ptr->items == NULL) + return NULL; + ptr->len = alen; + ''', indent=0) if obj.doublearray: - c_file.append(' ptr->subitem_lens = calloc ( alen + 1, sizeof (size_t));\n') - c_file.append(' if (ptr->subitem_lens == NULL)\n') - c_file.append(' return NULL;') + emit(c_file, ''' + ptr->subitem_lens = calloc ( alen + 1, sizeof (size_t)); + if (ptr->subitem_lens == NULL) + return NULL; + ''', indent=1) - c_file.append("""\n - for (i = 0; i < alen; i++) - { - yajl_val work = YAJL_GET_ARRAY_NO_CHECK (tree)->values[i]; -"""); + emit(c_file, ''' + + for (i = 0; i < alen; i++) + { + yajl_val work = YAJL_GET_ARRAY_NO_CHECK (tree)->values[i]; + ''', indent=1) if obj.subtypobj or obj.subtyp == 'object': if obj.subtypname: @@ -1534,54 +1699,67 @@ def get_c_epilog_for_array_make_parse(c_file, prefix, typ, obj): subtypename = helpers.get_name_substr(obj.name, prefix) if obj.doublearray: - c_file.append(' size_t j;\n') - c_file.append(' ptr->items[i] = calloc ( YAJL_GET_ARRAY_NO_CHECK(work)->len + 1, sizeof (**ptr->items));\n') - c_file.append(' if (ptr->items[i] == NULL)\n') - c_file.append(' return NULL;\n') - c_file.append(' yajl_val *tmps = YAJL_GET_ARRAY_NO_CHECK(work)->values;\n') - c_file.append(' for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(work)->len; j++)\n') - c_file.append(' {\n') - c_file.append(f' ptr->items[i][j] = make_{subtypename} (tmps[j], ctx, err);\n') - c_file.append(' if (ptr->items[i][j] == NULL)\n') - c_file.append(" return NULL;\n") - c_file.append(' ptr->subitem_lens[i] += 1;\n') - c_file.append(' }\n') + emit(c_file, f''' + size_t j; + ptr->items[i] = calloc ( YAJL_GET_ARRAY_NO_CHECK(work)->len + 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++) + {{ + ptr->items[i][j] = make_{subtypename} (tmps[j], ctx, err); + if (ptr->items[i][j] == NULL) + return NULL; + ptr->subitem_lens[i] += 1; + }} + ''', indent=2) else: - c_file.append(f' ptr->items[i] = make_{subtypename} (work, ctx, err);\n') - c_file.append(' if (ptr->items[i] == NULL)\n') - c_file.append(" return NULL;\n") + emit(c_file, f''' + ptr->items[i] = make_{subtypename} (work, ctx, err); + if (ptr->items[i] == NULL) + return NULL; + ''', indent=2) elif obj.subtyp == 'byte': if obj.doublearray: - c_file.append(' char *str = YAJL_GET_STRING (work);\n') - c_file.append(' ptr->items[j] = (uint8_t *)strdup (str ? str : "");\n') - c_file.append(' if (ptr->items[j] == NULL)\n') - c_file.append(" return NULL;\n") + emit(c_file, ''' + char *str = YAJL_GET_STRING (work); + ptr->items[j] = (uint8_t *)strdup (str ? str : ""); + if (ptr->items[j] == NULL) + return NULL; + ''', indent=2) else: - c_file.append(' char *str = YAJL_GET_STRING (tree);\n') - c_file.append(' memcpy(ptr->items, str ? str : "", strlen(str ? str : ""));\n') - c_file.append(' break;\n') + emit(c_file, ''' + char *str = YAJL_GET_STRING (tree); + memcpy(ptr->items, str ? str : "", strlen(str ? str : "")); + break; + ''', indent=2) else: if obj.doublearray: - c_file.append(' ptr->items[i] = calloc ( YAJL_GET_ARRAY_NO_CHECK(work)->len + 1, sizeof (**ptr->items));\n') - c_file.append(' if (ptr->items[i] == NULL)\n') - c_file.append(' return NULL;\n') - c_file.append(' size_t j;\n') - c_file.append(' yajl_val *tmps = YAJL_GET_ARRAY_NO_CHECK(work)->values;\n') - c_file.append(' for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(work)->len; j++)\n') - c_file.append(' {\n') + emit(c_file, ''' + ptr->items[i] = calloc ( YAJL_GET_ARRAY_NO_CHECK(work)->len + 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++) + { + ''', indent=2) read_val_generator(c_file, 3, 'tmps[j]', \ "ptr->items[i][j]", obj.subtyp, obj.origname, c_typ) - c_file.append(' ptr->subitem_lens[i] += 1;\n') - c_file.append(' }\n') + emit(c_file, ''' + ptr->subitem_lens[i] += 1; + } + ''', indent=3) else: read_val_generator(c_file, 2, 'work', \ "ptr->items[i]", obj.subtyp, obj.origname, c_typ) - c_file.append("""\n - } - return move_ptr(ptr); -} -""") + emit(c_file, ''' + + } + return move_ptr(ptr); + } + ''', indent=1) def get_c_epilog_for_array_make_free(c_file, prefix, typ, obj): c_typ = helpers.get_prefixed_pointer(obj.name, obj.subtyp, prefix) or \ @@ -1592,41 +1770,52 @@ def get_c_epilog_for_array_make_free(c_file, prefix, typ, obj): return typename = helpers.get_top_array_type_name(obj.name, prefix) - c_file.append(f"\n\nvoid free_{typename} ({typename} *ptr)" + """ -{ - size_t i; + emit(c_file, f''' - if (ptr == NULL) - return; - for (i = 0; i < ptr->len; i++) - { -""") + void free_{typename} ({typename} *ptr) + {{ + size_t i; + + if (ptr == NULL) + return; + + for (i = 0; i < ptr->len; i++) + {{ + ''', indent=0) if helpers.valid_basic_map_name(obj.subtyp): free_func = helpers.make_basic_map_name(obj.subtyp) - c_file.append(" if (ptr->items[i] != NULL)\n") - c_file.append(" {\n") - c_file.append(f" free_{free_func} (ptr->items[i]);\n") - c_file.append(" ptr->items[i] = NULL;\n") - c_file.append(" }\n") + emit(c_file, f''' + if (ptr->items[i] != NULL) + {{ + free_{free_func} (ptr->items[i]); + ptr->items[i] = NULL; + }} + ''', indent=2) elif obj.subtyp == 'string': if obj.doublearray: - c_file.append(" size_t j;\n") - c_file.append(" for (j = 0; j < ptr->subitem_lens[i]; j++)\n") - c_file.append(" {\n") - c_file.append(" free (ptr->items[i][j]);\n") - c_file.append(" ptr->items[i][j] = NULL;\n") - c_file.append(" }\n") - c_file.append(" free (ptr->items[i]);\n") - c_file.append(" ptr->items[i] = NULL;\n") + emit(c_file, ''' + size_t j; + for (j = 0; j < ptr->subitem_lens[i]; j++) + { + free (ptr->items[i][j]); + ptr->items[i][j] = NULL; + } + free (ptr->items[i]); + ptr->items[i] = NULL; + ''', indent=2) else: - c_file.append(" free (ptr->items[i]);\n") - c_file.append(" ptr->items[i] = NULL;\n") + emit(c_file, ''' + free (ptr->items[i]); + ptr->items[i] = NULL; + ''', indent=2) elif not helpers.is_compound_type(obj.subtyp): if obj.doublearray: - c_file.append(" free (ptr->items[i]);\n") - c_file.append(" ptr->items[i] = NULL;\n") + emit(c_file, ''' + free (ptr->items[i]); + ptr->items[i] = NULL; + ''', indent=2) elif obj.subtyp == 'object' or obj.subtypobj is not None: if obj.subtypname is not None: free_func = obj.subtypname @@ -1634,42 +1823,51 @@ def get_c_epilog_for_array_make_free(c_file, prefix, typ, obj): free_func = helpers.get_name_substr(obj.name, prefix) if obj.doublearray: - c_file.append(" size_t j;\n") - c_file.append(" for (j = 0; j < ptr->subitem_lens[i]; j++)\n") - c_file.append(" {\n") - c_file.append(f" free_{free_func} (ptr->items[i][j]);\n") - c_file.append(" ptr->items[i][j] = NULL;\n") - c_file.append(" }\n") - c_file.append(" free (ptr->items[i]);\n") - c_file.append(" ptr->items[i] = NULL;\n") + emit(c_file, f''' + size_t j; + for (j = 0; j < ptr->subitem_lens[i]; j++) + {{ + free_{free_func} (ptr->items[i][j]); + ptr->items[i][j] = NULL; + }} + free (ptr->items[i]); + ptr->items[i] = NULL; + ''', indent=2) else: - c_file.append(f" free_{free_func} (ptr->items[i]);\n") - c_file.append(" ptr->items[i] = NULL;\n") + emit(c_file, f''' + free_{free_func} (ptr->items[i]); + ptr->items[i] = NULL; + ''', indent=2) - c_file.append(""" - } -""") + emit(c_file, ''' + } + ''', indent=1) if obj.doublearray: - c_file.append(" free (ptr->subitem_lens);\n") - c_file.append(" ptr->subitem_lens = NULL;\n") + emit(c_file, ''' + free (ptr->subitem_lens); + ptr->subitem_lens = NULL; + ''', indent=1) c_typ = helpers.obtain_pointer(obj.name, obj.subtypobj, prefix) if c_typ != "": if obj.subobj is not None: c_typ = c_typ + "_element" - c_file.append(f" free_{c_typ} (ptr->items);\n") - c_file.append(" ptr->items = NULL;\n") + emit(c_file, f''' + free_{c_typ} (ptr->items); + ptr->items = NULL; + ''', indent=1) return else: - c_file.append(""" - free (ptr->items); - ptr->items = NULL; -""") + emit(c_file, ''' + free (ptr->items); + ptr->items = NULL; + ''', indent=1) - c_file.append("""\n - free (ptr); -} -""") + emit(c_file, ''' + + free (ptr); + } + ''', indent=1) def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): c_typ = helpers.get_prefixed_pointer(obj.name, obj.subtyp, prefix) or \ @@ -1680,113 +1878,144 @@ def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): return typename = helpers.get_top_array_type_name(obj.name, prefix) - c_file.append(f"yajl_gen_status gen_{typename} (yajl_gen g, const {typename} *ptr, const struct parser_context *ctx," + """ - parser_error *err) -{ - yajl_gen_status stat; - size_t i; + emit(c_file, f''' + yajl_gen_status gen_{typename} (yajl_gen g, const {typename} *ptr, const struct parser_context *ctx, + parser_error *err) + {{ + yajl_gen_status stat; + size_t i; - if (ptr == NULL) - return yajl_gen_status_ok; - *err = NULL; -""") + if (ptr == NULL) + return yajl_gen_status_ok; + *err = NULL; + ''', indent=0) if obj.subtypobj or obj.subtyp == 'object': - c_file.append("""\n - stat = yajl_gen_array_open ((yajl_gen) g); - if (stat != yajl_gen_status_ok) - GEN_SET_ERROR_AND_RETURN (stat, err); - for (i = 0; i < ptr->len; i++) - { -""") + emit(c_file, ''' + + stat = yajl_gen_array_open ((yajl_gen) g); + if (stat != yajl_gen_status_ok) + GEN_SET_ERROR_AND_RETURN (stat, err); + for (i = 0; i < ptr->len; i++) + { + ''', indent=1) if obj.subtypname: subtypename = obj.subtypname else: subtypename = helpers.get_name_substr(obj.name, prefix) - c_file.append(' {\n') + emit(c_file, ''' + { + ''', indent=1) if obj.doublearray: - c_file.append(' stat = yajl_gen_array_open ((yajl_gen) g);\n') + emit(c_file, f''' + stat = yajl_gen_array_open ((yajl_gen) g); + ''', indent=3) check_gen_status(c_file, indent=3) - c_file.append(" size_t j;\n") - c_file.append(' for (j = 0; j < ptr->subitem_lens[i]; j++)\n') - c_file.append(' {\n') - c_file.append(f' stat = gen_{subtypename} (g, ptr->items[i][j], ctx, err);\n') - c_file.append(" if (stat != yajl_gen_status_ok)\n") - c_file.append(" GEN_SET_ERROR_AND_RETURN (stat, err);\n") - c_file.append(' }\n') - c_file.append(' stat = yajl_gen_array_close ((yajl_gen) g);\n') + emit(c_file, f''' + size_t j; + 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) + GEN_SET_ERROR_AND_RETURN (stat, err); + }} + stat = yajl_gen_array_close ((yajl_gen) g); + ''', indent=3) else: - c_file.append(f' stat = gen_{subtypename} (g, ptr->items[i], ctx, err);\n') + emit(c_file, f''' + stat = gen_{subtypename} (g, ptr->items[i], ctx, err); + ''', indent=3) check_gen_status(c_file, indent=3) - c_file.append("""\n - } - } - stat = yajl_gen_array_close ((yajl_gen) g); -""") + emit(c_file, f''' + + }} + }} + stat = yajl_gen_array_close ((yajl_gen) g); + ''', indent=2) elif obj.subtyp == 'byte': - c_file.append(' {\n') - c_file.append(' const char *str = NULL;\n') + emit(c_file, ''' + { + const char *str = NULL; + ''', indent=1) if obj.doublearray: - c_file.append(' stat = yajl_gen_array_open ((yajl_gen) g);\n') + emit(c_file, ''' + stat = yajl_gen_array_open ((yajl_gen) g); + ''', indent=3) check_gen_status(c_file, indent=3) - c_file.append(" {\n") - c_file.append(" size_t i;\n") - c_file.append(" for (i = 0; i < ptr->len; i++)\n") - c_file.append(" {\n") - c_file.append(" if (ptr->items[i] != NULL)\n") - c_file.append(" str = (const char *)ptr->items[i];\n") - c_file.append(" else ()\n") - c_file.append(" str = "";\n") - c_file.append(' stat = yajl_gen_string ((yajl_gen) g, \ - (const unsigned char *)str, strlen(str));\n') - c_file.append(" }\n") - c_file.append(" }\n") - c_file.append(' stat = yajl_gen_array_close ((yajl_gen) g);\n') + emit(c_file, ''' + { + 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 = yajl_gen_array_close ((yajl_gen) g); + ''', indent=3) else: - c_file.append(" if (ptr != NULL && ptr->items != NULL)\n") - c_file.append(" {\n") - c_file.append(" str = (const char *)ptr->items;\n") - c_file.append(" }\n") - c_file.append(' stat = yajl_gen_string ((yajl_gen) g, \ - (const unsigned char *)str, ptr->len);\n') - c_file.append(' }\n') + emit(c_file, ''' + if (ptr != NULL && ptr->items != NULL) + { + str = (const char *)ptr->items; + } + stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)str, ptr->len); + ''', indent=2) + emit(c_file, ''' + } + ''', indent=1) else: - c_file.append("""\n - stat = yajl_gen_array_open ((yajl_gen) g); - if (stat != yajl_gen_status_ok) - GEN_SET_ERROR_AND_RETURN (stat, err); - for (i = 0; i < ptr->len; i++) - { -""") - c_file.append(' {\n') + emit(c_file, ''' + + stat = yajl_gen_array_open ((yajl_gen) g); + if (stat != yajl_gen_status_ok) + GEN_SET_ERROR_AND_RETURN (stat, err); + for (i = 0; i < ptr->len; i++) + { + ''', indent=1) + emit(c_file, ''' + { + ''', indent=2) if obj.doublearray: - c_file.append(' stat = yajl_gen_array_open ((yajl_gen) g);\n') + emit(c_file, ''' + stat = yajl_gen_array_open ((yajl_gen) g); + ''', indent=3) check_gen_status(c_file, indent=3) - c_file.append(" size_t j;\n") - c_file.append(' for (j = 0; j < ptr->subitem_lens[i]; j++)\n') - c_file.append(' {\n') + emit(c_file, ''' + size_t j; + for (j = 0; j < ptr->subitem_lens[i]; j++) + { + ''', indent=3) json_value_generator(c_file, 4, "ptr->items[i][j]", 'g', 'ctx', obj.subtyp) - c_file.append(' }\n') - c_file.append(' stat = yajl_gen_array_close ((yajl_gen) g);\n') + emit(c_file, ''' + } + stat = yajl_gen_array_close ((yajl_gen) g); + ''', indent=3) else: json_value_generator(c_file, 3, "ptr->items[i]", 'g', 'ctx', obj.subtyp) - c_file.append("""\n - } - } - stat = yajl_gen_array_close ((yajl_gen) g); -""") + emit(c_file, f''' + + }} + }} + stat = yajl_gen_array_close ((yajl_gen) g); + ''', indent=2) + + emit(c_file, ''' - c_file.append("""\n if (ptr->len > 0 && !(ctx->options & OPT_GEN_SIMPLIFY)) yajl_gen_config (g, yajl_gen_beautify, 1); if (stat != yajl_gen_status_ok) GEN_SET_ERROR_AND_RETURN (stat, err); - return yajl_gen_status_ok; -} -""") + ''', indent=1) + c_file.append(" return yajl_gen_status_ok;\n") + c_file.append("}\n") + c_file.append("\n") def get_c_epilog_for_array(c_file, prefix, typ, obj): typename = helpers.get_top_array_type_name(obj.name, prefix) @@ -1809,135 +2038,147 @@ def get_c_epilog(c_file, prefix, typ, obj): typename = helpers.get_top_array_type_name(obj.name, prefix) get_c_epilog_for_array(c_file, prefix, typ, obj) - c_file.append(f"\n{typename} *\n{typename}_parse_file (const char *filename, const struct parser_context *ctx, parser_error *err)" -"\n{" - f"\n{typename} *ptr = NULL;" + - """size_t filesize; - __auto_free char *content = NULL; - - if (filename == NULL || err == NULL) - return NULL; - - *err = NULL; - content = read_file (filename, &filesize); - if (content == NULL) - { - if (asprintf (err, "cannot read the file: %s", filename) < 0) - *err = strdup ("error allocating memory"); - return NULL; - }""" + - f"ptr = {typename}_parse_data (content, ctx, err);" + - """return ptr; -} -""") - - c_file.append( -f"{typename} * \n" + -f"{typename}_parse_file_stream (FILE *stream, const struct parser_context *ctx, parser_error *err)\n{{" + - f"{typename} *ptr = NULL;"+ - """\nsize_t filesize; - __auto_free char *content = NULL; - - if (stream == NULL || err == NULL) - return NULL; - - *err = NULL; - content = fread_file (stream, &filesize); - if (content == NULL) - { - *err = strdup ("cannot read the file"); - return NULL; - }\n""" + - f"ptr = {typename}_parse_data (content, ctx, err);" + - """return ptr; -} -""") - - c_file.append(""" -define_cleaner_function (yajl_val, yajl_tree_free) -""" + -f"\n {typename} * " + -f"{typename}_parse_data (const char *jsondata, const struct parser_context *ctx, parser_error *err)\n {{ \n" + - f" {typename} *ptr = NULL;" + - """__auto_cleanup(yajl_tree_free) yajl_val tree = NULL; - char errbuf[1024]; - struct parser_context tmp_ctx = { 0 }; - - if (jsondata == NULL || err == NULL) - return NULL; - - *err = NULL; - if (ctx == NULL) - ctx = (const struct parser_context *)(&tmp_ctx); - - tree = yajl_tree_parse (jsondata, errbuf, sizeof (errbuf)); - if (tree == NULL) - { - if (asprintf (err, "cannot parse the data: %s", errbuf) < 0) - *err = strdup ("error allocating memory"); - return NULL; - }\n""" + - f"ptr = make_{typename} (tree, ctx, err);" + - "return ptr; \n}\n" -) - - c_file.append("""\nstatic void\ncleanup_yajl_gen (yajl_gen g) -{ - if (!g) - return; - yajl_gen_clear (g); - yajl_gen_free (g); -} - -define_cleaner_function (yajl_gen, cleanup_yajl_gen) - -""") - - c_file.append("\n char * \n" + -f"{typename}_generate_json (const {typename} *ptr, const struct parser_context *ctx, parser_error *err)" + -"""{ - __auto_cleanup(cleanup_yajl_gen) yajl_gen g = NULL; - struct parser_context tmp_ctx = { 0 }; - const unsigned char *gen_buf = NULL; - char *json_buf = NULL; - size_t gen_len = 0; - - if (ptr == NULL || err == NULL) - return NULL; - - *err = NULL; - if (ctx == NULL) - ctx = (const struct parser_context *)(&tmp_ctx); - - if (!json_gen_init(&g, ctx)) - { - *err = strdup ("Json_gen init failed"); - return json_buf; - } \n -""" + - f"if (yajl_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); - if (gen_buf == NULL) - { - *err = strdup ("Error to get generated json"); - return json_buf; - } - - json_buf = calloc (1, gen_len + 1); - if (json_buf == NULL) - { - *err = strdup ("Cannot allocate memory"); - return json_buf; - } - (void) memcpy (json_buf, gen_buf, gen_len); - json_buf[gen_len] = '\\0'; - - return json_buf; -} -""") + emit(c_file, f''' + + {typename} * + {typename}_parse_file (const char *filename, const struct parser_context *ctx, parser_error *err) + {{ + {typename} *ptr = NULL; + size_t filesize; + __auto_free char *content = NULL; + + if (filename == NULL || err == NULL) + return NULL; + + *err = NULL; + content = read_file (filename, &filesize); + if (content == NULL) + {{ + if (asprintf (err, "cannot read the file: %s", filename) < 0) + *err = strdup ("error allocating memory"); + return NULL; + }} + ptr = {typename}_parse_data (content, ctx, err); + return ptr; + }} + ''', indent=0) + + emit(c_file, f''' + {typename} * + {typename}_parse_file_stream (FILE *stream, const struct parser_context *ctx, parser_error *err) + {{ + {typename} *ptr = NULL; + size_t filesize; + __auto_free char *content = NULL; + + if (stream == NULL || err == NULL) + return NULL; + + *err = NULL; + content = fread_file (stream, &filesize); + if (content == NULL) + {{ + *err = strdup ("cannot read the file"); + return NULL; + }} + ptr = {typename}_parse_data (content, ctx, err); + return ptr; + }} + ''', indent=0) + + emit(c_file, f''' + + define_cleaner_function (yajl_val, yajl_tree_free) + + {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; + char errbuf[1024]; + struct parser_context tmp_ctx = {{ 0 }}; + + if (jsondata == NULL || err == NULL) + return NULL; + + *err = NULL; + if (ctx == NULL) + ctx = (const struct parser_context *)(&tmp_ctx); + + tree = yajl_tree_parse (jsondata, errbuf, sizeof (errbuf)); + if (tree == NULL) + {{ + if (asprintf (err, "cannot parse the data: %s", errbuf) < 0) + *err = strdup ("error allocating memory"); + return NULL; + }} + ptr = make_{typename} (tree, ctx, err); + return ptr; + }} + ''', 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) + + 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; + struct parser_context tmp_ctx = {{ 0 }}; + const unsigned char *gen_buf = NULL; + char *json_buf = NULL; + size_t gen_len = 0; + + if (ptr == NULL || err == NULL) + return NULL; + + *err = NULL; + if (ctx == NULL) + ctx = (const struct parser_context *)(&tmp_ctx); + + if (!json_gen_init(&g, ctx)) + {{ + *err = strdup ("Json_gen init failed"); + return json_buf; + }} + + if (yajl_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); + if (gen_buf == NULL) + {{ + *err = strdup ("Error to get generated json"); + return json_buf; + }} + + json_buf = calloc (1, gen_len + 1); + if (json_buf == NULL) + {{ + *err = strdup ("Cannot allocate memory"); + return json_buf; + }} + (void) memcpy (json_buf, gen_buf, gen_len); + json_buf[gen_len] = '\\0'; + + return json_buf; + }} + ''', indent=0) From 4d2f6e585f12d9af5763f9a93194c6d7febfbf54 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 15:01:36 +0100 Subject: [PATCH 18/51] source: add do_read_value() helper to reduce boilerplate Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 106 +++++++++++++++++------------------------ 1 file changed, 43 insertions(+), 63 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index f32aea8a..a05d85d6 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -104,6 +104,29 @@ def check_gen_status(c_file, indent=0): c_file.append(f"{prefix} GEN_SET_ERROR_AND_RETURN (stat, err);\n") +def do_read_value(c_file, src_expr, dest_expr, typ, origname, obj_typename, indent=1): + """Wrap read_val_generator in a do-while(0) block. + + Args: + c_file: Output file list + src_expr: Source expression (e.g., 'get_val (tree, "name", yajl_t_string)') + dest_expr: Destination expression (e.g., 'ret->field') + typ: Field type + origname: Original field name from schema + obj_typename: Object type name + indent: Number of 4-space indentation levels + """ + emit(c_file, f''' + do + {{ + ''', indent=indent) + read_val_generator(c_file, indent + 1, src_expr, dest_expr, typ, origname, obj_typename) + emit(c_file, f''' + }} + while (0); + ''', indent=indent) + + def append_c_code(obj, c_file, prefix): """ Description: append c language code to file @@ -326,60 +349,20 @@ def parse_obj_type(obj, c_file, prefix, obj_typename): History: 2019-06-17 """ if obj.typ == 'string': - emit(c_file, f''' - do - {{ - ''', indent=1) - read_val_generator(c_file, 2, f'get_val (tree, "{obj.origname}", yajl_t_string)', \ - f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename) - emit(c_file, ''' - } - while (0); - ''', indent=1) + do_read_value(c_file, f'get_val (tree, "{obj.origname}", yajl_t_string)', + f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename, indent=1) elif helpers.judge_data_type(obj.typ): - emit(c_file, f''' - do - {{ - ''', indent=1) - read_val_generator(c_file, 2, f'get_val (tree, "{obj.origname}", yajl_t_number)', \ - f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename) - emit(c_file, ''' - } - while (0); - ''', indent=1) + do_read_value(c_file, f'get_val (tree, "{obj.origname}", yajl_t_number)', + f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename, indent=1) elif helpers.judge_data_pointer_type(obj.typ): - emit(c_file, f''' - do - {{ - ''', indent=1) - read_val_generator(c_file, 2, f'get_val (tree, "{obj.origname}", yajl_t_number)', \ - f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename) - emit(c_file, ''' - } - while (0); - ''', indent=1) + do_read_value(c_file, f'get_val (tree, "{obj.origname}", yajl_t_number)', + f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename, indent=1) if obj.typ == 'boolean': - emit(c_file, f''' - do - {{ - ''', indent=1) - read_val_generator(c_file, 2, f'get_val (tree, "{obj.origname}", yajl_t_true)', \ - f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename) - emit(c_file, ''' - } - while (0); - ''', indent=1) + do_read_value(c_file, f'get_val (tree, "{obj.origname}", yajl_t_true)', + f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename, indent=1) if obj.typ == 'booleanPointer': - emit(c_file, f''' - do - {{ - ''', indent=1) - read_val_generator(c_file, 2, f'get_val (tree, "{obj.origname}", yajl_t_true)', \ - f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename) - emit(c_file, ''' - } - while (0); - ''', indent=1) + do_read_value(c_file, f'get_val (tree, "{obj.origname}", yajl_t_true)', + f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename, indent=1) elif obj.typ == 'object' or obj.typ == 'mapStringObject': if obj.subtypname is not None: typename = obj.subtypname @@ -531,11 +514,9 @@ def parse_json_to_c(obj, c_file, prefix): if obj.typ == 'object' or (obj.typ == 'array' and obj.subtypobj): parse_obj_arr_obj(obj, c_file, prefix, obj_typename) - emit(c_file, ''' - return move_ptr (ret); - } - - ''', indent=1) + c_file.append(" return move_ptr (ret);\n") + c_file.append("}\n") + c_file.append("\n") def get_map_string_obj(obj, c_file, prefix): @@ -1304,11 +1285,9 @@ def make_clone(obj, c_file, prefix): else: raise Exception("Unimplemented type for clone: %s" % i.typ) - emit(c_file, ''' - return move_ptr (ret); - } - - ''', indent=1) + c_file.append(" return move_ptr (ret);\n") + c_file.append("}\n") + c_file.append("\n") def json_value_generator(c_file, level, src, dst, ptx, typ): @@ -1756,10 +1735,11 @@ def get_c_epilog_for_array_make_parse(c_file, prefix, typ, obj): emit(c_file, ''' - } - return move_ptr(ptr); - } + } ''', indent=1) + c_file.append(" return move_ptr(ptr);\n") + c_file.append("}\n") + c_file.append("\n") def get_c_epilog_for_array_make_free(c_file, prefix, typ, obj): c_typ = helpers.get_prefixed_pointer(obj.name, obj.subtyp, prefix) or \ From 7a626565afadb6acd64bcd98db289ab72fafc96a Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 18:30:31 +0000 Subject: [PATCH 19/51] source: add emit_asprintf_error() helper to reduce boilerplate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce emit_asprintf_error() helper function to emit the common pattern of asprintf with a strdup fallback for error handling. This pattern appears multiple times throughout the codebase. Apply the helper to the required field error check in parse_obj_arr_obj(). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index a05d85d6..4b829232 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -127,6 +127,22 @@ def do_read_value(c_file, src_expr, dest_expr, typ, origname, obj_typename, inde ''', indent=indent) +def emit_asprintf_error(c_file, err_var, format_str, format_args, indent=0): + """Emit asprintf error with strdup fallback. + + Args: + c_file: List to append code lines to + err_var: Error variable (e.g., 'err' or '&new_error') + format_str: Format string for asprintf + format_args: Arguments for format string + indent: Number of 4-space indentation levels + """ + emit(c_file, f''' + if (asprintf ({err_var}, "{format_str}", {format_args}) < 0) + *err = strdup ("error allocating memory"); + ''', indent=indent) + + def append_c_code(obj, c_file, prefix): """ Description: append c language code to file @@ -415,10 +431,11 @@ def parse_obj_arr_obj(obj, c_file, prefix, obj_typename): emit(c_file, f''' if (ret->{i.fixname} == NULL) {{ - if (asprintf (err, "Required field '%s' not present", "{i.origname}") < 0) - *err = strdup ("error allocating memory"); + ''', indent=1) + emit_asprintf_error(c_file, 'err', "Required field '%s' not present", f'"{i.origname}"', indent=2) + emit(c_file, ''' return NULL; - }} + } ''', indent=1) if obj.typ == 'object' and obj.children is not None: From 05ee4de991dc1d89922a6900f0904d37ba947077 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 18:32:17 +0000 Subject: [PATCH 20/51] source: add emit_value_error() helper for error wrapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce emit_value_error() helper function to generate the common pattern of wrapping error messages with additional context about which key failed to parse. This pattern appears in multiple places when parsing map types. Apply the helper to parse_obj_type() and read_val_generator(). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 49 +++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 4b829232..1eb71032 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -143,6 +143,27 @@ def emit_asprintf_error(c_file, err_var, format_str, format_args, indent=0): ''', indent=indent) +def emit_value_error(c_file, keyname, indent=0): + """Emit value error handling with error message wrapping. + + Generates code to wrap an existing error message with additional context + about which key failed to parse. + + Args: + c_file: List to append code lines to + keyname: The key name to include in the error message + indent: Number of 4-space indentation levels + """ + emit(c_file, f''' + char *new_error = NULL; + if (asprintf (&new_error, "Value error for key '{keyname}': %s", *err ? *err : "null") < 0) + new_error = strdup ("error allocating memory"); + free (*err); + *err = new_error; + return NULL; + ''', indent=indent) + + def append_c_code(obj, c_file, prefix): """ Description: append c language code to file @@ -401,15 +422,12 @@ def parse_obj_type(obj, c_file, prefix, obj_typename): ret->{obj.fixname} = make_{helpers.make_basic_map_name(obj.typ)} (tmp, ctx, err); if (ret->{obj.fixname} == NULL) {{ - char *new_error = NULL; - if (asprintf (&new_error, "Value error for key '{obj.origname}': %s", *err ? *err : "null") < 0) - new_error = strdup ("error allocating memory"); - free (*err); - *err = new_error; - return NULL; - }} - }} - }} + ''', indent=1) + emit_value_error(c_file, obj.origname, indent=4) + emit(c_file, ''' + } + } + } while (0); ''', indent=1) @@ -963,14 +981,11 @@ def read_val_generator(c_file, level, src, dest, typ, keyname, obj_typename): {dest} = make_{helpers.make_basic_map_name(typ)} (val, ctx, err); if ({dest} == NULL) {{ - char *new_error = NULL; - if (asprintf (&new_error, "Value error for key '{keyname}': %s", *err ? *err : "null") < 0) - new_error = strdup ("error allocating memory"); - free (*err); - *err = new_error; - return NULL; - }} - }} + ''', indent=level) + emit_value_error(c_file, keyname, indent=level + 2) + emit(c_file, ''' + } + } ''', indent=level) elif typ == 'string': emit(c_file, f''' From 3366b93ad66e17063f326e7b8a6353d39f859e53 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 18:34:01 +0000 Subject: [PATCH 21/51] source: add emit_invalid_type_check() helper for YAJL validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce emit_invalid_type_check() helper function to emit the common pattern of YAJL type validation with error return. This pattern appears when parsing numeric values. Apply the helper to read_val_generator() for numeric type parsing, reducing code duplication in the type validation logic. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 45 +++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 1eb71032..c862e950 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -164,6 +164,23 @@ 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. + + Args: + c_file: List to append code lines to + yajl_check: YAJL type check macro (e.g., 'YAJL_IS_NUMBER') + indent: Number of 4-space indentation levels + """ + emit(c_file, f''' + if (! {yajl_check} (val)) + {{ + *err = strdup ("invalid type"); + return NULL; + }} + ''', indent=indent) + + def append_c_code(obj, c_file, prefix): """ Description: append c language code to file @@ -1003,36 +1020,20 @@ def read_val_generator(c_file, level, src, dest, typ, keyname, obj_typename): yajl_val val = {src}; if (val != NULL) {{ + int invalid; ''', indent=level) + emit_invalid_type_check(c_file, 'YAJL_IS_NUMBER', indent=level + 1) if typ.startswith("uint") or \ (typ.startswith("int") and typ != "integer") or typ == "double": emit(c_file, f''' - int invalid; - if (! YAJL_IS_NUMBER (val)) - {{ - *err = strdup ("invalid type"); - return NULL; - }} invalid = common_safe_{typ} (YAJL_GET_NUMBER (val), &{dest}); ''', indent=level + 1) elif typ == "integer": emit(c_file, f''' - int invalid; - if (! YAJL_IS_NUMBER (val)) - {{ - *err = strdup ("invalid type"); - return NULL; - }} invalid = common_safe_int (YAJL_GET_NUMBER (val), (int *)&{dest}); ''', indent=level + 1) elif typ == "UID" or typ == "GID": emit(c_file, f''' - int invalid; - if (! YAJL_IS_NUMBER (val)) - {{ - *err = strdup ("invalid type"); - return NULL; - }} invalid = common_safe_uint (YAJL_GET_NUMBER (val), (unsigned int *)&{dest}); ''', indent=level + 1) emit(c_file, f''' @@ -1062,11 +1063,9 @@ def read_val_generator(c_file, level, src, dest, typ, keyname, obj_typename): if ({dest} == NULL) return NULL; int invalid; - if (! YAJL_IS_NUMBER (val)) - {{ - *err = strdup ("invalid type"); - return NULL; - }} + ''', indent=level) + emit_invalid_type_check(c_file, 'YAJL_IS_NUMBER', indent=level + 1) + emit(c_file, f''' invalid = common_safe_{num_type} (YAJL_GET_NUMBER (val), {dest}); if (invalid) {{ From aa9271ce615c5ff527b38e5c7be35437244e8490 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 18:35:19 +0000 Subject: [PATCH 22/51] source: add YAJL generation helper functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce helper functions for common YAJL JSON generation operations: - emit_gen_key(): Generate a JSON object key - emit_gen_map_open/close(): Generate map boundaries - emit_gen_array_open/close(): Generate array boundaries - emit_beautify_off/on(): Toggle JSON beautification These helpers will be used in subsequent commits to simplify the JSON generation code. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 92 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index c862e950..eff0a823 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -181,6 +181,98 @@ def emit_invalid_type_check(c_file, yajl_check='YAJL_IS_NUMBER', indent=0): ''', indent=indent) +# YAJL generation helpers + +def emit_gen_key(c_file, key, indent=0): + """Emit yajl_gen_string for an object key. + + Args: + c_file: List to append code lines to + key: Key string to generate + indent: Number of 4-space indentation levels + """ + key_len = len(key) + emit(c_file, f''' + stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{key}"), {key_len} /* strlen ("{key}") */); + ''', indent=indent) + + +def emit_gen_map_open(c_file, indent=0): + """Emit yajl_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); + ''', indent=indent) + + +def emit_gen_map_close(c_file, indent=0): + """Emit yajl_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); + ''', indent=indent) + + +def emit_gen_array_open(c_file, indent=0): + """Emit yajl_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); + ''', indent=indent) + + +def emit_gen_array_close(c_file, indent=0): + """Emit yajl_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); + ''', indent=indent) + + +def emit_beautify_off(c_file, condition='!len', indent=0): + """Emit yajl_gen_beautify disable. + + Args: + c_file: List to append code lines to + condition: Condition for disabling beautify + indent: Number of 4-space indentation levels + """ + emit(c_file, f''' + if ({condition} && !(ctx->options & OPT_GEN_SIMPLIFY)) + yajl_gen_config (g, yajl_gen_beautify, 0); + ''', indent=indent) + + +def emit_beautify_on(c_file, condition='!len', indent=0): + """Emit yajl_gen_beautify enable. + + Args: + c_file: List to append code lines to + condition: Condition for enabling beautify + indent: Number of 4-space indentation levels + """ + emit(c_file, f''' + if ({condition} && !(ctx->options & OPT_GEN_SIMPLIFY)) + yajl_gen_config (g, yajl_gen_beautify, 1); + ''', indent=indent) + + def append_c_code(obj, c_file, prefix): """ Description: append c language code to file From 7f5fcb56f5223bad7cbc809faef82abd29194fde Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 18:36:16 +0000 Subject: [PATCH 23/51] source: apply YAJL helpers to get_map_string_obj() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new YAJL generation helpers in get_map_string_obj() to simplify the code and improve readability: - emit_beautify_off/on() for beautify config - emit_gen_map_open/close() for map boundaries 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index eff0a823..528c190c 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -678,15 +678,13 @@ def get_map_string_obj(obj, c_file, prefix): else: childname = helpers.get_prefixed_name(child.name, prefix) - emit(c_file, f''' + emit(c_file, ''' size_t len = 0, i; if (ptr != NULL) len = ptr->len; - if (!len && !(ctx->options & OPT_GEN_SIMPLIFY)) - yajl_gen_config (g, yajl_gen_beautify, 0); - stat = yajl_gen_map_open ((yajl_gen) g); ''', indent=1) - + emit_beautify_off(c_file, '!len', indent=1) + emit_gen_map_open(c_file, indent=1) check_gen_status(c_file, indent=1) emit(c_file, f''' @@ -709,15 +707,10 @@ def get_map_string_obj(obj, c_file, prefix): emit(c_file, ''' } } - stat = yajl_gen_map_close ((yajl_gen) g); ''', indent=2) - + emit_gen_map_close(c_file, indent=1) check_gen_status(c_file, indent=1) - - emit(c_file, ''' - if (!len && !(ctx->options & OPT_GEN_SIMPLIFY)) - yajl_gen_config (g, yajl_gen_beautify, 1); - ''', indent=1) + emit_beautify_on(c_file, '!len', indent=1) def get_obj_arr_obj_array(obj, c_file, prefix): if obj.subtypobj or obj.subtyp == 'object': From a2cc69ba3d8cc92f7c865443a88ed5272e2f5414 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 18:38:06 +0000 Subject: [PATCH 24/51] source: apply YAJL helpers to get_obj_arr_obj_array() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the YAJL generation helpers throughout get_obj_arr_obj_array() to simplify array generation code: - emit_gen_key() for generating key strings - emit_gen_array_open/close() for array boundaries - emit_beautify_off/on() for beautify config This reduces duplication and improves readability. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 55 +++++++++++++----------------------------- 1 file changed, 17 insertions(+), 38 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 528c190c..7b2bfdaa 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -714,7 +714,6 @@ def get_map_string_obj(obj, c_file, prefix): def get_obj_arr_obj_array(obj, c_file, prefix): if obj.subtypobj or obj.subtyp == 'object': - l = len(obj.origname) if obj.subtypname: typename = obj.subtypname else: @@ -724,19 +723,16 @@ def get_obj_arr_obj_array(obj, c_file, prefix): if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) {{ size_t len = 0, i; - stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {int(l)} /* strlen ("{obj.origname}") */); ''', indent=1) - + emit_gen_key(c_file, obj.origname, indent=2) check_gen_status(c_file, indent=2) emit(c_file, f''' if (ptr != NULL && ptr->{obj.fixname} != NULL) len = ptr->{obj.fixname}_len; - if (!len && !(ctx->options & OPT_GEN_SIMPLIFY)) - yajl_gen_config (g, yajl_gen_beautify, 0); - stat = yajl_gen_array_open ((yajl_gen) g); ''', 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(c_file, ''' @@ -745,9 +741,7 @@ def get_obj_arr_obj_array(obj, c_file, prefix): ''', indent=2) if obj.doublearray: - emit(c_file, ''' - stat = yajl_gen_array_open ((yajl_gen) g); - ''', indent=3) + emit_gen_array_open(c_file, indent=3) check_gen_status(c_file, indent=3) emit(c_file, f''' size_t j; @@ -758,8 +752,8 @@ def get_obj_arr_obj_array(obj, c_file, prefix): check_gen_status(c_file, indent=4) emit(c_file, ''' } - stat = yajl_gen_array_close ((yajl_gen) g); ''', indent=4) + emit_gen_array_close(c_file, indent=3) else: emit(c_file, f''' stat = gen_{typename} (g, ptr->{obj.fixname}[i], ctx, err); @@ -768,32 +762,26 @@ def get_obj_arr_obj_array(obj, c_file, prefix): emit(c_file, ''' } - stat = yajl_gen_array_close ((yajl_gen) g); - if (!len && !(ctx->options & OPT_GEN_SIMPLIFY)) - yajl_gen_config (g, yajl_gen_beautify, 1); ''', indent=2) - + emit_gen_array_close(c_file, indent=2) + emit_beautify_on(c_file, '!len', indent=2) check_gen_status(c_file, indent=2) emit(c_file, ''' } ''', indent=1) elif obj.subtyp == 'byte': - l = len(obj.origname) emit(c_file, f''' if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL && ptr->{obj.fixname}_len)) {{ const char *str = ""; size_t len = 0; - stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */); ''', indent=1) - + emit_gen_key(c_file, obj.origname, indent=2) check_gen_status(c_file, indent=2) if obj.doublearray: - emit(c_file, ''' - stat = yajl_gen_array_open ((yajl_gen) g); - ''', indent=3) + emit_gen_array_open(c_file, indent=3) check_gen_status(c_file, indent=3) emit(c_file, f''' {{ @@ -807,8 +795,8 @@ def get_obj_arr_obj_array(obj, c_file, prefix): stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)str, strlen(str)); }} }} - stat = yajl_gen_array_close ((yajl_gen) g); ''', indent=2) + emit_gen_array_close(c_file, indent=2) else: emit(c_file, f''' if (ptr != NULL && ptr->{obj.fixname} != NULL) @@ -825,24 +813,20 @@ def get_obj_arr_obj_array(obj, c_file, prefix): } ''', indent=1) else: - l = len(obj.origname) emit(c_file, f''' if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) {{ size_t len = 0, i; - stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */); ''', indent=1) - + emit_gen_key(c_file, obj.origname, indent=2) check_gen_status(c_file, indent=2) emit(c_file, f''' if (ptr != NULL && ptr->{obj.fixname} != NULL) len = ptr->{obj.fixname}_len; - if (!len && !(ctx->options & OPT_GEN_SIMPLIFY)) - yajl_gen_config (g, yajl_gen_beautify, 0); - stat = yajl_gen_array_open ((yajl_gen) g); ''', 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(c_file, ''' @@ -852,9 +836,7 @@ def get_obj_arr_obj_array(obj, c_file, prefix): if obj.doublearray: typename = helpers.get_map_c_types(obj.subtyp) - emit(c_file, ''' - stat = yajl_gen_array_open ((yajl_gen) g); - ''', indent=3) + emit_gen_array_open(c_file, indent=3) check_gen_status(c_file, indent=3) emit(c_file, f''' size_t j; @@ -864,21 +846,18 @@ def get_obj_arr_obj_array(obj, c_file, prefix): json_value_generator(c_file, 4, f"ptr->{obj.fixname}[i][j]", 'g', 'ctx', obj.subtyp) emit(c_file, ''' } - stat = yajl_gen_array_close ((yajl_gen) g); ''', indent=4) + emit_gen_array_close(c_file, indent=3) else: json_value_generator(c_file, 3, f"ptr->{obj.fixname}[i]", 'g', 'ctx', obj.subtyp) emit(c_file, ''' } - stat = yajl_gen_array_close ((yajl_gen) g); ''', indent=2) - + emit_gen_array_close(c_file, indent=2) check_gen_status(c_file, indent=2) - + emit_beautify_on(c_file, '!len', indent=2) emit(c_file, ''' - if (!len && !(ctx->options & OPT_GEN_SIMPLIFY)) - yajl_gen_config (g, yajl_gen_beautify, 1); } ''', indent=2) From 6dcd043008736160a51ea1700d81274027b07fe0 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 18:39:10 +0000 Subject: [PATCH 25/51] source: apply YAJL helpers to get_c_json() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the YAJL generation helpers in get_c_json(): - emit_gen_map_open/close() for map boundaries - emit_beautify_off/on() for beautify config 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 7b2bfdaa..b91cfd02 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -1013,14 +1013,9 @@ def get_c_json(obj, c_file, prefix): elif obj.typ == 'object' or (obj.typ == 'array' and obj.subtypobj): nodes = obj.children if obj.typ == 'object' else obj.subtypobj if nodes is None: - emit(c_file, ''' - if (!(ctx->options & OPT_GEN_SIMPLIFY)) - yajl_gen_config (g, yajl_gen_beautify, 0); - ''', indent=1) + emit_beautify_off(c_file, 'true', indent=1) - emit(c_file, ''' - stat = yajl_gen_map_open ((yajl_gen) g); - ''', indent=1) + emit_gen_map_open(c_file, indent=1) check_gen_status(c_file, indent=1) for i in nodes or []: get_obj_arr_obj(i, c_file, prefix) @@ -1034,15 +1029,10 @@ def get_c_json(obj, c_file, prefix): GEN_SET_ERROR_AND_RETURN (stat, err); } ''', indent=1) - emit(c_file, ''' - stat = yajl_gen_map_close ((yajl_gen) g); - ''', indent=1) + emit_gen_map_close(c_file, indent=1) check_gen_status(c_file, indent=1) if nodes is None: - emit(c_file, ''' - if (!(ctx->options & OPT_GEN_SIMPLIFY)) - yajl_gen_config (g, yajl_gen_beautify, 1); - ''', indent=1) + emit_beautify_on(c_file, 'true', indent=1) c_file.append(" return yajl_gen_status_ok;\n") c_file.append("}\n") c_file.append("\n") From 717d0356f3ec8621f773932b6982e579bd260d8b Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 18:40:33 +0000 Subject: [PATCH 26/51] source: apply YAJL helpers to get_c_epilog_for_array_make_gen() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the YAJL generation helpers in get_c_epilog_for_array_make_gen(): - emit_gen_array_open/close() for array boundaries - Replace manual status checks with check_gen_status() where possible This simplifies the array epilog generation code. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 48 ++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index b91cfd02..3a4827eb 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -1956,11 +1956,10 @@ def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): ''', indent=0) if obj.subtypobj or obj.subtyp == 'object': + c_file.append('\n') + emit_gen_array_open(c_file, indent=1) + check_gen_status(c_file, indent=1) emit(c_file, ''' - - stat = yajl_gen_array_open ((yajl_gen) g); - if (stat != yajl_gen_status_ok) - GEN_SET_ERROR_AND_RETURN (stat, err); for (i = 0; i < ptr->len; i++) { ''', indent=1) @@ -1973,9 +1972,7 @@ def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): { ''', indent=1) if obj.doublearray: - emit(c_file, f''' - stat = yajl_gen_array_open ((yajl_gen) g); - ''', indent=3) + emit_gen_array_open(c_file, indent=3) check_gen_status(c_file, indent=3) emit(c_file, f''' size_t j; @@ -1985,28 +1982,26 @@ def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): if (stat != yajl_gen_status_ok) GEN_SET_ERROR_AND_RETURN (stat, err); }} - stat = yajl_gen_array_close ((yajl_gen) g); ''', indent=3) + emit_gen_array_close(c_file, indent=3) else: emit(c_file, f''' stat = gen_{subtypename} (g, ptr->items[i], ctx, err); ''', indent=3) check_gen_status(c_file, indent=3) - emit(c_file, f''' + emit(c_file, ''' - }} - }} - stat = yajl_gen_array_close ((yajl_gen) g); + } + } ''', indent=2) + emit_gen_array_close(c_file, indent=1) elif obj.subtyp == 'byte': emit(c_file, ''' { const char *str = NULL; ''', indent=1) if obj.doublearray: - emit(c_file, ''' - stat = yajl_gen_array_open ((yajl_gen) g); - ''', indent=3) + emit_gen_array_open(c_file, indent=3) check_gen_status(c_file, indent=3) emit(c_file, ''' { @@ -2020,8 +2015,8 @@ def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)str, strlen(str)); } } - stat = yajl_gen_array_close ((yajl_gen) g); ''', indent=3) + emit_gen_array_close(c_file, indent=3) else: emit(c_file, ''' if (ptr != NULL && ptr->items != NULL) @@ -2034,11 +2029,10 @@ def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): } ''', indent=1) else: + c_file.append('\n') + emit_gen_array_open(c_file, indent=1) + check_gen_status(c_file, indent=1) emit(c_file, ''' - - stat = yajl_gen_array_open ((yajl_gen) g); - if (stat != yajl_gen_status_ok) - GEN_SET_ERROR_AND_RETURN (stat, err); for (i = 0; i < ptr->len; i++) { ''', indent=1) @@ -2046,9 +2040,7 @@ def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): { ''', indent=2) if obj.doublearray: - emit(c_file, ''' - stat = yajl_gen_array_open ((yajl_gen) g); - ''', indent=3) + emit_gen_array_open(c_file, indent=3) check_gen_status(c_file, indent=3) emit(c_file, ''' size_t j; @@ -2058,17 +2050,17 @@ def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): json_value_generator(c_file, 4, "ptr->items[i][j]", 'g', 'ctx', obj.subtyp) emit(c_file, ''' } - stat = yajl_gen_array_close ((yajl_gen) g); ''', indent=3) + emit_gen_array_close(c_file, indent=3) else: json_value_generator(c_file, 3, "ptr->items[i]", 'g', 'ctx', obj.subtyp) - emit(c_file, f''' + emit(c_file, ''' - }} - }} - stat = yajl_gen_array_close ((yajl_gen) g); + } + } ''', indent=2) + emit_gen_array_close(c_file, indent=1) emit(c_file, ''' From a23b40c233584c9af90c80dae2bd743091591660 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 18:42:22 +0000 Subject: [PATCH 27/51] source: apply emit_gen_key() helper to get_obj_arr_obj() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use emit_gen_key() throughout get_obj_arr_obj() to simplify key generation code. This eliminates manual yajl_gen_string calls and length calculations for object keys. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 3a4827eb..680da08a 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -868,13 +868,12 @@ def get_obj_arr_obj(obj, c_file, prefix): History: 2019-06-17 """ if obj.typ == 'string': - l = len(obj.origname) emit(c_file, f''' if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) {{ char *str = ""; - stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */); ''', indent=1) + emit_gen_key(c_file, obj.origname, indent=2) check_gen_status(c_file, indent=2) emit(c_file, f''' if (ptr != NULL && ptr->{obj.fixname} != NULL) @@ -891,13 +890,12 @@ def get_obj_arr_obj(obj, c_file, prefix): numtyp = 'long long unsigned int' else: numtyp = 'long long int' - l = len(obj.origname) emit(c_file, f''' if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname}_present)) {{ {numtyp} num = 0; - stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */); ''', indent=1) + emit_gen_key(c_file, obj.origname, indent=2) check_gen_status(c_file, indent=2) emit(c_file, f''' if (ptr != NULL && ptr->{obj.fixname}) @@ -911,13 +909,12 @@ def get_obj_arr_obj(obj, c_file, prefix): numtyp = helpers.obtain_data_pointer_type(obj.typ) if numtyp == "": return - l = len(obj.origname) emit(c_file, f''' if ((ptr != NULL && ptr->{obj.fixname} != NULL)) {{ {helpers.get_map_c_types(numtyp)} num = 0; - stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */); ''', indent=1) + emit_gen_key(c_file, obj.origname, indent=2) check_gen_status(c_file, indent=2) emit(c_file, f''' if (ptr != NULL && ptr->{obj.fixname} != NULL) @@ -930,13 +927,12 @@ def get_obj_arr_obj(obj, c_file, prefix): } ''', indent=1) elif obj.typ == 'boolean': - l = len(obj.origname) emit(c_file, f''' if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname}_present)) {{ bool b = false; - stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */); ''', indent=1) + emit_gen_key(c_file, obj.origname, indent=2) check_gen_status(c_file, indent=2) emit(c_file, f''' if (ptr != NULL && ptr->{obj.fixname}) @@ -948,7 +944,6 @@ def get_obj_arr_obj(obj, c_file, prefix): } ''', indent=1) elif obj.typ == 'object' or obj.typ == 'mapStringObject': - l = len(obj.origname) if obj.subtypname: typename = obj.subtypname else: @@ -956,8 +951,8 @@ def get_obj_arr_obj(obj, c_file, prefix): emit(c_file, f''' if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) {{ - stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.origname}"), {l} /* strlen ("{obj.origname}") */); ''', indent=1) + emit_gen_key(c_file, obj.origname, indent=2) check_gen_status(c_file, indent=2) emit(c_file, f''' stat = gen_{typename} (g, ptr != NULL ? ptr->{obj.fixname} : NULL, ctx, err); @@ -969,12 +964,11 @@ def get_obj_arr_obj(obj, c_file, prefix): elif obj.typ == 'array': get_obj_arr_obj_array(obj, c_file, prefix) elif helpers.valid_basic_map_name(obj.typ): - l = len(obj.origname) emit(c_file, f''' if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) {{ - stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)("{obj.fixname}"), {l} /* strlen ("{obj.fixname}") */); ''', indent=1) + emit_gen_key(c_file, obj.fixname, indent=2) check_gen_status(c_file, indent=2) emit(c_file, f''' stat = gen_{helpers.make_basic_map_name(obj.typ)} (g, ptr ? ptr->{obj.fixname} : NULL, ctx, err); From 6ae8e2d615a7df12fb63766061780abc157813ed Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 18:43:39 +0000 Subject: [PATCH 28/51] source: consolidate numeric type conversion with helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add get_numeric_conversion_info() helper that returns the conversion function name and destination cast for numeric types. This eliminates the if-elif chain for selecting the appropriate common_safe_* function. Apply the helper to read_val_generator() to simplify numeric value parsing code. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 680da08a..edd17124 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -181,6 +181,24 @@ def emit_invalid_type_check(c_file, yajl_check='YAJL_IS_NUMBER', indent=0): ''', indent=indent) +def get_numeric_conversion_info(typ): + """Get conversion function and cast for a numeric type. + + Args: + typ: The type string (e.g., 'integer', 'uint64', 'UID') + + Returns: + Tuple of (conversion_function, dest_cast) or None if not a numeric type + """ + if typ.startswith("uint") or (typ.startswith("int") and typ != "integer") or typ == "double": + return f'common_safe_{typ}', '&' + elif typ == "integer": + return 'common_safe_int', '(int *)&' + elif typ == "UID" or typ == "GID": + return 'common_safe_uint', '(unsigned int *)&' + return None + + # YAJL generation helpers def emit_gen_key(c_file, key, indent=0): @@ -1064,6 +1082,7 @@ def read_val_generator(c_file, level, src, dest, typ, keyname, obj_typename): }} ''', indent=level) elif helpers.judge_data_type(typ): + conv_func, dest_cast = get_numeric_conversion_info(typ) emit(c_file, f''' yajl_val val = {src}; if (val != NULL) @@ -1071,20 +1090,8 @@ def read_val_generator(c_file, level, src, dest, typ, keyname, obj_typename): int invalid; ''', indent=level) emit_invalid_type_check(c_file, 'YAJL_IS_NUMBER', indent=level + 1) - if typ.startswith("uint") or \ - (typ.startswith("int") and typ != "integer") or typ == "double": - emit(c_file, f''' - invalid = common_safe_{typ} (YAJL_GET_NUMBER (val), &{dest}); - ''', indent=level + 1) - elif typ == "integer": - emit(c_file, f''' - invalid = common_safe_int (YAJL_GET_NUMBER (val), (int *)&{dest}); - ''', indent=level + 1) - elif typ == "UID" or typ == "GID": - emit(c_file, f''' - invalid = common_safe_uint (YAJL_GET_NUMBER (val), (unsigned int *)&{dest}); - ''', indent=level + 1) emit(c_file, f''' + invalid = {conv_func} (YAJL_GET_NUMBER (val), {dest_cast}{dest}); if (invalid) {{ if (asprintf (err, "Invalid value '%s' with type '{typ}' for key '{keyname}': %s", YAJL_GET_NUMBER (val), strerror (-invalid)) < 0) From 858f199534ce173c0b917c7f9bd6019915b4610a Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 19:53:53 +0000 Subject: [PATCH 29/51] source: remove dead code in make_clone() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the `if False:` block that was never executed. The code path in the else branch is now the only path. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index edd17124..8fe4af65 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -1269,21 +1269,12 @@ def make_clone(obj, c_file, prefix): ret->{i.fixname}[i] = src->{i.fixname}[i]; ''', indent=3) elif i.subtyp == 'object': - subnode_name = i.subtypname or helpers.get_prefixed_name(i.name, prefix) - if False: # i.subtypname is not None: + typename = helpers.get_prefixed_name(i.name, prefix) + if i.subtypname is not None: typename = i.subtypname + maybe_element = "_element" if i.subtypname is None else "" + if i.doublearray: emit(c_file, f''' - ret->{i.fixname}[i] = clone_{typename} (src->{i.fixname}[i]); - if (ret->{i.fixname}[i] == NULL) - return NULL; - ''', indent=3) - else: - typename = helpers.get_prefixed_name(i.name, prefix) - if i.subtypname is not None: - typename = i.subtypname - maybe_element = "_element" if i.subtypname is None else "" - if i.doublearray: - emit(c_file, f''' ret->{i.fixname}_item_lens[i] = src->{i.fixname}_item_lens[i]; ret->{i.fixname}[i] = calloc (ret->{i.fixname}_item_lens[i] + 1, sizeof (**ret->{i.fixname}[i])); if (ret->{i.fixname}[i] == NULL) @@ -1294,13 +1285,13 @@ def make_clone(obj, c_file, prefix): if (ret->{i.fixname}[i][j] == NULL) return NULL; }} - ''', indent=3) - else: - emit(c_file, f''' + ''', indent=3) + else: + emit(c_file, f''' ret->{i.fixname}[i] = clone_{typename}{maybe_element} (src->{i.fixname}[i]); if (ret->{i.fixname}[i] == NULL) return NULL; - ''', indent=3) + ''', indent=3) elif i.subtyp == 'string': if i.doublearray: From c174b0670c2c2d7876ec55b9c045d2d6828b6c54 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 19:54:29 +0000 Subject: [PATCH 30/51] source: remove superfluous else after return MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unnecessary `else:` keywords after `return` statements. When a branch ends with `return`, the `else:` is redundant. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 8fe4af65..ec199325 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -1197,8 +1197,7 @@ def make_clone(obj, c_file, prefix): if obj.typ == 'array': if objs is None: return - else: - typename = helpers.get_name_substr(obj.name, prefix) + typename = helpers.get_name_substr(obj.name, prefix) emit(c_file, f''' {typename} * @@ -1526,8 +1525,7 @@ def make_c_free (obj, c_file, prefix): if obj.typ == 'array': if objs is None: return - else: - typename = helpers.get_name_substr(obj.name, prefix) + typename = helpers.get_name_substr(obj.name, prefix) emit(c_file, f''' void @@ -1914,13 +1912,10 @@ def get_c_epilog_for_array_make_free(c_file, prefix, typ, obj): ptr->items = NULL; ''', indent=1) return - else: - emit(c_file, ''' - free (ptr->items); - ptr->items = NULL; - ''', indent=1) emit(c_file, ''' + free (ptr->items); + ptr->items = NULL; free (ptr); } From c3686db3b38f96869f5bd841d6835e47e39afbeb Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 20:07:11 +0000 Subject: [PATCH 31/51] source: add TypeHandler classes for type-specific C code generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a class-based approach for generating type-specific C code. Each type (string, boolean, numeric, etc.) has its own TypeHandler class with methods for: - emit_parse: parsing JSON to C struct - emit_generate: serializing C struct to JSON - emit_free: freeing memory - emit_clone: deep-copying - emit_read_value: reading a JSON value - emit_json_value: writing a JSON value The dispatch functions (read_val_generator, json_value_generator, get_obj_arr_obj) now use get_type_handler() to delegate to the appropriate handler. This makes the code more extensible and maintainable - adding a new type only requires adding a new handler class. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 652 ++++++++++++++++++++++++++--------------- 1 file changed, 423 insertions(+), 229 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index ec199325..3cedbce7 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -291,6 +291,415 @@ def emit_beautify_on(c_file, condition='!len', indent=0): ''', indent=indent) +# Type handler classes for C code generation +# Each type has methods for: parsing JSON, generating JSON, freeing memory, cloning + +class TypeHandler: + """Base class for type-specific C code generation.""" + + def emit_parse(self, c_file, obj, prefix, obj_typename, indent=1): + """Generate C code to parse this type from JSON.""" + pass + + def emit_generate(self, c_file, obj, prefix, indent=1): + """Generate C code to serialize this type to JSON.""" + pass + + def emit_free(self, c_file, obj, prefix, indent=1): + """Generate C code to free memory for this type.""" + pass + + def emit_clone(self, c_file, obj, prefix, indent=1): + """Generate C code to clone/deep-copy this type.""" + pass + + def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): + """Generate C code to read a value of this type.""" + pass + + def emit_json_value(self, c_file, src, dst, ptx, level=1): + """Generate C code to write a JSON value of this type.""" + pass + + +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)', + f"ret->{obj.fixname}", 'string', obj.origname, obj_typename, 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)) + {{ + char *str = ""; + ''', indent=indent) + emit_gen_key(c_file, obj.origname, indent=indent + 1) + check_gen_status(c_file, indent=indent + 1) + emit(c_file, f''' + if (ptr != NULL && ptr->{obj.fixname} != NULL) + str = ptr->{obj.fixname}; + ''', indent=indent + 1) + self.emit_json_value(c_file, "str", 'g', 'ctx', level=indent + 1) + emit(c_file, ''' + } + ''', 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) + + def emit_clone(self, c_file, obj, prefix, indent=1): + emit(c_file, f''' + if (src->{obj.fixname} != NULL) + {{ + ret->{obj.fixname} = strdup (src->{obj.fixname}); + if (ret->{obj.fixname} == NULL) + return NULL; + }} + ''', indent=indent) + + def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): + emit(c_file, f''' + yajl_val val = {src}; + if (val != NULL) + {{ + char *str = YAJL_GET_STRING (val); + {dest} = strdup (str ? str : ""); + if ({dest} == NULL) + return NULL; + }} + ''', indent=level) + + 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) + GEN_SET_ERROR_AND_RETURN (stat, err); + ''', indent=level) + + +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)', + f"ret->{obj.fixname}", 'boolean', obj.origname, obj_typename, 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}_present)) + {{ + bool b = false; + ''', indent=indent) + emit_gen_key(c_file, obj.origname, indent=indent + 1) + check_gen_status(c_file, indent=indent + 1) + emit(c_file, f''' + if (ptr != NULL && ptr->{obj.fixname}) + b = ptr->{obj.fixname}; + + ''', indent=indent + 1) + self.emit_json_value(c_file, "b", 'g', 'ctx', level=indent + 1) + emit(c_file, ''' + } + ''', indent=indent) + + def emit_free(self, c_file, obj, prefix, indent=1): + pass # Boolean doesn't need freeing + + def emit_clone(self, c_file, obj, prefix, indent=1): + emit(c_file, f''' + ret->{obj.fixname} = src->{obj.fixname}; + ret->{obj.fixname}_present = src->{obj.fixname}_present; + ''', indent=indent) + + def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): + emit(c_file, f''' + yajl_val val = {src}; + if (val != NULL) + {{ + {dest} = YAJL_IS_TRUE(val); + ''', indent=level) + if '[' not in dest: + emit(c_file, f''' + {dest}_present = 1; + }} + else + {{ + val = {src.replace('yajl_t_true', 'yajl_t_false')}; + if (val != NULL) + {{ + {dest} = 0; + {dest}_present = 1; + }} + }} + ''', indent=level + 1) + else: + emit(c_file, f''' + }} + ''', 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) + GEN_SET_ERROR_AND_RETURN (stat, err); + ''', indent=level) + + +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)', + f"ret->{obj.fixname}", 'booleanPointer', obj.origname, obj_typename, 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) + + 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) + + def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): + emit(c_file, f''' + yajl_val val = {src}; + if (val != NULL) + {{ + {dest} = calloc (1, sizeof (bool)); + if ({dest} == NULL) + return NULL; + *({dest}) = YAJL_IS_TRUE(val); + }} + else + {{ + val = get_val (tree, "{keyname}", yajl_t_false); + if (val != NULL) + {{ + {dest} = calloc (1, sizeof (bool)); + if ({dest} == NULL) + return NULL; + *({dest}) = YAJL_IS_TRUE(val); + }} + }} + ''', indent=level) + + +class NumericType(TypeHandler): + """Handler for numeric types (integer, double, int8-int64, uint8-uint64, UID, GID).""" + + def __init__(self, typ): + self.typ = typ + + def _get_conversion_info(self): + """Get conversion function and cast for this numeric type.""" + typ = self.typ + if typ.startswith("uint") or (typ.startswith("int") and typ != "integer") or typ == "double": + return f'common_safe_{typ}', '&' + elif typ == "integer": + return 'common_safe_int', '(int *)&' + elif typ == "UID" or typ == "GID": + return 'common_safe_uint', '(unsigned int *)&' + return None, None + + def _get_c_numtype(self): + """Get C type for JSON generation.""" + if self.typ == 'double': + return 'double' + elif self.typ.startswith("uint") or self.typ == 'GID' or self.typ == 'UID': + return 'long long unsigned int' + 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)', + f"ret->{obj.fixname}", self.typ, obj.origname, obj_typename, indent=indent) + + def emit_generate(self, c_file, obj, prefix, indent=1): + numtyp = self._get_c_numtype() + emit(c_file, f''' + if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname}_present)) + {{ + {numtyp} num = 0; + ''', indent=indent) + emit_gen_key(c_file, obj.origname, indent=indent + 1) + check_gen_status(c_file, indent=indent + 1) + emit(c_file, f''' + if (ptr != NULL && ptr->{obj.fixname}) + num = ({numtyp})ptr->{obj.fixname}; + ''', indent=indent + 1) + self.emit_json_value(c_file, "num", 'g', 'ctx', level=indent + 1) + emit(c_file, ''' + } + ''', indent=indent) + + def emit_free(self, c_file, obj, prefix, indent=1): + pass # Numeric types don't need freeing + + def emit_clone(self, c_file, obj, prefix, indent=1): + emit(c_file, f''' + ret->{obj.fixname} = src->{obj.fixname}; + ret->{obj.fixname}_present = src->{obj.fixname}_present; + ''', indent=indent) + + 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}; + if (val != NULL) + {{ + int invalid; + ''', indent=level) + emit_invalid_type_check(c_file, 'YAJL_IS_NUMBER', indent=level + 1) + emit(c_file, f''' + invalid = {conv_func} (YAJL_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) + *err = strdup ("error allocating memory"); + return NULL; + }} + ''', indent=level + 1) + if '[' not in dest: + emit(c_file, f''' + {dest}_present = 1; + ''', indent=level + 1) + emit(c_file, f''' + }} + ''', indent=level) + + 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}); + ''', indent=level) + elif self.typ.startswith("uint") or self.typ == 'GID' or self.typ == 'UID': + emit(c_file, f''' + stat = map_uint ({dst}, {src}); + ''', indent=level) + else: + emit(c_file, f''' + stat = map_int ({dst}, {src}); + ''', indent=level) + emit(c_file, f''' + if (stat != yajl_gen_status_ok) + GEN_SET_ERROR_AND_RETURN (stat, err); + ''', indent=level) + + +class NumericPointerType(TypeHandler): + """Handler for numeric pointer types (integerPointer, int8Pointer, etc.).""" + + def __init__(self, typ): + self.typ = typ + self.base_typ = helpers.obtain_data_pointer_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)', + f"ret->{obj.fixname}", self.typ, obj.origname, obj_typename, indent=indent) + + def emit_generate(self, c_file, obj, prefix, indent=1): + if self.base_typ == "": + return + emit(c_file, f''' + if ((ptr != NULL && ptr->{obj.fixname} != NULL)) + {{ + {helpers.get_map_c_types(self.base_typ)} num = 0; + ''', indent=indent) + emit_gen_key(c_file, obj.origname, indent=indent + 1) + check_gen_status(c_file, indent=indent + 1) + emit(c_file, f''' + if (ptr != NULL && ptr->{obj.fixname} != NULL) + {{ + num = ({helpers.get_map_c_types(self.base_typ)})*(ptr->{obj.fixname}); + }} + ''', indent=indent + 1) + NumericType(self.base_typ).emit_json_value(c_file, "num", 'g', 'ctx', level=indent + 1) + emit(c_file, ''' + } + ''', 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) + + 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) + + 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}; + if (val != NULL) + {{ + {dest} = calloc (1, sizeof ({helpers.get_map_c_types(self.base_typ)})); + if ({dest} == NULL) + return NULL; + int invalid; + ''', indent=level) + emit_invalid_type_check(c_file, 'YAJL_IS_NUMBER', indent=level + 1) + emit(c_file, f''' + invalid = common_safe_{self.base_typ} (YAJL_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) + *err = strdup ("error allocating memory"); + return NULL; + }} + }} + ''', indent=level) + + +# Type handler registry +_TYPE_HANDLERS = { + 'string': StringType(), + 'boolean': BooleanType(), + 'booleanPointer': BooleanPointerType(), +} + + +def get_type_handler(typ): + """Get the appropriate TypeHandler for a given type. + + Args: + typ: The type string (e.g., 'string', 'boolean', 'uint64') + + Returns: + TypeHandler instance or None if no handler exists + """ + if typ in _TYPE_HANDLERS: + return _TYPE_HANDLERS[typ] + if helpers.judge_data_type(typ): + return NumericType(typ) + if helpers.judge_data_pointer_type(typ): + return NumericPointerType(typ) + return None + + def append_c_code(obj, c_file, prefix): """ Description: append c language code to file @@ -880,87 +1289,10 @@ def get_obj_arr_obj_array(obj, c_file, prefix): ''', indent=2) def get_obj_arr_obj(obj, c_file, prefix): - """ - Description: c language generate object or array object - Interface: None - History: 2019-06-17 - """ - if obj.typ == 'string': - emit(c_file, f''' - if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) - {{ - char *str = ""; - ''', indent=1) - emit_gen_key(c_file, obj.origname, indent=2) - check_gen_status(c_file, indent=2) - emit(c_file, f''' - if (ptr != NULL && ptr->{obj.fixname} != NULL) - str = ptr->{obj.fixname}; - ''', indent=2) - json_value_generator(c_file, 2, "str", 'g', 'ctx', obj.typ) - emit(c_file, ''' - } - ''', indent=1) - elif helpers.judge_data_type(obj.typ): - if obj.typ == 'double': - numtyp = 'double' - elif obj.typ.startswith("uint") or obj.typ == 'GID' or obj.typ == 'UID': - numtyp = 'long long unsigned int' - else: - numtyp = 'long long int' - emit(c_file, f''' - if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname}_present)) - {{ - {numtyp} num = 0; - ''', indent=1) - emit_gen_key(c_file, obj.origname, indent=2) - check_gen_status(c_file, indent=2) - emit(c_file, f''' - if (ptr != NULL && ptr->{obj.fixname}) - num = ({numtyp})ptr->{obj.fixname}; - ''', indent=2) - json_value_generator(c_file, 2, "num", 'g', 'ctx', obj.typ) - emit(c_file, ''' - } - ''', indent=1) - elif helpers.judge_data_pointer_type(obj.typ): - numtyp = helpers.obtain_data_pointer_type(obj.typ) - if numtyp == "": - return - emit(c_file, f''' - if ((ptr != NULL && ptr->{obj.fixname} != NULL)) - {{ - {helpers.get_map_c_types(numtyp)} num = 0; - ''', indent=1) - emit_gen_key(c_file, obj.origname, indent=2) - check_gen_status(c_file, indent=2) - emit(c_file, f''' - if (ptr != NULL && ptr->{obj.fixname} != NULL) - {{ - num = ({helpers.get_map_c_types(numtyp)})*(ptr->{obj.fixname}); - }} - ''', indent=2) - json_value_generator(c_file, 2, "num", 'g', 'ctx', numtyp) - emit(c_file, ''' - } - ''', indent=1) - elif obj.typ == 'boolean': - emit(c_file, f''' - if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname}_present)) - {{ - bool b = false; - ''', indent=1) - emit_gen_key(c_file, obj.origname, indent=2) - check_gen_status(c_file, indent=2) - emit(c_file, f''' - if (ptr != NULL && ptr->{obj.fixname}) - b = ptr->{obj.fixname}; - - ''', indent=2) - json_value_generator(c_file, 2, "b", 'g', 'ctx', obj.typ) - emit(c_file, ''' - } - ''', indent=1) + """Generate C code to serialize a struct field to JSON.""" + handler = get_type_handler(obj.typ) + if handler: + handler.emit_generate(c_file, obj, prefix, indent=1) elif obj.typ == 'object' or obj.typ == 'mapStringObject': if obj.subtypname: typename = obj.subtypname @@ -1051,12 +1383,11 @@ def get_c_json(obj, c_file, prefix): def read_val_generator(c_file, level, src, dest, typ, keyname, obj_typename): - """ - Description: read value generateor - Interface: None - History: 2019-06-17 - """ - if helpers.valid_basic_map_name(typ): + """Generate C code to read a JSON value into a C variable.""" + handler = get_type_handler(typ) + if handler: + handler.emit_read_value(c_file, src, dest, keyname, obj_typename, level=level) + elif helpers.valid_basic_map_name(typ): emit(c_file, f''' yajl_val val = {src}; if (val != NULL) @@ -1070,113 +1401,6 @@ def read_val_generator(c_file, level, src, dest, typ, keyname, obj_typename): } } ''', indent=level) - elif typ == 'string': - emit(c_file, f''' - yajl_val val = {src}; - if (val != NULL) - {{ - char *str = YAJL_GET_STRING (val); - {dest} = strdup (str ? str : ""); - if ({dest} == NULL) - return NULL; - }} - ''', indent=level) - elif helpers.judge_data_type(typ): - conv_func, dest_cast = get_numeric_conversion_info(typ) - emit(c_file, f''' - yajl_val val = {src}; - if (val != NULL) - {{ - int invalid; - ''', indent=level) - emit_invalid_type_check(c_file, 'YAJL_IS_NUMBER', indent=level + 1) - emit(c_file, f''' - invalid = {conv_func} (YAJL_GET_NUMBER (val), {dest_cast}{dest}); - if (invalid) - {{ - if (asprintf (err, "Invalid value '%s' with type '{typ}' for key '{keyname}': %s", YAJL_GET_NUMBER (val), strerror (-invalid)) < 0) - *err = strdup ("error allocating memory"); - return NULL; - }} - ''', indent=level + 1) - if '[' not in dest: - emit(c_file, f''' - {dest}_present = 1; - ''', indent=level + 1) - emit(c_file, f''' - }} - ''', indent=level) - elif helpers.judge_data_pointer_type(typ): - num_type = helpers.obtain_data_pointer_type(typ) - if num_type == "": - return - emit(c_file, f''' - yajl_val val = {src}; - if (val != NULL) - {{ - {dest} = calloc (1, sizeof ({helpers.get_map_c_types(num_type)})); - if ({dest} == NULL) - return NULL; - int invalid; - ''', indent=level) - emit_invalid_type_check(c_file, 'YAJL_IS_NUMBER', indent=level + 1) - emit(c_file, f''' - invalid = common_safe_{num_type} (YAJL_GET_NUMBER (val), {dest}); - if (invalid) - {{ - if (asprintf (err, "Invalid value '%s' with type '{typ}' for key '{keyname}': %s", YAJL_GET_NUMBER (val), strerror (-invalid)) < 0) - *err = strdup ("error allocating memory"); - return NULL; - }} - }} - ''', indent=level) - elif typ == 'boolean': - emit(c_file, f''' - yajl_val val = {src}; - if (val != NULL) - {{ - {dest} = YAJL_IS_TRUE(val); - ''', indent=level) - if '[' not in dest: - emit(c_file, f''' - {dest}_present = 1; - }} - else - {{ - val = {src.replace('yajl_t_true', 'yajl_t_false')}; - if (val != NULL) - {{ - {dest} = 0; - {dest}_present = 1; - }} - }} - ''', indent=level + 1) - else: - emit(c_file, f''' - }} - ''', indent=level) - elif typ == 'booleanPointer': - emit(c_file, f''' - yajl_val val = {src}; - if (val != NULL) - {{ - {dest} = calloc (1, sizeof (bool)); - if ({dest} == NULL) - return NULL; - *({dest}) = YAJL_IS_TRUE(val); - }} - else - {{ - val = get_val (tree, "{keyname}", yajl_t_false); - if (val != NULL) - {{ - {dest} = calloc (1, sizeof (bool)); - if ({dest} == NULL) - return NULL; - *({dest}) = YAJL_IS_TRUE(val); - }} - }} - ''', indent=level) def make_clone(obj, c_file, prefix): @@ -1367,46 +1591,16 @@ def make_clone(obj, c_file, prefix): def json_value_generator(c_file, level, src, dst, ptx, typ): - """ - Description: json value generateor - Interface: None - History: 2019-06-17 - """ - if helpers.valid_basic_map_name(typ): + """Generate C code to write a JSON value.""" + handler = get_type_handler(typ) + if handler: + handler.emit_json_value(c_file, src, dst, ptx, level=level) + elif helpers.valid_basic_map_name(typ): emit(c_file, f''' stat = gen_{helpers.make_basic_map_name(typ)} ({dst}, {src}, {ptx}, err); if (stat != yajl_gen_status_ok) GEN_SET_ERROR_AND_RETURN (stat, err); ''', indent=level) - elif typ == 'string': - emit(c_file, f''' - stat = yajl_gen_string ((yajl_gen){dst}, (const unsigned char *)({src}), strlen ({src})); - if (stat != yajl_gen_status_ok) - GEN_SET_ERROR_AND_RETURN (stat, err); - ''', indent=level) - elif helpers.judge_data_type(typ): - if typ == 'double': - emit(c_file, f''' - stat = yajl_gen_double ((yajl_gen){dst}, {src}); - ''', indent=level) - elif typ.startswith("uint") or typ == 'GID' or typ == 'UID': - emit(c_file, f''' - stat = map_uint ({dst}, {src}); - ''', indent=level) - else: - emit(c_file, f''' - stat = map_int ({dst}, {src}); - ''', indent=level) - emit(c_file, f''' - if (stat != yajl_gen_status_ok) - GEN_SET_ERROR_AND_RETURN (stat, err); - ''', indent=level) - elif typ == 'boolean': - emit(c_file, f''' - stat = yajl_gen_bool ((yajl_gen){dst}, (int)({src})); - if (stat != yajl_gen_status_ok) - GEN_SET_ERROR_AND_RETURN (stat, err); - ''', indent=level) def make_c_array_free (i, c_file, prefix): if helpers.valid_basic_map_name(i.subtyp): From 2fc8679021c12bb6cf34f591fa8b9202ea72ab4b Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 20:08:42 +0000 Subject: [PATCH 32/51] source: use TypeHandler.emit_parse() in parse_obj_type() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the type-specific if/elif chain in parse_obj_type() with a call to the appropriate TypeHandler's emit_parse() method. Also remove the now-unused get_numeric_conversion_info() function since its logic is now part of the NumericType class. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 42 ++++-------------------------------------- 1 file changed, 4 insertions(+), 38 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 3cedbce7..7c23e91b 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -181,24 +181,6 @@ def emit_invalid_type_check(c_file, yajl_check='YAJL_IS_NUMBER', indent=0): ''', indent=indent) -def get_numeric_conversion_info(typ): - """Get conversion function and cast for a numeric type. - - Args: - typ: The type string (e.g., 'integer', 'uint64', 'UID') - - Returns: - Tuple of (conversion_function, dest_cast) or None if not a numeric type - """ - if typ.startswith("uint") or (typ.startswith("int") and typ != "integer") or typ == "double": - return f'common_safe_{typ}', '&' - elif typ == "integer": - return 'common_safe_int', '(int *)&' - elif typ == "UID" or typ == "GID": - return 'common_safe_uint', '(unsigned int *)&' - return None - - # YAJL generation helpers def emit_gen_key(c_file, key, indent=0): @@ -916,26 +898,10 @@ def parse_obj_type_array(obj, c_file, prefix, obj_typename): ''', indent=1) def parse_obj_type(obj, c_file, prefix, obj_typename): - """ - Description: generate c language for parse object type - Interface: None - History: 2019-06-17 - """ - if obj.typ == 'string': - do_read_value(c_file, f'get_val (tree, "{obj.origname}", yajl_t_string)', - f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename, indent=1) - elif helpers.judge_data_type(obj.typ): - do_read_value(c_file, f'get_val (tree, "{obj.origname}", yajl_t_number)', - f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename, indent=1) - elif helpers.judge_data_pointer_type(obj.typ): - do_read_value(c_file, f'get_val (tree, "{obj.origname}", yajl_t_number)', - f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename, indent=1) - if obj.typ == 'boolean': - do_read_value(c_file, f'get_val (tree, "{obj.origname}", yajl_t_true)', - f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename, indent=1) - if obj.typ == 'booleanPointer': - do_read_value(c_file, f'get_val (tree, "{obj.origname}", yajl_t_true)', - f"ret->{obj.fixname}", obj.typ, obj.origname, obj_typename, indent=1) + """Generate C code to parse a single JSON field into a struct member.""" + handler = get_type_handler(obj.typ) + if handler: + handler.emit_parse(c_file, obj, prefix, obj_typename, indent=1) elif obj.typ == 'object' or obj.typ == 'mapStringObject': if obj.subtypname is not None: typename = obj.subtypname From 141fa5043fea31f45245670489c735499d01468c Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 20:11:10 +0000 Subject: [PATCH 33/51] source: use TypeHandler in make_c_free() and make_clone() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply the TypeHandler pattern to make_c_free() and make_clone() for simple types (string, boolean, booleanPointer, numeric types). This delegates type-specific free and clone code generation to the appropriate handler classes, reducing the complexity of the dispatch logic in these functions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 57 +++++++++++++----------------------------- 1 file changed, 18 insertions(+), 39 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 7c23e91b..aa743040 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -1403,11 +1403,9 @@ def make_clone(obj, c_file, prefix): nodes = obj.children if obj.subtypobj is None else obj.subtypobj for i in nodes or []: - if helpers.judge_data_type(i.typ) or i.typ == 'boolean': - emit(c_file, f''' - ret->{i.fixname} = src->{i.fixname}; - ret->{i.fixname}_present = src->{i.fixname}_present; - ''', indent=1) + handler = get_type_handler(i.typ) + if handler: + handler.emit_clone(c_file, i, prefix, indent=1) elif i.typ == 'object': node_name = i.subtypname or helpers.get_prefixed_name(i.name, prefix) if obj.typ != 'mapStringObject': @@ -1433,15 +1431,6 @@ def make_clone(obj, c_file, prefix): }} }} ''', indent=1) - elif i.typ == 'string': - emit(c_file, f''' - if (src->{i.fixname}) - {{ - ret->{i.fixname} = strdup (src->{i.fixname}); - if (ret->{i.fixname} == NULL) - return NULL; - }} - ''', indent=1) elif i.typ == 'array': emit(c_file, f''' if (src->{i.fixname}) @@ -1705,42 +1694,32 @@ def make_c_free (obj, c_file, prefix): childname = helpers.get_prefixed_name(child.name, prefix) c_file_map_str(c_file, child, childname) for i in objs or []: - if helpers.valid_basic_map_name(i.typ): + handler = get_type_handler(i.typ) + if handler: + handler.emit_free(c_file, i, prefix, indent=1) + elif helpers.valid_basic_map_name(i.typ): free_func = helpers.make_basic_map_name(i.typ) emit(c_file, f''' free_{free_func} (ptr->{i.fixname}); ptr->{i.fixname} = NULL; ''', indent=1) - if i.typ == 'mapStringObject': - if i.subtypname: - free_func = i.subtypname - else: - free_func = helpers.get_prefixed_name(i.name, prefix) + elif i.typ == 'mapStringObject': + free_func = i.subtypname or helpers.get_prefixed_name(i.name, prefix) emit(c_file, f''' free_{free_func} (ptr->{i.fixname}); ptr->{i.fixname} = NULL; ''', indent=1) elif i.typ == 'array': - if make_c_array_free (i, c_file, prefix): - continue - else: - typename = helpers.get_prefixed_name(i.name, prefix) - if i.typ == 'string' or i.typ == 'booleanPointer' or \ - helpers.judge_data_pointer_type(i.typ): - emit(c_file, f''' - free (ptr->{i.fixname}); + make_c_array_free(i, c_file, prefix) + elif i.typ == 'object': + typename = i.subtypname or helpers.get_prefixed_name(i.name, prefix) + emit(c_file, f''' + if (ptr->{i.fixname} != NULL) + {{ + free_{typename} (ptr->{i.fixname}); ptr->{i.fixname} = NULL; - ''', indent=1) - elif i.typ == 'object': - if i.subtypname is not None: - typename = i.subtypname - emit(c_file, f''' - if (ptr->{i.fixname} != NULL) - {{ - free_{typename} (ptr->{i.fixname}); - ptr->{i.fixname} = NULL; - }} - ''', indent=1) + }} + ''', indent=1) if obj.typ == 'object': if obj.children is not None: From 633c0b51b44a95ff42bc78d72584dc4fcad31d07 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 20:18:14 +0000 Subject: [PATCH 34/51] source: add ObjectType, MapStringObjectType, and BasicMapType handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add TypeHandler classes for: - ObjectType: handles 'object' type parsing, generation, freeing - MapStringObjectType: handles 'mapStringObject' type - BasicMapType: handles basic map types (mapStringString, mapStringInt, etc.) Update get_type_handler() to return BasicMapType for valid basic maps. Simplify dispatch functions to use these handlers: - parse_obj_type: now only handles 'array' specially - get_obj_arr_obj: now only handles 'array' specially - json_value_generator: fully delegated to handlers Note: ObjectType.emit_clone() is a no-op because object cloning requires parent context (mapStringObject vs regular object). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 217 ++++++++++++++++++++++++++++------------- 1 file changed, 151 insertions(+), 66 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index aa743040..b340a1d6 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -656,11 +656,160 @@ def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): ''', indent=level) +class ObjectType(TypeHandler): + """Handler for object type.""" + + 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); + if (ret->{obj.fixname} == NULL && *err != 0) + return NULL; + ''', indent=indent) + + 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(c_file, obj.origname, indent=indent + 1) + check_gen_status(c_file, 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) + + def emit_free(self, c_file, obj, prefix, indent=1): + typename = obj.subtypname or helpers.get_prefixed_name(obj.name, prefix) + emit(c_file, f''' + if (ptr->{obj.fixname} != NULL) + {{ + free_{typename} (ptr->{obj.fixname}); + ptr->{obj.fixname} = NULL; + }} + ''', indent=indent) + + def emit_clone(self, c_file, obj, prefix, indent=1): + # Object clone requires context (parent type) - handled in make_clone + pass + + +class MapStringObjectType(TypeHandler): + """Handler for mapStringObject type.""" + + 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); + if (ret->{obj.fixname} == NULL && *err != 0) + return NULL; + ''', indent=indent) + + 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(c_file, obj.origname, indent=indent + 1) + check_gen_status(c_file, 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) + + def emit_free(self, c_file, obj, prefix, indent=1): + free_func = obj.subtypname or helpers.get_prefixed_name(obj.name, prefix) + emit(c_file, f''' + free_{free_func} (ptr->{obj.fixname}); + ptr->{obj.fixname} = NULL; + ''', indent=indent) + + +class BasicMapType(TypeHandler): + """Handler for basic map types (mapStringString, mapStringInt, etc.).""" + + def __init__(self, typ): + self.typ = typ + self.map_name = helpers.make_basic_map_name(typ) + + 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); + if (tmp != NULL) + {{ + ret->{obj.fixname} = make_{self.map_name} (tmp, ctx, err); + if (ret->{obj.fixname} == NULL) + {{ + ''', indent=indent) + emit_value_error(c_file, obj.origname, indent=indent + 3) + emit(c_file, ''' + } + } + } + while (0); + ''', 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(c_file, obj.fixname, indent=indent + 1) + check_gen_status(c_file, 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) + + def emit_free(self, c_file, obj, prefix, indent=1): + emit(c_file, f''' + free_{self.map_name} (ptr->{obj.fixname}); + ptr->{obj.fixname} = NULL; + ''', indent=indent) + + def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): + emit(c_file, f''' + yajl_val val = {src}; + if (val != NULL) + {{ + {dest} = make_{self.map_name} (val, ctx, err); + if ({dest} == NULL) + {{ + ''', indent=level) + emit_value_error(c_file, keyname, indent=level + 2) + emit(c_file, ''' + } + } + ''', indent=level) + + 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) + GEN_SET_ERROR_AND_RETURN (stat, err); + ''', indent=level) + + # Type handler registry _TYPE_HANDLERS = { 'string': StringType(), 'boolean': BooleanType(), 'booleanPointer': BooleanPointerType(), + 'object': ObjectType(), + 'mapStringObject': MapStringObjectType(), } @@ -679,6 +828,8 @@ def get_type_handler(typ): return NumericType(typ) if helpers.judge_data_pointer_type(typ): return NumericPointerType(typ) + if helpers.valid_basic_map_name(typ): + return BasicMapType(typ) return None @@ -902,36 +1053,8 @@ def parse_obj_type(obj, c_file, prefix, obj_typename): handler = get_type_handler(obj.typ) if handler: handler.emit_parse(c_file, obj, prefix, obj_typename, indent=1) - elif obj.typ == 'object' or obj.typ == 'mapStringObject': - if obj.subtypname is not None: - typename = obj.subtypname - else: - typename = 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); - if (ret->{obj.fixname} == NULL && *err != 0) - return NULL; - ''', indent=1) elif obj.typ == 'array': parse_obj_type_array(obj, c_file, prefix, obj_typename) - elif helpers.valid_basic_map_name(obj.typ): - emit(c_file, f''' - do - {{ - yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_object); - if (tmp != NULL) - {{ - ret->{obj.fixname} = make_{helpers.make_basic_map_name(obj.typ)} (tmp, ctx, err); - if (ret->{obj.fixname} == NULL) - {{ - ''', indent=1) - emit_value_error(c_file, obj.origname, indent=4) - emit(c_file, ''' - } - } - } - while (0); - ''', indent=1) def parse_obj_arr_obj(obj, c_file, prefix, obj_typename): """ @@ -1259,40 +1382,8 @@ def get_obj_arr_obj(obj, c_file, prefix): handler = get_type_handler(obj.typ) if handler: handler.emit_generate(c_file, obj, prefix, indent=1) - elif obj.typ == 'object' or obj.typ == 'mapStringObject': - if obj.subtypname: - typename = obj.subtypname - else: - typename = 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=1) - emit_gen_key(c_file, obj.origname, indent=2) - check_gen_status(c_file, indent=2) - emit(c_file, f''' - stat = gen_{typename} (g, ptr != NULL ? ptr->{obj.fixname} : NULL, ctx, err); - ''', indent=2) - check_gen_status(c_file, indent=2) - emit(c_file, ''' - } - ''', indent=1) elif obj.typ == 'array': get_obj_arr_obj_array(obj, c_file, prefix) - elif helpers.valid_basic_map_name(obj.typ): - emit(c_file, f''' - if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) - {{ - ''', indent=1) - emit_gen_key(c_file, obj.fixname, indent=2) - check_gen_status(c_file, indent=2) - emit(c_file, f''' - stat = gen_{helpers.make_basic_map_name(obj.typ)} (g, ptr ? ptr->{obj.fixname} : NULL, ctx, err); - ''', indent=2) - check_gen_status(c_file, indent=2) - emit(c_file, ''' - } - ''', indent=1) def get_c_json(obj, c_file, prefix): @@ -1550,12 +1641,6 @@ def json_value_generator(c_file, level, src, dst, ptx, typ): handler = get_type_handler(typ) if handler: handler.emit_json_value(c_file, src, dst, ptx, level=level) - elif helpers.valid_basic_map_name(typ): - emit(c_file, f''' - stat = gen_{helpers.make_basic_map_name(typ)} ({dst}, {src}, {ptx}, err); - if (stat != yajl_gen_status_ok) - GEN_SET_ERROR_AND_RETURN (stat, err); - ''', indent=level) def make_c_array_free (i, c_file, prefix): if helpers.valid_basic_map_name(i.subtyp): From 971a65b792552fe0f1be900374d04364d8a75722 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 20:24:43 +0000 Subject: [PATCH 35/51] source: add ArrayType handler for array operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move array-specific code generation into ArrayType TypeHandler class: - emit_parse: delegates to parse_obj_type_array() - emit_generate: delegates to get_obj_arr_obj_array() - emit_free: delegates to make_c_array_free() - emit_clone: generates array clone code with support for numeric, object, and string subtypes including double arrays Remove explicit elif branches for array type in parse_obj_type(), get_obj_arr_obj(), make_c_free(), and make_clone() - these are now handled through the type handler dispatch. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 165 +++++++++++++++++++++++------------------ 1 file changed, 91 insertions(+), 74 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index b340a1d6..4492cbc6 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -803,6 +803,96 @@ def emit_json_value(self, c_file, src, dst, ptx, level=1): ''', indent=level) +class ArrayType(TypeHandler): + """Handler for array type. + + Arrays are complex and delegate to specialized functions for each operation. + """ + + def emit_parse(self, c_file, obj, prefix, obj_typename, indent=1): + # Delegate to existing function - will be defined later + parse_obj_type_array(obj, c_file, prefix, obj_typename) + + def emit_generate(self, c_file, obj, prefix, indent=1): + # Delegate to existing function - will be defined later + get_obj_arr_obj_array(obj, c_file, prefix) + + def emit_free(self, c_file, obj, prefix, indent=1): + # Delegate to existing function - will be defined later + make_c_array_free(obj, c_file, prefix) + + def emit_clone(self, c_file, obj, prefix, indent=1): + """Generate clone code for array fields.""" + emit(c_file, f''' + if (src->{obj.fixname}) + {{ + ret->{obj.fixname}_len = src->{obj.fixname}_len; + ret->{obj.fixname} = calloc (src->{obj.fixname}_len + 1, sizeof (*ret->{obj.fixname})); + if (ret->{obj.fixname} == NULL) + return NULL; + for (size_t i = 0; i < src->{obj.fixname}_len; i++) + {{ + ''', indent=indent) + + if helpers.judge_data_type(obj.subtyp) or obj.subtyp == 'boolean': + emit(c_file, f''' + ret->{obj.fixname}[i] = src->{obj.fixname}[i]; + ''', indent=indent+2) + elif obj.subtyp == 'object': + typename = helpers.get_prefixed_name(obj.name, prefix) + if obj.subtypname is not None: + typename = obj.subtypname + maybe_element = "_element" if obj.subtypname is None else "" + if obj.doublearray: + emit(c_file, f''' + ret->{obj.fixname}_item_lens[i] = src->{obj.fixname}_item_lens[i]; + ret->{obj.fixname}[i] = calloc (ret->{obj.fixname}_item_lens[i] + 1, sizeof (**ret->{obj.fixname}[i])); + if (ret->{obj.fixname}[i] == NULL) + return NULL; + for (size_t j = 0; j < src->{obj.fixname}_item_lens[i]; j++) + {{ + ret->{obj.fixname}[i][j] = clone_{typename}{maybe_element} (src->{obj.fixname}[i][j]); + if (ret->{obj.fixname}[i][j] == NULL) + return NULL; + }} + ''', indent=indent+2) + else: + emit(c_file, f''' + ret->{obj.fixname}[i] = clone_{typename}{maybe_element} (src->{obj.fixname}[i]); + if (ret->{obj.fixname}[i] == NULL) + return NULL; + ''', indent=indent+2) + elif obj.subtyp == 'string': + if obj.doublearray: + emit(c_file, f''' + ret->{obj.fixname}[i] = calloc (ret->{obj.fixname}_item_lens[i] + 1, sizeof (**ret->{obj.fixname}[i])); + if (ret->{obj.fixname}[i] == NULL) + return NULL; + for (size_t j = 0; j < src->{obj.fixname}_item_lens[i]; j++) + {{ + ret->{obj.fixname}[i][j] = strdup (src->{obj.fixname}[i][j]); + if (ret->{obj.fixname}[i][j] == NULL) + return NULL; + }} + ''', indent=indent+2) + else: + emit(c_file, f''' + if (src->{obj.fixname}[i]) + {{ + ret->{obj.fixname}[i] = strdup (src->{obj.fixname}[i]); + if (ret->{obj.fixname}[i] == NULL) + return NULL; + }} + ''', indent=indent+2) + else: + raise Exception("Unimplemented type for array clone: %s (%s)" % (obj.subtyp, obj.subtypname)) + + emit(c_file, f''' + }} + }} + ''', indent=indent+1) + + # Type handler registry _TYPE_HANDLERS = { 'string': StringType(), @@ -810,6 +900,7 @@ def emit_json_value(self, c_file, src, dst, ptx, level=1): 'booleanPointer': BooleanPointerType(), 'object': ObjectType(), 'mapStringObject': MapStringObjectType(), + 'array': ArrayType(), } @@ -1053,8 +1144,6 @@ def parse_obj_type(obj, c_file, prefix, obj_typename): handler = get_type_handler(obj.typ) if handler: handler.emit_parse(c_file, obj, prefix, obj_typename, indent=1) - elif obj.typ == 'array': - parse_obj_type_array(obj, c_file, prefix, obj_typename) def parse_obj_arr_obj(obj, c_file, prefix, obj_typename): """ @@ -1382,8 +1471,6 @@ def get_obj_arr_obj(obj, c_file, prefix): handler = get_type_handler(obj.typ) if handler: handler.emit_generate(c_file, obj, prefix, indent=1) - elif obj.typ == 'array': - get_obj_arr_obj_array(obj, c_file, prefix) def get_c_json(obj, c_file, prefix): @@ -1522,74 +1609,6 @@ def make_clone(obj, c_file, prefix): }} }} ''', indent=1) - elif i.typ == 'array': - emit(c_file, f''' - if (src->{i.fixname}) - {{ - ret->{i.fixname}_len = src->{i.fixname}_len; - ret->{i.fixname} = calloc (src->{i.fixname}_len + 1, sizeof (*ret->{i.fixname})); - if (ret->{i.fixname} == NULL) - return NULL; - for (size_t i = 0; i < src->{i.fixname}_len; i++) - {{ - ''', indent=1) - if helpers.judge_data_type(i.subtyp) or i.subtyp == 'boolean': - emit(c_file, f''' - ret->{i.fixname}[i] = src->{i.fixname}[i]; - ''', indent=3) - elif i.subtyp == 'object': - typename = helpers.get_prefixed_name(i.name, prefix) - if i.subtypname is not None: - typename = i.subtypname - maybe_element = "_element" if i.subtypname is None else "" - if i.doublearray: - emit(c_file, f''' - ret->{i.fixname}_item_lens[i] = src->{i.fixname}_item_lens[i]; - ret->{i.fixname}[i] = calloc (ret->{i.fixname}_item_lens[i] + 1, sizeof (**ret->{i.fixname}[i])); - if (ret->{i.fixname}[i] == NULL) - return NULL; - for (size_t j = 0; j < src->{i.fixname}_item_lens[i]; j++) - {{ - ret->{i.fixname}[i][j] = clone_{typename}{maybe_element} (src->{i.fixname}[i][j]); - if (ret->{i.fixname}[i][j] == NULL) - return NULL; - }} - ''', indent=3) - else: - emit(c_file, f''' - ret->{i.fixname}[i] = clone_{typename}{maybe_element} (src->{i.fixname}[i]); - if (ret->{i.fixname}[i] == NULL) - return NULL; - ''', indent=3) - - elif i.subtyp == 'string': - if i.doublearray: - emit(c_file, f''' - ret->{i.fixname}[i] = calloc (ret->{i.fixname}_item_lens[i] + 1, sizeof (**ret->{i.fixname}[i])); - if (ret->{i.fixname}[i] == NULL) - return NULL; - for (size_t j = 0; j < src->{i.fixname}_item_lens[i]; j++) - {{ - ret->{i.fixname}[i][j] = strdup (src->{i.fixname}[i][j]); - if (ret->{i.fixname}[i][j] == NULL) - return NULL; - }} - ''', indent=3) - else: - emit(c_file, f''' - if (src->{i.fixname}[i]) - {{ - ret->{i.fixname}[i] = strdup (src->{i.fixname}[i]); - if (ret->{i.fixname}[i] == NULL) - return NULL; - }} - ''', indent=3) - else: - raise Exception("Unimplemented type for array clone: %s (%s)" % (i.subtyp, i.subtypname)) - emit(c_file, f''' - }} - }} - ''', indent=2) elif i.typ == 'mapStringString': emit(c_file, f''' ret->{i.fixname} = clone_map_string_string (src->{i.fixname}); @@ -1794,8 +1813,6 @@ def make_c_free (obj, c_file, prefix): free_{free_func} (ptr->{i.fixname}); ptr->{i.fixname} = NULL; ''', indent=1) - elif i.typ == 'array': - make_c_array_free(i, c_file, prefix) elif i.typ == 'object': typename = i.subtypname or helpers.get_prefixed_name(i.name, prefix) emit(c_file, f''' From 20de7a40a97153e1cfff2e07e273499113335635 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 20:27:10 +0000 Subject: [PATCH 36/51] source: add emit_clone() to MapStringObjectType and BasicMapType MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move clone code generation for mapStringObject and basic map types (mapStringString, mapStringInt, etc.) into their TypeHandler classes: - MapStringObjectType.emit_clone: clones key-value pairs with values being complex objects - BasicMapType.emit_clone: delegates to clone_map_string_string or similar functions (note: clone functions don't use json_ prefix) Remove explicit elif branches for these types in make_clone() - now handled through the type handler dispatch. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 79 ++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 4492cbc6..4c50d075 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -732,6 +732,38 @@ def emit_free(self, c_file, obj, prefix, indent=1): ptr->{obj.fixname} = NULL; ''', indent=indent) + def emit_clone(self, c_file, obj, prefix, indent=1): + if obj.subtypname is not None: + subtypname = obj.subtypname + maybe_element = "_element" + else: + subtypname = obj.children[0].subtypname + maybe_element = "" + emit(c_file, f''' + if (src->{obj.fixname}) + {{ + ret->{obj.fixname} = calloc (1, sizeof (*ret->{obj.fixname})); + if (ret->{obj.fixname} == NULL) + return NULL; + ret->{obj.fixname}->len = src->{obj.fixname}->len; + ret->{obj.fixname}->keys = calloc (src->{obj.fixname}->len + 1, sizeof (char *)); + if (ret->{obj.fixname}->keys == NULL) + return NULL; + ret->{obj.fixname}->values = calloc (src->{obj.fixname}->len + 1, sizeof (*ret->{obj.fixname}->values)); + if (ret->{obj.fixname}->values == NULL) + return NULL; + for (size_t i = 0; i < ret->{obj.fixname}->len; i++) + {{ + ret->{obj.fixname}->keys[i] = strdup (src->{obj.fixname}->keys[i]); + if (ret->{obj.fixname}->keys[i] == NULL) + return NULL; + ret->{obj.fixname}->values[i] = clone_{subtypname}{maybe_element} (src->{obj.fixname}->values[i]); + if (ret->{obj.fixname}->values[i] == NULL) + return NULL; + }} + }} + ''', indent=indent) + class BasicMapType(TypeHandler): """Handler for basic map types (mapStringString, mapStringInt, etc.).""" @@ -802,6 +834,15 @@ def emit_json_value(self, c_file, src, dst, ptx, level=1): GEN_SET_ERROR_AND_RETURN (stat, err); ''', indent=level) + def emit_clone(self, c_file, obj, prefix, indent=1): + # Clone function doesn't use json_ prefix + clone_name = self.map_name.replace('json_', '', 1) + emit(c_file, f''' + ret->{obj.fixname} = clone_{clone_name} (src->{obj.fixname}); + if (ret->{obj.fixname} == NULL) + return NULL; + ''', indent=indent) + class ArrayType(TypeHandler): """Handler for array type. @@ -1609,44 +1650,6 @@ def make_clone(obj, c_file, prefix): }} }} ''', indent=1) - elif i.typ == 'mapStringString': - emit(c_file, f''' - ret->{i.fixname} = clone_map_string_string (src->{i.fixname}); - if (ret->{i.fixname} == NULL) - return NULL; - ''', indent=1) - elif i.typ == 'mapStringObject': - if i.subtypname is not None: - subtypname = i.subtypname - maybe_element = "_element" - else: - subtypname = i.children[0].subtypname - maybe_element = "" - - emit(c_file, f''' - if (src->{i.fixname}) - {{ - ret->{i.fixname} = calloc (1, sizeof (*ret->{i.fixname})); - if (ret->{i.fixname} == NULL) - return NULL; - ret->{i.fixname}->len = src->{i.fixname}->len; - ret->{i.fixname}->keys = calloc (src->{i.fixname}->len + 1, sizeof (char *)); - if (ret->{i.fixname}->keys == NULL) - return NULL; - ret->{i.fixname}->values = calloc (src->{i.fixname}->len + 1, sizeof (*ret->{i.fixname}->values)); - if (ret->{i.fixname}->values == NULL) - return NULL; - for (size_t i = 0; i < ret->{i.fixname}->len; i++) - {{ - ret->{i.fixname}->keys[i] = strdup (src->{i.fixname}->keys[i]); - if (ret->{i.fixname}->keys[i] == NULL) - return NULL; - ret->{i.fixname}->values[i] = clone_{subtypname}{maybe_element} (src->{i.fixname}->values[i]); - if (ret->{i.fixname}->values[i] == NULL) - return NULL; - }} - }} - ''', indent=1) else: raise Exception("Unimplemented type for clone: %s" % i.typ) From b49928de62f3aa9152bc2f5ddd8c9bbc37eee8ea Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 20:28:35 +0000 Subject: [PATCH 37/51] source: remove dead code in make_c_free() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove explicit elif branches for basic map types, mapStringObject, and object types in make_c_free(). These are now fully handled by the type handler dispatch (BasicMapType, MapStringObjectType, ObjectType respectively). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 4c50d075..c3fd4c5f 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -1804,27 +1804,6 @@ def make_c_free (obj, c_file, prefix): handler = get_type_handler(i.typ) if handler: handler.emit_free(c_file, i, prefix, indent=1) - elif helpers.valid_basic_map_name(i.typ): - free_func = helpers.make_basic_map_name(i.typ) - emit(c_file, f''' - free_{free_func} (ptr->{i.fixname}); - ptr->{i.fixname} = NULL; - ''', indent=1) - elif i.typ == 'mapStringObject': - free_func = i.subtypname or helpers.get_prefixed_name(i.name, prefix) - emit(c_file, f''' - free_{free_func} (ptr->{i.fixname}); - ptr->{i.fixname} = NULL; - ''', indent=1) - elif i.typ == 'object': - typename = i.subtypname or helpers.get_prefixed_name(i.name, prefix) - emit(c_file, f''' - if (ptr->{i.fixname} != NULL) - {{ - free_{typename} (ptr->{i.fixname}); - ptr->{i.fixname} = NULL; - }} - ''', indent=1) if obj.typ == 'object': if obj.children is not None: From 31824dc28d371918dba4d900f0cef36f948a1344 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 20:29:41 +0000 Subject: [PATCH 38/51] source: remove dead code in read_val_generator() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove explicit elif branch for basic map types since this is now handled by BasicMapType.emit_read_value() via the type handler dispatch. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index c3fd4c5f..6aab3bb2 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -1572,20 +1572,6 @@ def read_val_generator(c_file, level, src, dest, typ, keyname, obj_typename): handler = get_type_handler(typ) if handler: handler.emit_read_value(c_file, src, dest, keyname, obj_typename, level=level) - elif helpers.valid_basic_map_name(typ): - emit(c_file, f''' - yajl_val val = {src}; - if (val != NULL) - {{ - {dest} = make_{helpers.make_basic_map_name(typ)} (val, ctx, err); - if ({dest} == NULL) - {{ - ''', indent=level) - emit_value_error(c_file, keyname, indent=level + 2) - emit(c_file, ''' - } - } - ''', indent=level) def make_clone(obj, c_file, prefix): From 45a1b357764c61e057325d3aa7a313df1eb8e682 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 20:34:21 +0000 Subject: [PATCH 39/51] source: skip handler for object type in make_clone() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Object type clone requires parent context to determine whether to emit simple clone code (for regular object parents) or array iteration clone code (for mapStringObject parents). Skip the handler dispatch for object type and use explicit handling instead. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 6aab3bb2..a85a2f29 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -694,7 +694,8 @@ def emit_free(self, c_file, obj, prefix, indent=1): ''', indent=indent) def emit_clone(self, c_file, obj, prefix, indent=1): - # Object clone requires context (parent type) - handled in make_clone + # Object clone within mapStringObject requires special handling + # that needs parent context - handled in make_clone pass @@ -1609,7 +1610,9 @@ def make_clone(obj, c_file, prefix): nodes = obj.children if obj.subtypobj is None else obj.subtypobj for i in nodes or []: handler = get_type_handler(i.typ) - if handler: + # Object type needs parent context (mapStringObject vs regular object) + # so we handle it explicitly below rather than via handler + if handler and i.typ != 'object': handler.emit_clone(c_file, i, prefix, indent=1) elif i.typ == 'object': node_name = i.subtypname or helpers.get_prefixed_name(i.name, prefix) From df11b0a248c690230aa103aa6bfd53ecc0473a69 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 20:41:11 +0000 Subject: [PATCH 40/51] helpers: rename judge_* functions to clearer names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename functions with confusing "judge_" prefix to more descriptive names following Python naming conventions: - judge_data_type -> is_numeric_type - judge_data_pointer_type -> is_numeric_pointer_type - obtain_data_pointer_type -> get_pointer_base_type Also simplify the docstrings to be more concise and informative. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/headers.py | 4 ++-- src/ocispec/helpers.py | 24 ++++++------------------ src/ocispec/sources.py | 10 +++++----- 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/src/ocispec/headers.py b/src/ocispec/headers.py index b1bfe2d9..f1c93120 100755 --- a/src/ocispec/headers.py +++ b/src/ocispec/headers.py @@ -55,7 +55,7 @@ def append_header_arr(obj, header, prefix): helpers.get_map_c_types(i.typ) header.append(f" {c_typ}{' ' if '*' not in c_typ else ''}{i.fixname};\n") for i in obj.subtypobj: - if helpers.judge_data_type(i.typ) or i.typ == 'boolean': + if helpers.is_numeric_type(i.typ) or i.typ == 'boolean': header.append(f" unsigned int {i.fixname}_present : 1;\n") typename = helpers.get_name_substr(obj.name, prefix) header.append(f"}}\n{typename};\n\n") @@ -157,7 +157,7 @@ def append_type_c_header(obj, header, prefix): header.append(" char unuseful; // unuseful definition to avoid empty struct\n") present_tags = [] for i in obj.children or []: - if helpers.judge_data_type(i.typ) or i.typ == 'boolean': + if helpers.is_numeric_type(i.typ) or i.typ == 'boolean': present_tags.append(f" unsigned int {i.fixname}_present : 1;\n") if i.typ == 'array': append_header_child_arr(i, header, prefix) diff --git a/src/ocispec/helpers.py b/src/ocispec/helpers.py index 0de60c4d..ce480f1d 100755 --- a/src/ocispec/helpers.py +++ b/src/ocispec/helpers.py @@ -192,33 +192,21 @@ def is_compound_type(typ): ''' return typ in ('object', 'array', 'mapStringObject') -def judge_data_type(typ): - ''' - Description: Check numeric type - Interface: None - History: 2019-06-17 - ''' +def is_numeric_type(typ): + '''Check if typ is a numeric type (int, uint, double, etc.).''' if (typ.startswith("int") or typ.startswith("uint")) and \ "Pointer" not in typ: return True return typ in ("integer", "UID", "GID", "double") -def judge_data_pointer_type(typ): - ''' - Description: Check numeric pointer type - Interface: None - History: 2019-06-17 - ''' +def is_numeric_pointer_type(typ): + '''Check if typ is a pointer to a numeric type.''' if (typ.startswith("int") or typ.startswith("uint")) and "Pointer" in typ: return True return False -def obtain_data_pointer_type(typ): - ''' - Description: Get numeric pointer type - Interface: None - History: 2019-06-17 - ''' +def get_pointer_base_type(typ): + '''Extract the base type from a pointer type (e.g., "int64Pointer" -> "int64").''' index = typ.find("Pointer") return typ[0:index] if index != -1 else "" diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index a85a2f29..433ae569 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -586,7 +586,7 @@ class NumericPointerType(TypeHandler): def __init__(self, typ): self.typ = typ - self.base_typ = helpers.obtain_data_pointer_type(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)', @@ -876,7 +876,7 @@ def emit_clone(self, c_file, obj, prefix, indent=1): {{ ''', indent=indent) - if helpers.judge_data_type(obj.subtyp) or obj.subtyp == 'boolean': + if helpers.is_numeric_type(obj.subtyp) or obj.subtyp == 'boolean': emit(c_file, f''' ret->{obj.fixname}[i] = src->{obj.fixname}[i]; ''', indent=indent+2) @@ -957,9 +957,9 @@ def get_type_handler(typ): """ if typ in _TYPE_HANDLERS: return _TYPE_HANDLERS[typ] - if helpers.judge_data_type(typ): + if helpers.is_numeric_type(typ): return NumericType(typ) - if helpers.judge_data_pointer_type(typ): + if helpers.is_numeric_pointer_type(typ): return NumericPointerType(typ) if helpers.valid_basic_map_name(typ): return BasicMapType(typ) @@ -1197,7 +1197,7 @@ def parse_obj_arr_obj(obj, c_file, prefix, obj_typename): required_to_check = [] for i in nodes or []: if obj.required and i.origname in obj.required and \ - not helpers.judge_data_type(i.typ) and i.typ != 'boolean': + not helpers.is_numeric_type(i.typ) and i.typ != 'boolean': required_to_check.append(i) parse_obj_type(i, c_file, prefix, obj_typename) From 73e8f9ef6b965efe9123d912788dfda7f1446f56 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 21:00:30 +0000 Subject: [PATCH 41/51] source: inline array functions into ArrayType handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move parse_obj_type_array, get_obj_arr_obj_array, and make_c_array_free directly into the ArrayType class methods they were called from. This consolidates all array-related code generation into the ArrayType handler, making the code more cohesive and easier to maintain. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 798 ++++++++++++++++++++--------------------- 1 file changed, 390 insertions(+), 408 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 433ae569..5ecb8a4d 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -852,16 +852,400 @@ class ArrayType(TypeHandler): """ def emit_parse(self, c_file, obj, prefix, obj_typename, indent=1): - # Delegate to existing function - will be defined later - parse_obj_type_array(obj, c_file, prefix, obj_typename) + """Generate C code to parse an array from JSON.""" + if obj.subtypobj or obj.subtyp == 'object': + typename = obj.subtypname if obj.subtypname else helpers.get_name_substr(obj.name, prefix) + + emit(c_file, f''' + do + {{ + yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_array); + if (tmp != NULL && YAJL_GET_ARRAY (tmp) != NULL) + {{ + size_t i; + size_t len = YAJL_GET_ARRAY_NO_CHECK (tmp)->len; + yajl_val *values = YAJL_GET_ARRAY_NO_CHECK (tmp)->values; + 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.doublearray: + calloc_with_check(c_file, f'ret->{obj.fixname}_item_lens', 'len + 1', 'size_t', indent=3) + + emit(c_file, ''' + for (i = 0; i < len; i++) + { + yajl_val val = values[i]; + ''', indent=3) + + if obj.doublearray: + emit(c_file, f''' + size_t j; + ret->{obj.fixname}[i] = calloc ( YAJL_GET_ARRAY_NO_CHECK(val)->len + 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++) + { + ''', indent=4) + emit(c_file, f''' + ret->{obj.fixname}[i][j] = make_{typename} (items[j], ctx, err); + ''', indent=5) + null_check_return(c_file, f'ret->{obj.fixname}[i][j]', indent=5) + emit(c_file, f''' + ret->{obj.fixname}_item_lens[i] += 1; + }}; + ''', indent=5) + else: + emit(c_file, f''' + ret->{obj.fixname}[i] = make_{typename} (val, ctx, err); + ''', indent=4) + null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=4) + + emit(c_file, ''' + } + } + } + while (0); + ''', indent=1) + elif obj.subtyp == 'byte': + emit(c_file, f''' + do + {{ + yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_string); + if (tmp != NULL) + {{ + ''', indent=1) + + if obj.doublearray: + emit(c_file, f''' + yajl_val *items = YAJL_GET_ARRAY_NO_CHECK(tmp)->values; + ret->{obj.fixname} = calloc ( YAJL_GET_ARRAY_NO_CHECK(tmp)->len + 1, sizeof (*ret->{obj.fixname})); + ''', indent=4) + null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=4) + emit(c_file, ''' + size_t j; + for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(tmp)->len; j++) + { + char *str = YAJL_GET_STRING (itmes[j]); + ''', indent=4) + emit(c_file, f''' + ret->{obj.fixname}[j] = (uint8_t *)strdup (str ? str : ""); + ''', indent=5) + null_check_return(c_file, f'ret->{obj.fixname}[j]', indent=5) + emit(c_file, ''' + }; + ''', indent=5) + else: + emit(c_file, ''' + char *str = YAJL_GET_STRING (tmp); + ''', indent=3) + emit(c_file, f''' + ret->{obj.fixname} = (uint8_t *)strdup (str ? str : ""); + ''', indent=3) + null_check_return(c_file, f'ret->{obj.fixname}', indent=3) + emit(c_file, f''' + ret->{obj.fixname}_len = str != NULL ? strlen (str) : 0; + ''', indent=3) + + emit(c_file, ''' + } + } + while (0); + ''', indent=1) + else: + emit(c_file, f''' + do + {{ + yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_array); + if (tmp != NULL && YAJL_GET_ARRAY (tmp) != NULL) + {{ + size_t i; + size_t len = YAJL_GET_ARRAY_NO_CHECK (tmp)->len; + yajl_val *values = YAJL_GET_ARRAY_NO_CHECK (tmp)->values; + 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.doublearray: + calloc_with_check(c_file, f'ret->{obj.fixname}_item_lens', 'len + 1', 'size_t', indent=3) + + emit(c_file, ''' + for (i = 0; i < len; i++) + { + ''', indent=3) + + if obj.doublearray: + 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})); + ''', indent=4) + null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=5) + emit(c_file, ''' + size_t j; + for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(values[i])->len; j++) + { + ''', indent=4) + read_val_generator(c_file, 5, 'items[j]', \ + f"ret->{obj.fixname}[i][j]", obj.subtyp, obj.origname, obj_typename) + emit(c_file, f''' + ret->{obj.fixname}_item_lens[i] += 1; + }}; + ''', indent=5) + else: + read_val_generator(c_file, 4, 'values[i]', \ + f"ret->{obj.fixname}[i]", obj.subtyp, obj.origname, obj_typename) + + emit(c_file, ''' + } + } + } + while (0); + ''', indent=1) def emit_generate(self, c_file, obj, prefix, indent=1): - # Delegate to existing function - will be defined later - get_obj_arr_obj_array(obj, c_file, prefix) + """Generate C code to serialize an array to JSON.""" + if obj.subtypobj or obj.subtyp == 'object': + 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(c_file, obj.origname, indent=2) + check_gen_status(c_file, 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(c_file, ''' + for (i = 0; i < len; i++) + { + ''', indent=2) + + if obj.doublearray: + emit_gen_array_open(c_file, indent=3) + check_gen_status(c_file, indent=3) + emit(c_file, f''' + size_t j; + for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++) + {{ + stat = gen_{typename} (g, ptr->{obj.fixname}[i][j], ctx, err); + ''', indent=3) + check_gen_status(c_file, indent=4) + emit(c_file, ''' + } + ''', indent=4) + emit_gen_array_close(c_file, indent=3) + else: + emit(c_file, f''' + stat = gen_{typename} (g, ptr->{obj.fixname}[i], ctx, err); + ''', indent=3) + check_gen_status(c_file, indent=3) + + emit(c_file, ''' + } + ''', indent=2) + emit_gen_array_close(c_file, indent=2) + emit_beautify_on(c_file, '!len', indent=2) + check_gen_status(c_file, indent=2) + + emit(c_file, ''' + } + ''', indent=1) + elif obj.subtyp == 'byte': + emit(c_file, f''' + if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL && ptr->{obj.fixname}_len)) + {{ + const char *str = ""; + size_t len = 0; + ''', indent=1) + emit_gen_key(c_file, obj.origname, indent=2) + check_gen_status(c_file, indent=2) + + if obj.doublearray: + emit_gen_array_open(c_file, indent=3) + check_gen_status(c_file, indent=3) + emit(c_file, f''' + {{ + size_t i; + for (i = 0; i < ptr->{obj.fixname}_len; i++) + {{ + if (ptr->{obj.fixname}[i] != NULL) + str = (const char *)ptr->{obj.fixname}[i]; + else () + str = ""; + stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)str, strlen(str)); + }} + }} + ''', indent=2) + emit_gen_array_close(c_file, indent=2) + else: + emit(c_file, f''' + if (ptr != NULL && ptr->{obj.fixname} != NULL) + {{ + str = (const char *)ptr->{obj.fixname}; + len = ptr->{obj.fixname}_len; + }} + stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)str, len); + ''', indent=2) + + check_gen_status(c_file, indent=2) + + emit(c_file, ''' + } + ''', indent=1) + else: + 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(c_file, obj.origname, indent=2) + check_gen_status(c_file, 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(c_file, ''' + for (i = 0; i < len; i++) + { + ''', indent=2) + + if obj.doublearray: + typename = helpers.get_map_c_types(obj.subtyp) + emit_gen_array_open(c_file, indent=3) + check_gen_status(c_file, indent=3) + emit(c_file, f''' + size_t j; + for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++) + {{ + ''', indent=3) + json_value_generator(c_file, 4, f"ptr->{obj.fixname}[i][j]", 'g', 'ctx', obj.subtyp) + emit(c_file, ''' + } + ''', indent=4) + emit_gen_array_close(c_file, indent=3) + else: + json_value_generator(c_file, 3, f"ptr->{obj.fixname}[i]", 'g', 'ctx', obj.subtyp) + + emit(c_file, ''' + } + ''', indent=2) + emit_gen_array_close(c_file, indent=2) + check_gen_status(c_file, indent=2) + emit_beautify_on(c_file, '!len', indent=2) + emit(c_file, ''' + } + ''', indent=2) def emit_free(self, c_file, obj, prefix, indent=1): - # Delegate to existing function - will be defined later - make_c_array_free(obj, c_file, prefix) + """Generate C code to free an array.""" + if helpers.valid_basic_map_name(obj.subtyp): + free_func = helpers.make_basic_map_name(obj.subtyp) + emit(c_file, f''' + if (ptr->{obj.fixname} != NULL) + {{ + size_t i; + for (i = 0; i < ptr->{obj.fixname}_len; i++) + {{ + if (ptr->{obj.fixname}[i] != NULL) + {{ + free_{free_func} (ptr->{obj.fixname}[i]); + ptr->{obj.fixname}[i] = NULL; + }} + }} + ''', indent=1) + free_and_null(c_file, "ptr", obj.fixname, indent=2) + emit(c_file, ''' + } + ''', indent=1) + elif obj.subtyp == 'string': + c_file_str(c_file, obj) + elif not helpers.is_compound_type(obj.subtyp): + emit(c_file, ''' + { + ''', indent=0) + if obj.doublearray: + emit(c_file, f''' + size_t i; + for (i = 0; i < ptr->{obj.fixname}_len; i++) + {{ + ''', indent=3) + free_and_null(c_file, "ptr", f"{obj.fixname}[i]", indent=4) + emit(c_file, ''' + } + ''', indent=3) + free_and_null(c_file, "ptr", f"{obj.fixname}_item_lens", indent=3) + free_and_null(c_file, "ptr", obj.fixname, indent=2) + emit(c_file, ''' + } + ''', indent=1) + elif obj.subtyp == 'object' or obj.subtypobj is not None: + free_func = obj.subtypname if obj.subtypname is not None else helpers.get_name_substr(obj.name, prefix) + + emit(c_file, f''' + if (ptr->{obj.fixname} != NULL) + {{ + size_t i; + for (i = 0; i < ptr->{obj.fixname}_len; i++) + {{ + ''', indent=1) + + if obj.doublearray: + emit(c_file, f''' + size_t j; + for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++) + {{ + free_{free_func} (ptr->{obj.fixname}[i][j]); + ptr->{obj.fixname}[i][j] = NULL; + }} + ''', indent=2) + free_and_null(c_file, "ptr", f"{obj.fixname}[i]", indent=2) + else: + emit(c_file, f''' + if (ptr->{obj.fixname}[i] != NULL) + {{ + free_{free_func} (ptr->{obj.fixname}[i]); + ptr->{obj.fixname}[i] = NULL; + }} + ''', indent=2) + + emit(c_file, ''' + } + ''', indent=2) + + if obj.doublearray: + free_and_null(c_file, "ptr", f"{obj.fixname}_item_lens", indent=2) + + free_and_null(c_file, "ptr", obj.fixname, indent=2) + + emit(c_file, ''' + } + ''', indent=1) + + c_typ = helpers.obtain_pointer(obj.name, obj.subtypobj, prefix) + if c_typ == "": + return + if obj.subtypname is not None: + c_typ = c_typ + "_element" + + emit(c_file, f''' + free_{c_typ} (ptr->{obj.fixname}); + ptr->{obj.fixname} = NULL; + ''', indent=1) def emit_clone(self, c_file, obj, prefix, indent=1): """Generate clone code for array fields.""" @@ -1026,161 +1410,6 @@ def parse_map_string_obj(obj, c_file, prefix, obj_typename): c_file.append(' }\n') -def parse_obj_type_array(obj, c_file, prefix, obj_typename): - if obj.subtypobj or obj.subtyp == 'object': - if obj.subtypname: - typename = obj.subtypname - else: - typename = helpers.get_name_substr(obj.name, prefix) - - emit(c_file, f''' - do - {{ - yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_array); - if (tmp != NULL && YAJL_GET_ARRAY (tmp) != NULL) - {{ - size_t i; - size_t len = YAJL_GET_ARRAY_NO_CHECK (tmp)->len; - yajl_val *values = YAJL_GET_ARRAY_NO_CHECK (tmp)->values; - 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.doublearray: - calloc_with_check(c_file, f'ret->{obj.fixname}_item_lens', 'len + 1', 'size_t', indent=3) - - emit(c_file, ''' - for (i = 0; i < len; i++) - { - yajl_val val = values[i]; - ''', indent=3) - - if obj.doublearray: - emit(c_file, f''' - size_t j; - ret->{obj.fixname}[i] = calloc ( YAJL_GET_ARRAY_NO_CHECK(val)->len + 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++) - { - ''', indent=4) - emit(c_file, f''' - ret->{obj.fixname}[i][j] = make_{typename} (items[j], ctx, err); - ''', indent=5) - null_check_return(c_file, f'ret->{obj.fixname}[i][j]', indent=5) - emit(c_file, f''' - ret->{obj.fixname}_item_lens[i] += 1; - }}; - ''', indent=5) - else: - emit(c_file, f''' - ret->{obj.fixname}[i] = make_{typename} (val, ctx, err); - ''', indent=4) - null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=4) - - emit(c_file, ''' - } - } - } - while (0); - ''', indent=1) - elif obj.subtyp == 'byte': - emit(c_file, f''' - do - {{ - yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_string); - if (tmp != NULL) - {{ - ''', indent=1) - - if obj.doublearray: - emit(c_file, f''' - yajl_val *items = YAJL_GET_ARRAY_NO_CHECK(tmp)->values; - ret->{obj.fixname} = calloc ( YAJL_GET_ARRAY_NO_CHECK(tmp)->len + 1, sizeof (*ret->{obj.fixname})); - ''', indent=4) - null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=4) - emit(c_file, ''' - size_t j; - for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(tmp)->len; j++) - { - char *str = YAJL_GET_STRING (itmes[j]); - ''', indent=4) - emit(c_file, f''' - ret->{obj.fixname}[j] = (uint8_t *)strdup (str ? str : ""); - ''', indent=5) - null_check_return(c_file, f'ret->{obj.fixname}[j]', indent=5) - emit(c_file, ''' - }; - ''', indent=5) - else: - emit(c_file, ''' - char *str = YAJL_GET_STRING (tmp); - ''', indent=3) - emit(c_file, f''' - ret->{obj.fixname} = (uint8_t *)strdup (str ? str : ""); - ''', indent=3) - null_check_return(c_file, f'ret->{obj.fixname}', indent=3) - emit(c_file, f''' - ret->{obj.fixname}_len = str != NULL ? strlen (str) : 0; - ''', indent=3) - - emit(c_file, ''' - } - } - while (0); - ''', indent=1) - else: - emit(c_file, f''' - do - {{ - yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_array); - if (tmp != NULL && YAJL_GET_ARRAY (tmp) != NULL) - {{ - size_t i; - size_t len = YAJL_GET_ARRAY_NO_CHECK (tmp)->len; - yajl_val *values = YAJL_GET_ARRAY_NO_CHECK (tmp)->values; - 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.doublearray: - calloc_with_check(c_file, f'ret->{obj.fixname}_item_lens', 'len + 1', 'size_t', indent=3) - - emit(c_file, ''' - for (i = 0; i < len; i++) - { - ''', indent=3) - - if obj.doublearray: - 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})); - ''', indent=4) - null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=5) - emit(c_file, ''' - size_t j; - for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(values[i])->len; j++) - { - ''', indent=4) - read_val_generator(c_file, 5, 'items[j]', \ - f"ret->{obj.fixname}[i][j]", obj.subtyp, obj.origname, obj_typename) - emit(c_file, f''' - ret->{obj.fixname}_item_lens[i] += 1; - }}; - ''', indent=5) - else: - read_val_generator(c_file, 4, 'values[i]', \ - f"ret->{obj.fixname}[i]", obj.subtyp, obj.origname, obj_typename) - - emit(c_file, ''' - } - } - } - while (0); - ''', indent=1) - def parse_obj_type(obj, c_file, prefix, obj_typename): """Generate C code to parse a single JSON field into a struct member.""" handler = get_type_handler(obj.typ) @@ -1359,154 +1588,6 @@ def get_map_string_obj(obj, c_file, prefix): check_gen_status(c_file, indent=1) emit_beautify_on(c_file, '!len', indent=1) -def get_obj_arr_obj_array(obj, c_file, prefix): - if obj.subtypobj or obj.subtyp == 'object': - if obj.subtypname: - typename = obj.subtypname - else: - typename = 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(c_file, obj.origname, indent=2) - check_gen_status(c_file, 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(c_file, ''' - for (i = 0; i < len; i++) - { - ''', indent=2) - - if obj.doublearray: - emit_gen_array_open(c_file, indent=3) - check_gen_status(c_file, indent=3) - emit(c_file, f''' - size_t j; - for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++) - {{ - stat = gen_{typename} (g, ptr->{obj.fixname}[i][j], ctx, err); - ''', indent=3) - check_gen_status(c_file, indent=4) - emit(c_file, ''' - } - ''', indent=4) - emit_gen_array_close(c_file, indent=3) - else: - emit(c_file, f''' - stat = gen_{typename} (g, ptr->{obj.fixname}[i], ctx, err); - ''', indent=3) - check_gen_status(c_file, indent=3) - - emit(c_file, ''' - } - ''', indent=2) - emit_gen_array_close(c_file, indent=2) - emit_beautify_on(c_file, '!len', indent=2) - check_gen_status(c_file, indent=2) - - emit(c_file, ''' - } - ''', indent=1) - elif obj.subtyp == 'byte': - emit(c_file, f''' - if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL && ptr->{obj.fixname}_len)) - {{ - const char *str = ""; - size_t len = 0; - ''', indent=1) - emit_gen_key(c_file, obj.origname, indent=2) - check_gen_status(c_file, indent=2) - - if obj.doublearray: - emit_gen_array_open(c_file, indent=3) - check_gen_status(c_file, indent=3) - emit(c_file, f''' - {{ - size_t i; - for (i = 0; i < ptr->{obj.fixname}_len; i++) - {{ - if (ptr->{obj.fixname}[i] != NULL) - str = (const char *)ptr->{obj.fixname}[i]; - else () - str = ""; - stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)str, strlen(str)); - }} - }} - ''', indent=2) - emit_gen_array_close(c_file, indent=2) - else: - emit(c_file, f''' - if (ptr != NULL && ptr->{obj.fixname} != NULL) - {{ - str = (const char *)ptr->{obj.fixname}; - len = ptr->{obj.fixname}_len; - }} - stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)str, len); - ''', indent=2) - - check_gen_status(c_file, indent=2) - - emit(c_file, ''' - } - ''', indent=1) - else: - 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(c_file, obj.origname, indent=2) - check_gen_status(c_file, 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(c_file, ''' - for (i = 0; i < len; i++) - { - ''', indent=2) - - if obj.doublearray: - typename = helpers.get_map_c_types(obj.subtyp) - emit_gen_array_open(c_file, indent=3) - check_gen_status(c_file, indent=3) - emit(c_file, f''' - size_t j; - for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++) - {{ - ''', indent=3) - json_value_generator(c_file, 4, f"ptr->{obj.fixname}[i][j]", 'g', 'ctx', obj.subtyp) - emit(c_file, ''' - } - ''', indent=4) - emit_gen_array_close(c_file, indent=3) - else: - json_value_generator(c_file, 3, f"ptr->{obj.fixname}[i]", 'g', 'ctx', obj.subtyp) - - emit(c_file, ''' - } - ''', indent=2) - emit_gen_array_close(c_file, indent=2) - check_gen_status(c_file, indent=2) - emit_beautify_on(c_file, '!len', indent=2) - emit(c_file, ''' - } - ''', indent=2) def get_obj_arr_obj(obj, c_file, prefix): """Generate C code to serialize a struct field to JSON.""" @@ -1653,105 +1734,6 @@ def json_value_generator(c_file, level, src, dst, ptx, typ): if handler: handler.emit_json_value(c_file, src, dst, ptx, level=level) -def make_c_array_free (i, c_file, prefix): - if helpers.valid_basic_map_name(i.subtyp): - free_func = helpers.make_basic_map_name(i.subtyp) - emit(c_file, f''' - if (ptr->{i.fixname} != NULL) - {{ - size_t i; - for (i = 0; i < ptr->{i.fixname}_len; i++) - {{ - if (ptr->{i.fixname}[i] != NULL) - {{ - free_{free_func} (ptr->{i.fixname}[i]); - ptr->{i.fixname}[i] = NULL; - }} - }} - ''', indent=1) - free_and_null(c_file, "ptr", i.fixname, indent=2) - emit(c_file, ''' - } - ''', indent=1) - elif i.subtyp == 'string': - c_file_str(c_file, i) - elif not helpers.is_compound_type(i.subtyp): - emit(c_file, ''' - { - ''', indent=0) - if i.doublearray: - emit(c_file, f''' - size_t i; - for (i = 0; i < ptr->{i.fixname}_len; i++) - {{ - ''', indent=3) - free_and_null(c_file, "ptr", f"{i.fixname}[i]", indent=4) - emit(c_file, ''' - } - ''', indent=3) - free_and_null(c_file, "ptr", f"{i.fixname}_item_lens", indent=3) - free_and_null(c_file, "ptr", i.fixname, indent=2) - emit(c_file, ''' - } - ''', indent=1) - elif i.subtyp == 'object' or i.subtypobj is not None: - if i.subtypname is not None: - free_func = i.subtypname - else: - free_func = helpers.get_name_substr(i.name, prefix) - - emit(c_file, f''' - if (ptr->{i.fixname} != NULL) - {{ - size_t i; - for (i = 0; i < ptr->{i.fixname}_len; i++) - {{ - ''', indent=1) - - if i.doublearray: - emit(c_file, f''' - size_t j; - for (j = 0; j < ptr->{i.fixname}_item_lens[i]; j++) - {{ - free_{free_func} (ptr->{i.fixname}[i][j]); - ptr->{i.fixname}[i][j] = NULL; - }} - ''', indent=2) - free_and_null(c_file, "ptr", f"{i.fixname}[i]", indent=2) - else: - emit(c_file, f''' - if (ptr->{i.fixname}[i] != NULL) - {{ - free_{free_func} (ptr->{i.fixname}[i]); - ptr->{i.fixname}[i] = NULL; - }} - ''', indent=2) - - emit(c_file, ''' - } - ''', indent=2) - - if i.doublearray: - free_and_null(c_file, "ptr", f"{i.fixname}_item_lens", indent=2) - - free_and_null(c_file, "ptr", i.fixname, indent=2) - - emit(c_file, ''' - } - ''', indent=1) - - c_typ = helpers.obtain_pointer(i.name, i.subtypobj, prefix) - if c_typ == "": - return True - if i.subtypname is not None: - c_typ = c_typ + "_element" - - emit(c_file, f''' - free_{c_typ} (ptr->{i.fixname}); - ptr->{i.fixname} = NULL; - ''', indent=1) - - return False def make_c_free (obj, c_file, prefix): """ From 337925c11936e400ba44478dc79ed10a8305e6f5 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 21:02:17 +0000 Subject: [PATCH 42/51] source: inline trivial wrapper functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove parse_obj_type and get_obj_arr_obj wrapper functions by inlining their single-line bodies directly at the call sites. These wrappers just called get_type_handler() and delegated to the handler methods, adding unnecessary indirection. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 5ecb8a4d..25734c0b 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -1410,12 +1410,6 @@ def parse_map_string_obj(obj, c_file, prefix, obj_typename): c_file.append(' }\n') -def parse_obj_type(obj, c_file, prefix, obj_typename): - """Generate C code to parse a single JSON field into a struct member.""" - handler = get_type_handler(obj.typ) - if handler: - handler.emit_parse(c_file, obj, prefix, obj_typename, indent=1) - def parse_obj_arr_obj(obj, c_file, prefix, obj_typename): """ Description: generate c language for parse object or array object @@ -1428,7 +1422,9 @@ def parse_obj_arr_obj(obj, c_file, prefix, obj_typename): if obj.required and i.origname in obj.required and \ not helpers.is_numeric_type(i.typ) and i.typ != 'boolean': required_to_check.append(i) - parse_obj_type(i, c_file, prefix, obj_typename) + handler = get_type_handler(i.typ) + if handler: + handler.emit_parse(c_file, i, prefix, obj_typename, indent=1) for i in required_to_check: emit(c_file, f''' @@ -1589,13 +1585,6 @@ def get_map_string_obj(obj, c_file, prefix): emit_beautify_on(c_file, '!len', indent=1) -def get_obj_arr_obj(obj, c_file, prefix): - """Generate C code to serialize a struct field to JSON.""" - handler = get_type_handler(obj.typ) - if handler: - handler.emit_generate(c_file, obj, prefix, indent=1) - - def get_c_json(obj, c_file, prefix): """ Description: c language generate json file @@ -1629,7 +1618,9 @@ def get_c_json(obj, c_file, prefix): emit_gen_map_open(c_file, indent=1) check_gen_status(c_file, indent=1) for i in nodes or []: - get_obj_arr_obj(i, c_file, prefix) + handler = get_type_handler(i.typ) + if handler: + handler.emit_generate(c_file, i, prefix, indent=1) if obj.typ == 'object': if obj.children is not None: emit(c_file, ''' From e59b69678dc707c421ca149ecc34f7620f2c65f9 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 21:22:26 +0000 Subject: [PATCH 43/51] source: inline c_file_str() into ArrayType.emit_free() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the c_file_str() function body directly into the ArrayType.emit_free() method since it was only called from there. This consolidates all array string freeing logic within the ArrayType handler. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 87 +++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 48 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 25734c0b..d6367112 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -1173,7 +1173,45 @@ def emit_free(self, c_file, obj, prefix, indent=1): } ''', indent=1) elif obj.subtyp == 'string': - c_file_str(c_file, obj) + emit(c_file, f''' + if (ptr->{obj.fixname} != NULL) + {{ + size_t i; + for (i = 0; i < ptr->{obj.fixname}_len; i++) + {{ + ''', indent=1) + + if obj.doublearray: + emit(c_file, f''' + size_t j; + for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++) + {{ + ''', indent=3) + free_and_null(c_file, "ptr", f"{obj.fixname}[i][j]", indent=4) + emit(c_file, ''' + } + ''', indent=3) + + emit(c_file, f''' + if (ptr->{obj.fixname}[i] != NULL) + {{ + ''', indent=3) + + free_and_null(c_file, "ptr", f"{obj.fixname}[i]", indent=4) + + emit(c_file, ''' + } + } + ''', indent=3) + + if obj.doublearray: + free_and_null(c_file, "ptr", f"{obj.fixname}_item_lens", indent=2) + + free_and_null(c_file, "ptr", obj.fixname, indent=2) + + emit(c_file, ''' + } + ''', indent=1) elif not helpers.is_compound_type(obj.subtyp): emit(c_file, ''' { @@ -1810,53 +1848,6 @@ def c_file_map_str(c_file, child, childname): } ''', indent=1) -def c_file_str(c_file, i): - """ - Description: generate c code template - Interface: None - History: 2019-10-31 - """ - emit(c_file, f''' - if (ptr->{i.fixname} != NULL) - {{ - size_t i; - for (i = 0; i < ptr->{i.fixname}_len; i++) - {{ - ''', indent=1) - - if i.doublearray: - emit(c_file, f''' - size_t j; - for (j = 0; j < ptr->{i.fixname}_item_lens[i]; j++) - {{ - ''', indent=3) - free_and_null(c_file, "ptr", f"{i.fixname}[i][j]", indent=4) - emit(c_file, ''' - } - ''', indent=3) - - emit(c_file, f''' - if (ptr->{i.fixname}[i] != NULL) - {{ - ''', indent=3) - - free_and_null(c_file, "ptr", f"{i.fixname}[i]", indent=4) - - emit(c_file, ''' - } - } - ''', indent=3) - - if i.doublearray: - free_and_null(c_file, "ptr", f"{i.fixname}_item_lens", indent=2) - - free_and_null(c_file, "ptr", i.fixname, indent=2) - - emit(c_file, ''' - } - ''', indent=1) - - def src_reflect(structs, schema_info, c_file, root_typ): """ Description: reflect code From 4ff41175e6876038bf30e5ec98039cc7b1c8cb28 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 21:23:26 +0000 Subject: [PATCH 44/51] source: inline c_file_map_str() into make_c_free() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the c_file_map_str() function body directly into make_c_free() since it was only called from there. This removes unnecessary indirection for mapStringObject free logic. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 52 ++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index d6367112..fb99786b 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -1799,7 +1799,28 @@ def make_c_free (obj, c_file, prefix): childname = child.subtypname else: childname = helpers.get_prefixed_name(child.name, prefix) - c_file_map_str(c_file, child, childname) + emit(c_file, f''' + if (ptr->keys != NULL && ptr->{child.fixname} != NULL) + {{ + size_t i; + for (i = 0; i < ptr->len; i++) + {{ + ''', indent=1) + + free_and_null(c_file, "ptr", "keys[i]", indent=3) + + emit(c_file, f''' + free_{childname} (ptr->{child.fixname}[i]); + ptr->{child.fixname}[i] = NULL; + }} + ''', indent=3) + + free_and_null(c_file, "ptr", "keys", indent=2) + free_and_null(c_file, "ptr", child.fixname, indent=2) + + emit(c_file, ''' + } + ''', indent=1) for i in objs or []: handler = get_type_handler(i.typ) if handler: @@ -1819,35 +1840,6 @@ def make_c_free (obj, c_file, prefix): ''', indent=1) -def c_file_map_str(c_file, child, childname): - """ - Description: generate c code for map string - Interface: None - History: 2019-10-31 - """ - emit(c_file, f''' - if (ptr->keys != NULL && ptr->{child.fixname} != NULL) - {{ - size_t i; - for (i = 0; i < ptr->len; i++) - {{ - ''', indent=1) - - free_and_null(c_file, "ptr", "keys[i]", indent=3) - - emit(c_file, f''' - free_{childname} (ptr->{child.fixname}[i]); - ptr->{child.fixname}[i] = NULL; - }} - ''', indent=3) - - free_and_null(c_file, "ptr", "keys", indent=2) - free_and_null(c_file, "ptr", child.fixname, indent=2) - - emit(c_file, ''' - } - ''', indent=1) - def src_reflect(structs, schema_info, c_file, root_typ): """ Description: reflect code From 11b951f84a71ff56a3d0b01f00fc05b3013dc2b6 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 21:24:46 +0000 Subject: [PATCH 45/51] source: extract get_compound_children() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace duplicate type-case dispatch logic in make_clone() and make_c_free() with a new get_compound_children() helper function. Both functions had identical dictionary-based dispatch to get children/subtypes for compound types. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index fb99786b..58a02e04 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -273,6 +273,16 @@ def emit_beautify_on(c_file, condition='!len', indent=0): ''', indent=indent) +def get_compound_children(obj): + """Get the children/subtypes for a compound type. + + Returns [] for mapStringObject, obj.children for object, obj.subtypobj for array. + """ + return {'mapStringObject': [], + 'object': obj.children, + 'array': obj.subtypobj}[obj.typ] + + # Type handler classes for C code generation # Each type has methods for: parsing JSON, generating JSON, freeing memory, cloning @@ -1695,11 +1705,7 @@ def make_clone(obj, c_file, prefix): if not helpers.is_compound_type(obj.typ) or obj.subtypname: return typename = helpers.get_prefixed_name(obj.name, prefix) - case = obj.typ - result = {'mapStringObject': lambda x: [], - 'object': lambda x: x.children, - 'array': lambda x: x.subtypobj}[case](obj) - objs = result + objs = get_compound_children(obj) if obj.typ == 'array': if objs is None: return @@ -1773,11 +1779,7 @@ def make_c_free (obj, c_file, prefix): if not helpers.is_compound_type(obj.typ) or obj.subtypname: return typename = helpers.get_prefixed_name(obj.name, prefix) - case = obj.typ - result = {'mapStringObject': lambda x: [], - 'object': lambda x: x.children, - 'array': lambda x: x.subtypobj}[case](obj) - objs = result + objs = get_compound_children(obj) if obj.typ == 'array': if objs is None: return From d91d34a54729b5d66bb1a1efbb22a0691ea45cb2 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 21:26:03 +0000 Subject: [PATCH 46/51] source: add emit_gen_key_with_check() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a combined helper that calls emit_gen_key() followed by check_gen_status(), replacing 10 occurrences of this common pattern throughout the codebase. This reduces boilerplate and makes the code more concise. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 58a02e04..5e793d92 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -197,6 +197,12 @@ def emit_gen_key(c_file, key, indent=0): ''', 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_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. @@ -327,8 +333,7 @@ def emit_generate(self, c_file, obj, prefix, indent=1): {{ char *str = ""; ''', indent=indent) - emit_gen_key(c_file, obj.origname, indent=indent + 1) - check_gen_status(c_file, indent=indent + 1) + emit_gen_key_with_check(c_file, obj.origname, indent=indent + 1) emit(c_file, f''' if (ptr != NULL && ptr->{obj.fixname} != NULL) str = ptr->{obj.fixname}; @@ -387,8 +392,7 @@ def emit_generate(self, c_file, obj, prefix, indent=1): {{ bool b = false; ''', indent=indent) - emit_gen_key(c_file, obj.origname, indent=indent + 1) - check_gen_status(c_file, indent=indent + 1) + emit_gen_key_with_check(c_file, obj.origname, indent=indent + 1) emit(c_file, f''' if (ptr != NULL && ptr->{obj.fixname}) b = ptr->{obj.fixname}; @@ -526,8 +530,7 @@ def emit_generate(self, c_file, obj, prefix, indent=1): {{ {numtyp} num = 0; ''', indent=indent) - emit_gen_key(c_file, obj.origname, indent=indent + 1) - check_gen_status(c_file, indent=indent + 1) + emit_gen_key_with_check(c_file, obj.origname, indent=indent + 1) emit(c_file, f''' if (ptr != NULL && ptr->{obj.fixname}) num = ({numtyp})ptr->{obj.fixname}; @@ -610,8 +613,7 @@ def emit_generate(self, c_file, obj, prefix, indent=1): {{ {helpers.get_map_c_types(self.base_typ)} num = 0; ''', indent=indent) - emit_gen_key(c_file, obj.origname, indent=indent + 1) - check_gen_status(c_file, indent=indent + 1) + emit_gen_key_with_check(c_file, obj.origname, indent=indent + 1) emit(c_file, f''' if (ptr != NULL && ptr->{obj.fixname} != NULL) {{ @@ -683,8 +685,7 @@ def emit_generate(self, c_file, obj, prefix, indent=1): if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) {{ ''', indent=indent) - emit_gen_key(c_file, obj.origname, indent=indent + 1) - check_gen_status(c_file, indent=indent + 1) + 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) @@ -726,8 +727,7 @@ def emit_generate(self, c_file, obj, prefix, indent=1): if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) {{ ''', indent=indent) - emit_gen_key(c_file, obj.origname, indent=indent + 1) - check_gen_status(c_file, indent=indent + 1) + 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) @@ -807,8 +807,7 @@ def emit_generate(self, c_file, obj, prefix, indent=1): if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) {{ ''', indent=indent) - emit_gen_key(c_file, obj.fixname, indent=indent + 1) - check_gen_status(c_file, indent=indent + 1) + 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) @@ -1024,8 +1023,7 @@ def emit_generate(self, c_file, obj, prefix, indent=1): {{ size_t len = 0, i; ''', indent=1) - emit_gen_key(c_file, obj.origname, indent=2) - check_gen_status(c_file, indent=2) + emit_gen_key_with_check(c_file, obj.origname, indent=2) emit(c_file, f''' if (ptr != NULL && ptr->{obj.fixname} != NULL) @@ -1077,8 +1075,7 @@ def emit_generate(self, c_file, obj, prefix, indent=1): const char *str = ""; size_t len = 0; ''', indent=1) - emit_gen_key(c_file, obj.origname, indent=2) - check_gen_status(c_file, indent=2) + emit_gen_key_with_check(c_file, obj.origname, indent=2) if obj.doublearray: emit_gen_array_open(c_file, indent=3) @@ -1118,8 +1115,7 @@ def emit_generate(self, c_file, obj, prefix, indent=1): {{ size_t len = 0, i; ''', indent=1) - emit_gen_key(c_file, obj.origname, indent=2) - check_gen_status(c_file, indent=2) + emit_gen_key_with_check(c_file, obj.origname, indent=2) emit(c_file, f''' if (ptr != NULL && ptr->{obj.fixname} != NULL) From 81ff1fa5f2ad04bd97a42cdd4443c93d3ba68053 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 21:26:56 +0000 Subject: [PATCH 47/51] source: document ObjectType.emit_clone() design decision MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improve the comment explaining why ObjectType.emit_clone() is intentionally empty. The actual object cloning is handled directly in make_clone() because it requires per-type context that the handler dispatch pattern doesn't provide. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 5e793d92..fb3a6abe 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -705,8 +705,10 @@ def emit_free(self, c_file, obj, prefix, indent=1): ''', indent=indent) def emit_clone(self, c_file, obj, prefix, indent=1): - # Object clone within mapStringObject requires special handling - # that needs parent context - handled in make_clone + # Intentionally empty: object cloning is handled directly in make_clone() + # because it requires per-type context (typename resolution) that the + # handler dispatch pattern doesn't provide. The actual cloning logic + # for object fields is emitted by make_clone() at lines 1725-1745. pass From fed6accd6aae0e610c7c3da94f2121a17b7491ab Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 21:28:08 +0000 Subject: [PATCH 48/51] source: add BooleanPointerType.emit_generate() and emit_json_value() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complete the BooleanPointerType handler by adding emit_generate() and emit_json_value() methods. These were missing, which meant booleanPointer fields couldn't be serialized to JSON. The implementation follows the same pattern as NumericPointerType. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index fb3a6abe..0bcb88a7 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -453,6 +453,24 @@ 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)', f"ret->{obj.fixname}", 'booleanPointer', obj.origname, obj_typename, indent=indent) + def emit_generate(self, c_file, obj, prefix, indent=1): + emit(c_file, f''' + if ((ptr != NULL && ptr->{obj.fixname} != NULL)) + {{ + bool b = false; + ''', indent=indent) + emit_gen_key_with_check(c_file, obj.origname, indent=indent + 1) + emit(c_file, f''' + if (ptr != NULL && ptr->{obj.fixname} != NULL) + {{ + b = *(ptr->{obj.fixname}); + }} + ''', indent=indent + 1) + self.emit_json_value(c_file, "b", 'g', 'ctx', level=indent + 1) + emit(c_file, ''' + } + ''', indent=indent) + def emit_free(self, c_file, obj, prefix, indent=1): emit(c_file, f''' free (ptr->{obj.fixname}); @@ -493,6 +511,13 @@ def emit_read_value(self, c_file, src, dest, keyname, obj_typename, level=1): }} ''', 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) + GEN_SET_ERROR_AND_RETURN (stat, err); + ''', indent=level) + class NumericType(TypeHandler): """Handler for numeric types (integer, double, int8-int64, uint8-uint64, UID, GID).""" From dc24f9a3315b1628686eff955fc441051b37b574 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 21:41:43 +0000 Subject: [PATCH 49/51] source: refactor ArrayType using ArraySubtypeHandler classes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the monolithic ArrayType class into specialized subtype handlers: - ObjectArrayHandler: handles arrays of objects - ByteArrayHandler: handles byte arrays - PrimitiveArrayHandler: handles arrays of strings, numerics, etc. - BasicMapArrayHandler: handles arrays of basic map types The ArrayType class now delegates to _get_array_subtype_handler() which returns the appropriate handler based on the array's element type. This follows the same pattern used for the TypeHandler hierarchy and makes each subtype's code generation logic self-contained. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 887 +++++++++++++++++++++++------------------ 1 file changed, 490 insertions(+), 397 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 0bcb88a7..7ee2f772 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -881,432 +881,571 @@ def emit_clone(self, c_file, obj, prefix, indent=1): ''', indent=indent) -class ArrayType(TypeHandler): - """Handler for array type. +# Array subtype handlers for different element types +class ArraySubtypeHandler: + """Base class for array subtype-specific code generation.""" - Arrays are complex and delegate to specialized functions for each operation. - """ + def emit_parse(self, c_file, obj, prefix, obj_typename): + """Generate C code to parse array elements.""" + pass - def emit_parse(self, c_file, obj, prefix, obj_typename, indent=1): - """Generate C code to parse an array from JSON.""" - if obj.subtypobj or obj.subtyp == 'object': - typename = obj.subtypname if obj.subtypname else helpers.get_name_substr(obj.name, prefix) + def emit_generate(self, c_file, obj, prefix): + """Generate C code to serialize array elements.""" + pass - emit(c_file, f''' - do - {{ - yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_array); - if (tmp != NULL && YAJL_GET_ARRAY (tmp) != NULL) - {{ - size_t i; - size_t len = YAJL_GET_ARRAY_NO_CHECK (tmp)->len; - yajl_val *values = YAJL_GET_ARRAY_NO_CHECK (tmp)->values; - ret->{obj.fixname}_len = len; - ''', indent=1) + def emit_free(self, c_file, obj, prefix): + """Generate C code to free array elements.""" + pass - calloc_with_check(c_file, f'ret->{obj.fixname}', 'len + 1', f'*ret->{obj.fixname}', indent=3) - if obj.doublearray: - calloc_with_check(c_file, f'ret->{obj.fixname}_item_lens', 'len + 1', 'size_t', indent=3) + def emit_clone(self, c_file, obj, prefix, indent): + """Generate C code to clone array elements.""" + pass - emit(c_file, ''' - for (i = 0; i < len; i++) - { - yajl_val val = values[i]; - ''', indent=3) - if obj.doublearray: - emit(c_file, f''' - size_t j; - ret->{obj.fixname}[i] = calloc ( YAJL_GET_ARRAY_NO_CHECK(val)->len + 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++) - { - ''', indent=4) - emit(c_file, f''' - ret->{obj.fixname}[i][j] = make_{typename} (items[j], ctx, err); - ''', indent=5) - null_check_return(c_file, f'ret->{obj.fixname}[i][j]', indent=5) - emit(c_file, f''' - ret->{obj.fixname}_item_lens[i] += 1; - }}; - ''', indent=5) - else: - emit(c_file, f''' - ret->{obj.fixname}[i] = make_{typename} (val, ctx, err); - ''', indent=4) - null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=4) +class ObjectArrayHandler(ArraySubtypeHandler): + """Handler for arrays of objects.""" - emit(c_file, ''' - } - } - } - while (0); - ''', indent=1) - elif obj.subtyp == 'byte': - emit(c_file, f''' - do + 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 + {{ + yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_array); + if (tmp != NULL && YAJL_GET_ARRAY (tmp) != NULL) {{ - yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_string); - if (tmp != NULL) - {{ - ''', indent=1) + size_t i; + size_t len = YAJL_GET_ARRAY_NO_CHECK (tmp)->len; + yajl_val *values = YAJL_GET_ARRAY_NO_CHECK (tmp)->values; + ret->{obj.fixname}_len = len; + ''', indent=1) - if obj.doublearray: - emit(c_file, f''' - yajl_val *items = YAJL_GET_ARRAY_NO_CHECK(tmp)->values; - ret->{obj.fixname} = calloc ( YAJL_GET_ARRAY_NO_CHECK(tmp)->len + 1, sizeof (*ret->{obj.fixname})); - ''', indent=4) - null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=4) - emit(c_file, ''' - size_t j; - for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(tmp)->len; j++) - { - char *str = YAJL_GET_STRING (itmes[j]); - ''', indent=4) - emit(c_file, f''' - ret->{obj.fixname}[j] = (uint8_t *)strdup (str ? str : ""); - ''', indent=5) - null_check_return(c_file, f'ret->{obj.fixname}[j]', indent=5) - emit(c_file, ''' - }; - ''', indent=5) - else: - emit(c_file, ''' - char *str = YAJL_GET_STRING (tmp); - ''', indent=3) - emit(c_file, f''' - ret->{obj.fixname} = (uint8_t *)strdup (str ? str : ""); - ''', indent=3) - null_check_return(c_file, f'ret->{obj.fixname}', indent=3) - emit(c_file, f''' - ret->{obj.fixname}_len = str != NULL ? strlen (str) : 0; - ''', indent=3) + calloc_with_check(c_file, f'ret->{obj.fixname}', 'len + 1', f'*ret->{obj.fixname}', indent=3) + if obj.doublearray: + calloc_with_check(c_file, f'ret->{obj.fixname}_item_lens', 'len + 1', 'size_t', indent=3) + + emit(c_file, ''' + for (i = 0; i < len; i++) + { + yajl_val val = values[i]; + ''', indent=3) + if obj.doublearray: + emit(c_file, f''' + size_t j; + ret->{obj.fixname}[i] = calloc ( YAJL_GET_ARRAY_NO_CHECK(val)->len + 1, sizeof (**ret->{obj.fixname})); + ''', indent=4) + null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=4) emit(c_file, ''' - } - } - while (0); - ''', indent=1) + yajl_val *items = YAJL_GET_ARRAY_NO_CHECK(val)->values; + for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(val)->len; j++) + { + ''', indent=4) + emit(c_file, f''' + ret->{obj.fixname}[i][j] = make_{typename} (items[j], ctx, err); + ''', indent=5) + null_check_return(c_file, f'ret->{obj.fixname}[i][j]', indent=5) + emit(c_file, f''' + ret->{obj.fixname}_item_lens[i] += 1; + }}; + ''', indent=5) else: emit(c_file, f''' - do - {{ - yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_array); - if (tmp != NULL && YAJL_GET_ARRAY (tmp) != NULL) - {{ - size_t i; - size_t len = YAJL_GET_ARRAY_NO_CHECK (tmp)->len; - yajl_val *values = YAJL_GET_ARRAY_NO_CHECK (tmp)->values; - ret->{obj.fixname}_len = len; - ''', indent=1) + ret->{obj.fixname}[i] = make_{typename} (val, ctx, err); + ''', indent=4) + null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=4) - calloc_with_check(c_file, f'ret->{obj.fixname}', 'len + 1', f'*ret->{obj.fixname}', indent=3) - if obj.doublearray: - calloc_with_check(c_file, f'ret->{obj.fixname}_item_lens', 'len + 1', 'size_t', indent=3) + emit(c_file, ''' + } + } + } + while (0); + ''', indent=1) - emit(c_file, ''' - for (i = 0; i < len; i++) - { - ''', indent=3) + def emit_generate(self, c_file, obj, prefix): + typename = obj.subtypname if obj.subtypname else helpers.get_name_substr(obj.name, prefix) - if obj.doublearray: - 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})); - ''', indent=4) - null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=5) - emit(c_file, ''' - size_t j; - for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(values[i])->len; j++) - { - ''', indent=4) - read_val_generator(c_file, 5, 'items[j]', \ - f"ret->{obj.fixname}[i][j]", obj.subtyp, obj.origname, obj_typename) - emit(c_file, f''' - ret->{obj.fixname}_item_lens[i] += 1; - }}; - ''', indent=5) - else: - read_val_generator(c_file, 4, 'values[i]', \ - f"ret->{obj.fixname}[i]", obj.subtyp, obj.origname, obj_typename) + 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(c_file, ''' + for (i = 0; i < len; i++) + { + ''', indent=2) + + if obj.doublearray: + emit_gen_array_open(c_file, indent=3) + check_gen_status(c_file, indent=3) + emit(c_file, f''' + size_t j; + for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++) + {{ + stat = gen_{typename} (g, ptr->{obj.fixname}[i][j], ctx, err); + ''', indent=3) + check_gen_status(c_file, indent=4) emit(c_file, ''' - } - } } - while (0); - ''', indent=1) + ''', indent=4) + emit_gen_array_close(c_file, indent=3) + else: + emit(c_file, f''' + stat = gen_{typename} (g, ptr->{obj.fixname}[i], ctx, err); + ''', indent=3) + check_gen_status(c_file, indent=3) - def emit_generate(self, c_file, obj, prefix, indent=1): - """Generate C code to serialize an array to JSON.""" - if obj.subtypobj or obj.subtyp == 'object': - typename = obj.subtypname if obj.subtypname else helpers.get_name_substr(obj.name, prefix) + emit(c_file, ''' + } + ''', indent=2) + emit_gen_array_close(c_file, indent=2) + emit_beautify_on(c_file, '!len', indent=2) + check_gen_status(c_file, indent=2) - emit(c_file, f''' - if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) + emit(c_file, ''' + } + ''', indent=1) + + def emit_free(self, c_file, obj, prefix): + free_func = obj.subtypname if obj.subtypname is not None else helpers.get_name_substr(obj.name, prefix) + + emit(c_file, f''' + if (ptr->{obj.fixname} != NULL) + {{ + size_t i; + for (i = 0; i < ptr->{obj.fixname}_len; i++) {{ - size_t len = 0, i; - ''', indent=1) - emit_gen_key_with_check(c_file, obj.origname, indent=2) + ''', indent=1) + if obj.doublearray: emit(c_file, f''' - if (ptr != NULL && ptr->{obj.fixname} != NULL) - len = ptr->{obj.fixname}_len; + size_t j; + for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++) + {{ + free_{free_func} (ptr->{obj.fixname}[i][j]); + ptr->{obj.fixname}[i][j] = NULL; + }} ''', 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(c_file, ''' - for (i = 0; i < len; i++) - { + free_and_null(c_file, "ptr", f"{obj.fixname}[i]", indent=2) + else: + emit(c_file, f''' + if (ptr->{obj.fixname}[i] != NULL) + {{ + free_{free_func} (ptr->{obj.fixname}[i]); + ptr->{obj.fixname}[i] = NULL; + }} ''', indent=2) - if obj.doublearray: - emit_gen_array_open(c_file, indent=3) - check_gen_status(c_file, indent=3) - emit(c_file, f''' - size_t j; - for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++) - {{ - stat = gen_{typename} (g, ptr->{obj.fixname}[i][j], ctx, err); - ''', indent=3) - check_gen_status(c_file, indent=4) - emit(c_file, ''' - } - ''', indent=4) - emit_gen_array_close(c_file, indent=3) - else: - emit(c_file, f''' - stat = gen_{typename} (g, ptr->{obj.fixname}[i], ctx, err); - ''', indent=3) - check_gen_status(c_file, indent=3) + emit(c_file, ''' + } + ''', indent=2) - emit(c_file, ''' - } - ''', indent=2) - emit_gen_array_close(c_file, indent=2) - emit_beautify_on(c_file, '!len', indent=2) - check_gen_status(c_file, indent=2) + if obj.doublearray: + free_and_null(c_file, "ptr", f"{obj.fixname}_item_lens", indent=2) - emit(c_file, ''' - } - ''', indent=1) - elif obj.subtyp == 'byte': - emit(c_file, f''' - if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL && ptr->{obj.fixname}_len)) - {{ - const char *str = ""; - size_t len = 0; - ''', indent=1) - emit_gen_key_with_check(c_file, obj.origname, indent=2) + free_and_null(c_file, "ptr", obj.fixname, indent=2) - if obj.doublearray: - emit_gen_array_open(c_file, indent=3) - check_gen_status(c_file, indent=3) - emit(c_file, f''' - {{ - size_t i; - for (i = 0; i < ptr->{obj.fixname}_len; i++) - {{ - if (ptr->{obj.fixname}[i] != NULL) - str = (const char *)ptr->{obj.fixname}[i]; - else () - str = ""; - stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)str, strlen(str)); - }} - }} - ''', indent=2) - emit_gen_array_close(c_file, indent=2) - else: - emit(c_file, f''' - if (ptr != NULL && ptr->{obj.fixname} != NULL) - {{ - str = (const char *)ptr->{obj.fixname}; - len = ptr->{obj.fixname}_len; - }} - stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)str, len); - ''', indent=2) + emit(c_file, ''' + } + ''', indent=1) - check_gen_status(c_file, indent=2) + def emit_clone(self, c_file, obj, prefix, indent): + typename = helpers.get_prefixed_name(obj.name, prefix) + if obj.subtypname is not None: + typename = obj.subtypname + maybe_element = "_element" if obj.subtypname is None else "" - emit(c_file, ''' - } - ''', indent=1) + if obj.doublearray: + emit(c_file, f''' + ret->{obj.fixname}_item_lens[i] = src->{obj.fixname}_item_lens[i]; + ret->{obj.fixname}[i] = calloc (ret->{obj.fixname}_item_lens[i] + 1, sizeof (**ret->{obj.fixname}[i])); + if (ret->{obj.fixname}[i] == NULL) + return NULL; + for (size_t j = 0; j < src->{obj.fixname}_item_lens[i]; j++) + {{ + ret->{obj.fixname}[i][j] = clone_{typename}{maybe_element} (src->{obj.fixname}[i][j]); + if (ret->{obj.fixname}[i][j] == NULL) + return NULL; + }} + ''', indent=indent+2) else: emit(c_file, f''' - if ((ctx->options & OPT_GEN_KEY_VALUE) || (ptr != NULL && ptr->{obj.fixname} != NULL)) + ret->{obj.fixname}[i] = clone_{typename}{maybe_element} (src->{obj.fixname}[i]); + if (ret->{obj.fixname}[i] == NULL) + return NULL; + ''', indent=indent+2) + + +class ByteArrayHandler(ArraySubtypeHandler): + """Handler for byte arrays.""" + + 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); + if (tmp != NULL) {{ - size_t len = 0, i; - ''', indent=1) - emit_gen_key_with_check(c_file, obj.origname, indent=2) + ''', indent=1) + if obj.doublearray: 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) - + yajl_val *items = YAJL_GET_ARRAY_NO_CHECK(tmp)->values; + ret->{obj.fixname} = calloc ( YAJL_GET_ARRAY_NO_CHECK(tmp)->len + 1, sizeof (*ret->{obj.fixname})); + ''', indent=4) + null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=4) emit(c_file, ''' - for (i = 0; i < len; i++) + size_t j; + for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(tmp)->len; j++) { - ''', indent=2) - - if obj.doublearray: - typename = helpers.get_map_c_types(obj.subtyp) - emit_gen_array_open(c_file, indent=3) - check_gen_status(c_file, indent=3) - emit(c_file, f''' - size_t j; - for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++) - {{ - ''', indent=3) - json_value_generator(c_file, 4, f"ptr->{obj.fixname}[i][j]", 'g', 'ctx', obj.subtyp) - emit(c_file, ''' - } - ''', indent=4) - emit_gen_array_close(c_file, indent=3) - else: - json_value_generator(c_file, 3, f"ptr->{obj.fixname}[i]", 'g', 'ctx', obj.subtyp) - + char *str = YAJL_GET_STRING (itmes[j]); + ''', indent=4) + emit(c_file, f''' + ret->{obj.fixname}[j] = (uint8_t *)strdup (str ? str : ""); + ''', indent=5) + null_check_return(c_file, f'ret->{obj.fixname}[j]', indent=5) emit(c_file, ''' - } - ''', indent=2) - emit_gen_array_close(c_file, indent=2) - check_gen_status(c_file, indent=2) - emit_beautify_on(c_file, '!len', indent=2) + }; + ''', indent=5) + else: emit(c_file, ''' + char *str = YAJL_GET_STRING (tmp); + ''', indent=3) + emit(c_file, f''' + ret->{obj.fixname} = (uint8_t *)strdup (str ? str : ""); + ''', indent=3) + null_check_return(c_file, f'ret->{obj.fixname}', indent=3) + emit(c_file, f''' + ret->{obj.fixname}_len = str != NULL ? strlen (str) : 0; + ''', indent=3) + + emit(c_file, ''' + } } - ''', indent=2) + while (0); + ''', indent=1) - def emit_free(self, c_file, obj, prefix, indent=1): - """Generate C code to free an array.""" - if helpers.valid_basic_map_name(obj.subtyp): - free_func = helpers.make_basic_map_name(obj.subtyp) + 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 && ptr->{obj.fixname}_len)) + {{ + const char *str = ""; + size_t len = 0; + ''', indent=1) + emit_gen_key_with_check(c_file, obj.origname, indent=2) + + if obj.doublearray: + emit_gen_array_open(c_file, indent=3) + check_gen_status(c_file, indent=3) emit(c_file, f''' - if (ptr->{obj.fixname} != NULL) - {{ + {{ size_t i; for (i = 0; i < ptr->{obj.fixname}_len; i++) {{ if (ptr->{obj.fixname}[i] != NULL) - {{ - free_{free_func} (ptr->{obj.fixname}[i]); - ptr->{obj.fixname}[i] = NULL; - }} + str = (const char *)ptr->{obj.fixname}[i]; + else () + str = ""; + stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)str, strlen(str)); }} - ''', indent=1) - free_and_null(c_file, "ptr", obj.fixname, indent=2) - emit(c_file, ''' - } - ''', indent=1) - elif obj.subtyp == 'string': + }} + ''', indent=2) + emit_gen_array_close(c_file, indent=2) + else: emit(c_file, f''' - if (ptr->{obj.fixname} != NULL) + if (ptr != NULL && ptr->{obj.fixname} != NULL) + {{ + str = (const char *)ptr->{obj.fixname}; + len = ptr->{obj.fixname}_len; + }} + stat = yajl_gen_string ((yajl_gen) g, (const unsigned char *)str, len); + ''', indent=2) + + check_gen_status(c_file, indent=2) + + emit(c_file, ''' + } + ''', indent=1) + + def emit_free(self, c_file, obj, prefix): + # Byte arrays use the primitive array free path + pass + + def emit_clone(self, c_file, obj, prefix, indent): + # Byte arrays use primitive clone (just copy) + emit(c_file, f''' + ret->{obj.fixname}[i] = src->{obj.fixname}[i]; + ''', indent=indent+2) + + +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 + {{ + yajl_val tmp = get_val (tree, "{obj.origname}", yajl_t_array); + if (tmp != NULL && YAJL_GET_ARRAY (tmp) != NULL) {{ size_t i; - for (i = 0; i < ptr->{obj.fixname}_len; i++) - {{ - ''', indent=1) + size_t len = YAJL_GET_ARRAY_NO_CHECK (tmp)->len; + yajl_val *values = YAJL_GET_ARRAY_NO_CHECK (tmp)->values; + ret->{obj.fixname}_len = len; + ''', indent=1) - if obj.doublearray: - emit(c_file, f''' + calloc_with_check(c_file, f'ret->{obj.fixname}', 'len + 1', f'*ret->{obj.fixname}', indent=3) + if obj.doublearray: + calloc_with_check(c_file, f'ret->{obj.fixname}_item_lens', 'len + 1', 'size_t', indent=3) + + emit(c_file, ''' + for (i = 0; i < len; i++) + { + ''', indent=3) + + if obj.doublearray: + 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})); + ''', indent=4) + null_check_return(c_file, f'ret->{obj.fixname}[i]', indent=5) + emit(c_file, ''' size_t j; - for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++) - {{ - ''', indent=3) - free_and_null(c_file, "ptr", f"{obj.fixname}[i][j]", indent=4) - emit(c_file, ''' + for (j = 0; j < YAJL_GET_ARRAY_NO_CHECK(values[i])->len; j++) + { + ''', indent=4) + read_val_generator(c_file, 5, 'items[j]', + f"ret->{obj.fixname}[i][j]", obj.subtyp, obj.origname, obj_typename) + emit(c_file, f''' + ret->{obj.fixname}_item_lens[i] += 1; + }}; + ''', indent=5) + else: + read_val_generator(c_file, 4, 'values[i]', + f"ret->{obj.fixname}[i]", obj.subtyp, obj.origname, obj_typename) + + emit(c_file, ''' } - ''', indent=3) + } + } + while (0); + ''', 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(c_file, ''' + for (i = 0; i < len; i++) + { + ''', indent=2) + + if obj.doublearray: + emit_gen_array_open(c_file, indent=3) + check_gen_status(c_file, indent=3) emit(c_file, f''' - if (ptr->{obj.fixname}[i] != NULL) - {{ + size_t j; + for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++) + {{ ''', indent=3) + json_value_generator(c_file, 4, f"ptr->{obj.fixname}[i][j]", 'g', 'ctx', obj.subtyp) + emit(c_file, ''' + } + ''', indent=4) + emit_gen_array_close(c_file, indent=3) + else: + json_value_generator(c_file, 3, f"ptr->{obj.fixname}[i]", 'g', 'ctx', obj.subtyp) - free_and_null(c_file, "ptr", f"{obj.fixname}[i]", indent=4) + emit(c_file, ''' + } + ''', indent=2) + emit_gen_array_close(c_file, indent=2) + check_gen_status(c_file, indent=2) + emit_beautify_on(c_file, '!len', indent=2) + emit(c_file, ''' + } + ''', indent=2) + + def emit_free(self, c_file, obj, prefix): + if obj.subtyp == 'string': + self._emit_free_string(c_file, obj) + else: + self._emit_free_numeric(c_file, obj) + def _emit_free_string(self, c_file, obj): + emit(c_file, f''' + if (ptr->{obj.fixname} != NULL) + {{ + size_t i; + for (i = 0; i < ptr->{obj.fixname}_len; i++) + {{ + ''', indent=1) + + if obj.doublearray: + emit(c_file, f''' + size_t j; + for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++) + {{ + ''', indent=3) + free_and_null(c_file, "ptr", f"{obj.fixname}[i][j]", indent=4) emit(c_file, ''' - } } ''', indent=3) - if obj.doublearray: - free_and_null(c_file, "ptr", f"{obj.fixname}_item_lens", indent=2) + emit(c_file, f''' + if (ptr->{obj.fixname}[i] != NULL) + {{ + ''', indent=3) - free_and_null(c_file, "ptr", obj.fixname, indent=2) + free_and_null(c_file, "ptr", f"{obj.fixname}[i]", indent=4) - emit(c_file, ''' + emit(c_file, ''' + } } - ''', indent=1) - elif not helpers.is_compound_type(obj.subtyp): - emit(c_file, ''' - { - ''', indent=0) - if obj.doublearray: - emit(c_file, f''' - size_t i; - for (i = 0; i < ptr->{obj.fixname}_len; i++) - {{ - ''', indent=3) - free_and_null(c_file, "ptr", f"{obj.fixname}[i]", indent=4) - emit(c_file, ''' - } - ''', indent=3) - free_and_null(c_file, "ptr", f"{obj.fixname}_item_lens", indent=3) - free_and_null(c_file, "ptr", obj.fixname, indent=2) - emit(c_file, ''' - } - ''', indent=1) - elif obj.subtyp == 'object' or obj.subtypobj is not None: - free_func = obj.subtypname if obj.subtypname is not None else helpers.get_name_substr(obj.name, prefix) + ''', indent=3) + + if obj.doublearray: + free_and_null(c_file, "ptr", f"{obj.fixname}_item_lens", indent=2) + free_and_null(c_file, "ptr", obj.fixname, indent=2) + + emit(c_file, ''' + } + ''', indent=1) + + def _emit_free_numeric(self, c_file, obj): + emit(c_file, ''' + { + ''', indent=0) + if obj.doublearray: emit(c_file, f''' - if (ptr->{obj.fixname} != NULL) - {{ size_t i; for (i = 0; i < ptr->{obj.fixname}_len; i++) {{ - ''', indent=1) + ''', indent=3) + free_and_null(c_file, "ptr", f"{obj.fixname}[i]", indent=4) + emit(c_file, ''' + } + ''', indent=3) + free_and_null(c_file, "ptr", f"{obj.fixname}_item_lens", indent=3) + free_and_null(c_file, "ptr", obj.fixname, indent=2) + emit(c_file, ''' + } + ''', indent=1) - if obj.doublearray: - emit(c_file, f''' - size_t j; - for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++) - {{ - free_{free_func} (ptr->{obj.fixname}[i][j]); - ptr->{obj.fixname}[i][j] = NULL; + def emit_clone(self, c_file, obj, prefix, indent): + if obj.subtyp == 'string': + self._emit_clone_string(c_file, obj, indent) + else: + # Numeric types - simple copy + emit(c_file, f''' + ret->{obj.fixname}[i] = src->{obj.fixname}[i]; + ''', indent=indent+2) + + def _emit_clone_string(self, c_file, obj, indent): + if obj.doublearray: + emit(c_file, f''' + ret->{obj.fixname}[i] = calloc (ret->{obj.fixname}_item_lens[i] + 1, sizeof (**ret->{obj.fixname}[i])); + if (ret->{obj.fixname}[i] == NULL) + return NULL; + for (size_t j = 0; j < src->{obj.fixname}_item_lens[i]; j++) + {{ + ret->{obj.fixname}[i][j] = strdup (src->{obj.fixname}[i][j]); + if (ret->{obj.fixname}[i][j] == NULL) + return NULL; + }} + ''', indent=indent+2) + else: + emit(c_file, f''' + if (src->{obj.fixname}[i]) + {{ + ret->{obj.fixname}[i] = strdup (src->{obj.fixname}[i]); + if (ret->{obj.fixname}[i] == NULL) + return NULL; + }} + ''', indent=indent+2) + + +class BasicMapArrayHandler(ArraySubtypeHandler): + """Handler for arrays of basic map types.""" + + def emit_free(self, c_file, obj, prefix): + free_func = helpers.make_basic_map_name(obj.subtyp) + emit(c_file, f''' + if (ptr->{obj.fixname} != NULL) + {{ + size_t i; + for (i = 0; i < ptr->{obj.fixname}_len; i++) + {{ + if (ptr->{obj.fixname}[i] != NULL) + {{ + free_{free_func} (ptr->{obj.fixname}[i]); + ptr->{obj.fixname}[i] = NULL; }} - ''', indent=2) - free_and_null(c_file, "ptr", f"{obj.fixname}[i]", indent=2) - else: - emit(c_file, f''' - if (ptr->{obj.fixname}[i] != NULL) - {{ - free_{free_func} (ptr->{obj.fixname}[i]); - ptr->{obj.fixname}[i] = NULL; - }} - ''', indent=2) + }} + ''', indent=1) + free_and_null(c_file, "ptr", obj.fixname, indent=2) + emit(c_file, ''' + } + ''', indent=1) - emit(c_file, ''' - } - ''', indent=2) - if obj.doublearray: - free_and_null(c_file, "ptr", f"{obj.fixname}_item_lens", indent=2) +def _get_array_subtype_handler(obj): + """Get the appropriate handler for an array's element type.""" + if helpers.valid_basic_map_name(obj.subtyp): + return BasicMapArrayHandler() + elif obj.subtypobj or obj.subtyp == 'object': + return ObjectArrayHandler() + elif obj.subtyp == 'byte': + return ByteArrayHandler() + else: + return PrimitiveArrayHandler() - free_and_null(c_file, "ptr", obj.fixname, indent=2) - emit(c_file, ''' - } - ''', indent=1) +class ArrayType(TypeHandler): + """Handler for array type. + Delegates to specialized ArraySubtypeHandler classes based on element type. + """ + + def emit_parse(self, c_file, obj, prefix, obj_typename, indent=1): + """Generate C code to parse an array from JSON.""" + handler = _get_array_subtype_handler(obj) + handler.emit_parse(c_file, obj, prefix, obj_typename) + + def emit_generate(self, c_file, obj, prefix, indent=1): + """Generate C code to serialize an array to JSON.""" + handler = _get_array_subtype_handler(obj) + handler.emit_generate(c_file, obj, prefix) + + def emit_free(self, c_file, obj, prefix, indent=1): + """Generate C code to free an array.""" + handler = _get_array_subtype_handler(obj) + handler.emit_free(c_file, obj, prefix) + + # Handle additional cleanup for some array types c_typ = helpers.obtain_pointer(obj.name, obj.subtypobj, prefix) if c_typ == "": return @@ -1331,62 +1470,17 @@ def emit_clone(self, c_file, obj, prefix, indent=1): {{ ''', indent=indent) + handler = _get_array_subtype_handler(obj) if helpers.is_numeric_type(obj.subtyp) or obj.subtyp == 'boolean': emit(c_file, f''' - ret->{obj.fixname}[i] = src->{obj.fixname}[i]; + ret->{obj.fixname}[i] = src->{obj.fixname}[i]; ''', indent=indent+2) - elif obj.subtyp == 'object': - typename = helpers.get_prefixed_name(obj.name, prefix) - if obj.subtypname is not None: - typename = obj.subtypname - maybe_element = "_element" if obj.subtypname is None else "" - if obj.doublearray: - emit(c_file, f''' - ret->{obj.fixname}_item_lens[i] = src->{obj.fixname}_item_lens[i]; - ret->{obj.fixname}[i] = calloc (ret->{obj.fixname}_item_lens[i] + 1, sizeof (**ret->{obj.fixname}[i])); - if (ret->{obj.fixname}[i] == NULL) - return NULL; - for (size_t j = 0; j < src->{obj.fixname}_item_lens[i]; j++) - {{ - ret->{obj.fixname}[i][j] = clone_{typename}{maybe_element} (src->{obj.fixname}[i][j]); - if (ret->{obj.fixname}[i][j] == NULL) - return NULL; - }} - ''', indent=indent+2) - else: - emit(c_file, f''' - ret->{obj.fixname}[i] = clone_{typename}{maybe_element} (src->{obj.fixname}[i]); - if (ret->{obj.fixname}[i] == NULL) - return NULL; - ''', indent=indent+2) - elif obj.subtyp == 'string': - if obj.doublearray: - emit(c_file, f''' - ret->{obj.fixname}[i] = calloc (ret->{obj.fixname}_item_lens[i] + 1, sizeof (**ret->{obj.fixname}[i])); - if (ret->{obj.fixname}[i] == NULL) - return NULL; - for (size_t j = 0; j < src->{obj.fixname}_item_lens[i]; j++) - {{ - ret->{obj.fixname}[i][j] = strdup (src->{obj.fixname}[i][j]); - if (ret->{obj.fixname}[i][j] == NULL) - return NULL; - }} - ''', indent=indent+2) - else: - emit(c_file, f''' - if (src->{obj.fixname}[i]) - {{ - ret->{obj.fixname}[i] = strdup (src->{obj.fixname}[i]); - if (ret->{obj.fixname}[i] == NULL) - return NULL; - }} - ''', indent=indent+2) else: - raise Exception("Unimplemented type for array clone: %s (%s)" % (obj.subtyp, obj.subtypname)) + handler.emit_clone(c_file, obj, prefix, indent) emit(c_file, f''' - }} }} + }} ''', indent=indent+1) @@ -1400,7 +1494,6 @@ def emit_clone(self, c_file, obj, prefix, indent=1): 'array': ArrayType(), } - def get_type_handler(typ): """Get the appropriate TypeHandler for a given type. From 7d7e02c5e54792696f73e6a2ff6e1db1ec00dd2b Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 22:01:16 +0000 Subject: [PATCH 50/51] source: add struct-level methods to TypeHandler classes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add emit_make_body(), emit_gen_body(), emit_free_body(), and emit_clone_body() methods to ObjectType, MapStringObjectType, and ArrayType classes. These methods encapsulate the struct-level code generation logic that was previously scattered across orchestrator functions. Refactor parse_json_to_c(), get_c_json(), make_clone(), and make_c_free() to delegate to these new methods, simplifying the orchestrator functions to just handle function signature generation and the common prologue/epilogue code. Remove the now-unused parse_map_string_obj(), parse_obj_arr_obj(), and get_map_string_obj() helper functions since their logic is now encapsulated in the TypeHandler classes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/sources.py | 678 +++++++++++++++++++++++------------------ 1 file changed, 381 insertions(+), 297 deletions(-) diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 7ee2f772..3affbd8d 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -730,12 +730,153 @@ def emit_free(self, c_file, obj, prefix, indent=1): ''', indent=indent) def emit_clone(self, c_file, obj, prefix, indent=1): - # Intentionally empty: object cloning is handled directly in make_clone() - # because it requires per-type context (typename resolution) that the - # handler dispatch pattern doesn't provide. The actual cloning logic - # for object fields is emitted by make_clone() at lines 1725-1745. + # Intentionally empty: object field cloning is handled explicitly in + # emit_clone_body() because it requires typename resolution that the + # simple handler dispatch pattern doesn't provide. pass + def emit_make_body(self, c_file, obj, prefix): + """Generate the body of make_typename() for objects.""" + obj_typename = helpers.get_prefixed_name(obj.name, prefix) + nodes = obj.children + required_to_check = [] + for i in nodes or []: + if obj.required and i.origname in obj.required and \ + not helpers.is_numeric_type(i.typ) and i.typ != 'boolean': + required_to_check.append(i) + handler = get_type_handler(i.typ) + if handler: + handler.emit_parse(c_file, i, prefix, obj_typename, indent=1) + + for i in required_to_check: + emit(c_file, f''' + if (ret->{i.fixname} == NULL) + {{ + ''', indent=1) + emit_asprintf_error(c_file, 'err', "Required field '%s' not present", f'"{i.origname}"', indent=2) + emit(c_file, ''' + return NULL; + } + ''', indent=1) + + 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]) + emit(c_file, f''' + if (tree->type == yajl_t_object) + {{ + size_t i; + size_t j = 0; + size_t cnt = tree->u.object.len; + yajl_val resi = NULL; + + if (ctx->options & OPT_PARSE_FULLKEY) + {{ + resi = calloc (1, sizeof(*tree)); + 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) + {{ + yajl_tree_free (resi); + return NULL; + }} + resi->u.object.values = calloc (cnt, sizeof (yajl_val)); + if (resi->u.object.values == NULL) + {{ + yajl_tree_free (resi); + return NULL; + }} + }} + + for (i = 0; i < tree->u.object.len; 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++; + }} + j++; + }} + }} + + if ((ctx->options & OPT_PARSE_STRICT) && j > 0 && ctx->errfile != NULL) + (void) fprintf (ctx->errfile, "WARNING: unknown key found\\n"); + + if (ctx->options & OPT_PARSE_FULLKEY) + ret->_residual = resi; + }} + ''', indent=1) + + def emit_gen_body(self, c_file, obj, prefix): + """Generate the body of gen_typename() for objects.""" + nodes = obj.children + if nodes is None: + emit_beautify_off(c_file, 'true', indent=1) + + emit_gen_map_open(c_file, indent=1) + check_gen_status(c_file, indent=1) + for i in nodes or []: + handler = get_type_handler(i.typ) + if handler: + handler.emit_generate(c_file, i, prefix, indent=1) + if obj.children is not None: + emit(c_file, ''' + if (ptr != NULL && ptr->_residual != NULL) + { + stat = gen_yajl_object_residual (ptr->_residual, g, err); + if (yajl_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) + if nodes is None: + emit_beautify_on(c_file, 'true', indent=1) + + def emit_free_body(self, c_file, obj, prefix): + """Generate the body of free_typename() for objects.""" + objs = obj.children + for i in objs or []: + handler = get_type_handler(i.typ) + if handler: + handler.emit_free(c_file, i, prefix, indent=1) + + if obj.children is not None: + emit(c_file, ''' + yajl_tree_free (ptr->_residual); + ptr->_residual = NULL; + ''', indent=1) + + def emit_clone_body(self, c_file, obj, prefix): + """Generate the body of clone_typename() for objects.""" + nodes = obj.children + for i in nodes or []: + handler = get_type_handler(i.typ) + # Object type needs parent context (mapStringObject vs regular object) + # so we handle it explicitly below rather than via handler + if handler and i.typ != 'object': + handler.emit_clone(c_file, i, prefix, indent=1) + elif i.typ == 'object': + node_name = i.subtypname or helpers.get_prefixed_name(i.name, prefix) + emit(c_file, f''' + if (src->{i.fixname}) + {{ + ret->{i.fixname} = clone_{node_name} (src->{i.fixname}); + if (ret->{i.fixname} == NULL) + return NULL; + }} + ''', indent=1) + else: + raise Exception("Unimplemented type for clone: %s" % i.typ) + class MapStringObjectType(TypeHandler): """Handler for mapStringObject type.""" @@ -802,6 +943,154 @@ def emit_clone(self, c_file, obj, prefix, indent=1): }} ''', indent=indent) + def emit_make_body(self, c_file, obj, prefix): + """Generate the body of make_typename() for mapStringObject.""" + child = obj.children[0] + if helpers.valid_basic_map_name(child.typ): + childname = helpers.make_basic_map_name(child.typ) + else: + if child.subtypname: + childname = child.subtypname + else: + childname = helpers.get_prefixed_name(child.name, prefix) + + emit(c_file, f''' + if (YAJL_GET_OBJECT (tree) != NULL) + {{ + 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; + ret->len = len; + ''', indent=1) + + calloc_with_check(c_file, 'ret->keys', 'len + 1', '*ret->keys', indent=2) + calloc_with_check(c_file, f'ret->{child.fixname}', 'len + 1', f'*ret->{child.fixname}', indent=2) + + emit(c_file, f''' + for (i = 0; i < len; i++) + {{ + yajl_val val; + const char *tmpkey = keys[i]; + ret->keys[i] = strdup (tmpkey ? tmpkey : ""); + ''', indent=2) + + null_check_return(c_file, 'ret->keys[i]', indent=3) + + emit(c_file, f''' + val = values[i]; + ret->{child.fixname}[i] = make_{childname} (val, ctx, err); + ''', indent=3) + + null_check_return(c_file, f'ret->{child.fixname}[i]', indent=3) + + c_file.append(' }\n') + c_file.append(' }\n') + + def emit_gen_body(self, c_file, obj, prefix): + """Generate the body of gen_typename() for mapStringObject.""" + child = obj.children[0] + if helpers.valid_basic_map_name(child.typ): + childname = helpers.make_basic_map_name(child.typ) + else: + if child.subtypname: + childname = child.subtypname + else: + childname = helpers.get_prefixed_name(child.name, prefix) + + emit(c_file, ''' + size_t len = 0, i; + if (ptr != NULL) + len = ptr->len; + ''', indent=1) + emit_beautify_off(c_file, '!len', indent=1) + emit_gen_map_open(c_file, indent=1) + check_gen_status(c_file, indent=1) + + emit(c_file, f''' + if (len || (ptr != NULL && ptr->keys != NULL && ptr->{child.fixname} != NULL)) + {{ + 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)); + ''', indent=1) + + check_gen_status(c_file, indent=3) + + emit(c_file, f''' + stat = gen_{childname} (g, ptr->{child.fixname}[i], ctx, err); + ''', indent=3) + + check_gen_status(c_file, indent=3) + + emit(c_file, ''' + } + } + ''', indent=2) + emit_gen_map_close(c_file, indent=1) + check_gen_status(c_file, indent=1) + emit_beautify_on(c_file, '!len', indent=1) + + def emit_free_body(self, c_file, obj, prefix): + """Generate the body of free_typename() for mapStringObject.""" + child = obj.children[0] + if helpers.valid_basic_map_name(child.typ): + childname = helpers.make_basic_map_name(child.typ) + else: + if child.subtypname: + childname = child.subtypname + else: + childname = helpers.get_prefixed_name(child.name, prefix) + emit(c_file, f''' + if (ptr->keys != NULL && ptr->{child.fixname} != NULL) + {{ + size_t i; + for (i = 0; i < ptr->len; i++) + {{ + ''', indent=1) + + free_and_null(c_file, "ptr", "keys[i]", indent=3) + + emit(c_file, f''' + free_{childname} (ptr->{child.fixname}[i]); + ptr->{child.fixname}[i] = NULL; + }} + ''', indent=3) + + free_and_null(c_file, "ptr", "keys", indent=2) + free_and_null(c_file, "ptr", child.fixname, indent=2) + + emit(c_file, ''' + } + ''', indent=1) + + def emit_clone_body(self, c_file, obj, prefix): + """Generate the body of clone_typename() for mapStringObject.""" + nodes = obj.children + for i in nodes or []: + handler = get_type_handler(i.typ) + # Object type needs parent context for mapStringObject + if handler and i.typ != 'object': + handler.emit_clone(c_file, i, prefix, indent=1) + elif i.typ == 'object': + node_name = i.subtypname or helpers.get_prefixed_name(i.name, prefix) + emit(c_file, f''' + if (src->{i.fixname}) + {{ + size_t i; + ret->{i.fixname} = calloc (src->len + 1, sizeof (*ret->{i.fixname})); + for (i = 0; i < src->len; i++) + {{ + ret->{i.fixname}[i] = clone_{node_name} (src->{i.fixname}[i]); + if (ret->{i.fixname}[i] == NULL) + return NULL; + }} + }} + ''', indent=1) + else: + raise Exception("Unimplemented type for clone: %s" % i.typ) + class BasicMapType(TypeHandler): """Handler for basic map types (mapStringString, mapStringInt, etc.).""" @@ -1483,6 +1772,76 @@ def emit_clone(self, c_file, obj, prefix, indent=1): }} ''', indent=indent+1) + def emit_make_body(self, c_file, obj, prefix): + """Generate the body of make_typename() for array subtypes (element structs).""" + obj_typename = helpers.get_name_substr(obj.name, prefix) + nodes = obj.subtypobj + required_to_check = [] + for i in nodes or []: + if obj.required and i.origname in obj.required and \ + not helpers.is_numeric_type(i.typ) and i.typ != 'boolean': + required_to_check.append(i) + handler = get_type_handler(i.typ) + if handler: + handler.emit_parse(c_file, i, prefix, obj_typename, indent=1) + + for i in required_to_check: + emit(c_file, f''' + if (ret->{i.fixname} == NULL) + {{ + ''', indent=1) + emit_asprintf_error(c_file, 'err', "Required field '%s' not present", f'"{i.origname}"', indent=2) + emit(c_file, ''' + return NULL; + } + ''', indent=1) + + def emit_gen_body(self, c_file, obj, prefix): + """Generate the body of gen_typename() for array subtypes.""" + nodes = obj.subtypobj + if nodes is None: + emit_beautify_off(c_file, 'true', indent=1) + + emit_gen_map_open(c_file, indent=1) + check_gen_status(c_file, indent=1) + for i in nodes or []: + handler = get_type_handler(i.typ) + if handler: + handler.emit_generate(c_file, i, prefix, indent=1) + emit_gen_map_close(c_file, indent=1) + check_gen_status(c_file, indent=1) + if nodes is None: + emit_beautify_on(c_file, 'true', indent=1) + + def emit_free_body(self, c_file, obj, prefix): + """Generate the body of free_typename() for array subtypes.""" + objs = obj.subtypobj + for i in objs or []: + handler = get_type_handler(i.typ) + if handler: + handler.emit_free(c_file, i, prefix, indent=1) + + def emit_clone_body(self, c_file, obj, prefix): + """Generate the body of clone_typename() for array subtypes.""" + nodes = obj.subtypobj + for i in nodes or []: + handler = get_type_handler(i.typ) + # Object type needs parent context + if handler and i.typ != 'object': + handler.emit_clone(c_file, i, prefix, indent=1) + elif i.typ == 'object': + node_name = i.subtypname or helpers.get_prefixed_name(i.name, prefix) + emit(c_file, f''' + if (src->{i.fixname}) + {{ + ret->{i.fixname} = clone_{node_name} (src->{i.fixname}); + if (ret->{i.fixname} == NULL) + return NULL; + }} + ''', indent=1) + else: + raise Exception("Unimplemented type for clone: %s" % i.typ) + # Type handler registry _TYPE_HANDLERS = { @@ -1525,138 +1884,6 @@ def append_c_code(obj, c_file, prefix): get_c_json(obj, c_file, prefix) make_clone(obj, c_file, prefix) -def parse_map_string_obj(obj, c_file, prefix, obj_typename): - """ - Description: generate c language for parse json map string object - Interface: None - History: 2019-06-17 - """ - child = obj.children[0] - if helpers.valid_basic_map_name(child.typ): - childname = helpers.make_basic_map_name(child.typ) - else: - if child.subtypname: - childname = child.subtypname - else: - childname = helpers.get_prefixed_name(child.name, prefix) - - emit(c_file, f''' - if (YAJL_GET_OBJECT (tree) != NULL) - {{ - 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; - ret->len = len; - ''', indent=1) - - calloc_with_check(c_file, 'ret->keys', 'len + 1', '*ret->keys', indent=2) - calloc_with_check(c_file, f'ret->{child.fixname}', 'len + 1', f'*ret->{child.fixname}', indent=2) - - emit(c_file, f''' - for (i = 0; i < len; i++) - {{ - yajl_val val; - const char *tmpkey = keys[i]; - ret->keys[i] = strdup (tmpkey ? tmpkey : ""); - ''', indent=2) - - null_check_return(c_file, 'ret->keys[i]', indent=3) - - emit(c_file, f''' - val = values[i]; - ret->{child.fixname}[i] = make_{childname} (val, ctx, err); - ''', indent=3) - - null_check_return(c_file, f'ret->{child.fixname}[i]', indent=3) - - c_file.append(' }\n') - c_file.append(' }\n') - - -def parse_obj_arr_obj(obj, c_file, prefix, obj_typename): - """ - Description: generate c language for parse object or array object - Interface: None - History: 2019-06-17 - """ - nodes = obj.children if obj.typ == 'object' else obj.subtypobj - required_to_check = [] - for i in nodes or []: - if obj.required and i.origname in obj.required and \ - not helpers.is_numeric_type(i.typ) and i.typ != 'boolean': - required_to_check.append(i) - handler = get_type_handler(i.typ) - if handler: - handler.emit_parse(c_file, i, prefix, obj_typename, indent=1) - - for i in required_to_check: - emit(c_file, f''' - if (ret->{i.fixname} == NULL) - {{ - ''', indent=1) - emit_asprintf_error(c_file, 'err', "Required field '%s' not present", f'"{i.origname}"', indent=2) - emit(c_file, ''' - return NULL; - } - ''', indent=1) - - if obj.typ == 'object' and 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]) - emit(c_file, f''' - if (tree->type == yajl_t_object) - {{ - size_t i; - size_t j = 0; - size_t cnt = tree->u.object.len; - yajl_val resi = NULL; - - if (ctx->options & OPT_PARSE_FULLKEY) - {{ - resi = calloc (1, sizeof(*tree)); - 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) - {{ - yajl_tree_free (resi); - return NULL; - }} - resi->u.object.values = calloc (cnt, sizeof (yajl_val)); - if (resi->u.object.values == NULL) - {{ - yajl_tree_free (resi); - return NULL; - }} - }} - - for (i = 0; i < tree->u.object.len; 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++; - }} - j++; - }} - }} - - if ((ctx->options & OPT_PARSE_STRICT) && j > 0 && ctx->errfile != NULL) - (void) fprintf (ctx->errfile, "WARNING: unknown key found\\n"); - - if (ctx->options & OPT_PARSE_FULLKEY) - ret->_residual = resi; - }} - ''', indent=1) - def parse_json_to_c(obj, c_file, prefix): """ @@ -1669,11 +1896,10 @@ def parse_json_to_c(obj, c_file, prefix): if obj.typ == 'object' or obj.typ == 'mapStringObject': if obj.subtypname: return - obj_typename = typename = helpers.get_prefixed_name(obj.name, prefix) + typename = helpers.get_prefixed_name(obj.name, prefix) if obj.typ == 'array': - obj_typename = typename = helpers.get_name_substr(obj.name, prefix) - objs = obj.subtypobj - if objs is None or obj.subtypname: + typename = helpers.get_name_substr(obj.name, prefix) + if obj.subtypobj is None or obj.subtypname: return emit(c_file, f''' define_cleaner_function ({typename} *, free_{typename}) @@ -1689,66 +1915,16 @@ def parse_json_to_c(obj, c_file, prefix): if (ret == NULL) return NULL; ''', indent=0) - if obj.typ == 'mapStringObject': - parse_map_string_obj(obj, c_file, prefix, obj_typename) - if obj.typ == 'object' or (obj.typ == 'array' and obj.subtypobj): - parse_obj_arr_obj(obj, c_file, prefix, obj_typename) + handler = get_type_handler(obj.typ) + if handler and hasattr(handler, 'emit_make_body'): + handler.emit_make_body(c_file, obj, prefix) + c_file.append(" return move_ptr (ret);\n") c_file.append("}\n") c_file.append("\n") -def get_map_string_obj(obj, c_file, prefix): - """ - Description: c language generate map string object - Interface: None - History: 2019-06-17 - """ - child = obj.children[0] - if helpers.valid_basic_map_name(child.typ): - childname = helpers.make_basic_map_name(child.typ) - else: - if child.subtypname: - childname = child.subtypname - else: - childname = helpers.get_prefixed_name(child.name, prefix) - - emit(c_file, ''' - size_t len = 0, i; - if (ptr != NULL) - len = ptr->len; - ''', indent=1) - emit_beautify_off(c_file, '!len', indent=1) - emit_gen_map_open(c_file, indent=1) - check_gen_status(c_file, indent=1) - - emit(c_file, f''' - if (len || (ptr != NULL && ptr->keys != NULL && ptr->{child.fixname} != NULL)) - {{ - 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)); - ''', indent=1) - - check_gen_status(c_file, indent=3) - - emit(c_file, f''' - stat = gen_{childname} (g, ptr->{child.fixname}[i], ctx, err); - ''', indent=3) - - check_gen_status(c_file, indent=3) - - emit(c_file, ''' - } - } - ''', indent=2) - emit_gen_map_close(c_file, indent=1) - check_gen_status(c_file, indent=1) - emit_beautify_on(c_file, '!len', indent=1) - - def get_c_json(obj, c_file, prefix): """ Description: c language generate json file @@ -1761,8 +1937,7 @@ def get_c_json(obj, c_file, prefix): typename = helpers.get_prefixed_name(obj.name, prefix) elif obj.typ == 'array': typename = helpers.get_name_substr(obj.name, prefix) - objs = obj.subtypobj - if objs is None: + if obj.subtypobj is None: return emit(c_file, f''' yajl_gen_status @@ -1772,33 +1947,11 @@ def get_c_json(obj, c_file, prefix): *err = NULL; (void) ptr; /* Silence compiler warning. */ ''', indent=0) - if obj.typ == 'mapStringObject': - get_map_string_obj(obj, c_file, prefix) - elif obj.typ == 'object' or (obj.typ == 'array' and obj.subtypobj): - nodes = obj.children if obj.typ == 'object' else obj.subtypobj - if nodes is None: - emit_beautify_off(c_file, 'true', indent=1) - emit_gen_map_open(c_file, indent=1) - check_gen_status(c_file, indent=1) - for i in nodes or []: - handler = get_type_handler(i.typ) - if handler: - handler.emit_generate(c_file, i, prefix, indent=1) - if obj.typ == 'object': - if obj.children is not None: - emit(c_file, ''' - if (ptr != NULL && ptr->_residual != NULL) - { - stat = gen_yajl_object_residual (ptr->_residual, g, err); - if (yajl_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) - if nodes is None: - emit_beautify_on(c_file, 'true', indent=1) + handler = get_type_handler(obj.typ) + 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("}\n") c_file.append("\n") @@ -1839,40 +1992,9 @@ def make_clone(obj, c_file, prefix): return NULL; ''', indent=0) - nodes = obj.children if obj.subtypobj is None else obj.subtypobj - for i in nodes or []: - handler = get_type_handler(i.typ) - # Object type needs parent context (mapStringObject vs regular object) - # so we handle it explicitly below rather than via handler - if handler and i.typ != 'object': - handler.emit_clone(c_file, i, prefix, indent=1) - elif i.typ == 'object': - node_name = i.subtypname or helpers.get_prefixed_name(i.name, prefix) - if obj.typ != 'mapStringObject': - emit(c_file, f''' - if (src->{i.fixname}) - {{ - ret->{i.fixname} = clone_{node_name} (src->{i.fixname}); - if (ret->{i.fixname} == NULL) - return NULL; - }} - ''', indent=1) - else: - emit(c_file, f''' - if (src->{i.fixname}) - {{ - size_t i; - ret->{i.fixname} = calloc (src->len + 1, sizeof (*ret->{i.fixname})); - for (i = 0; i < src->len; i++) - {{ - ret->{i.fixname}[i] = clone_{node_name} (src->{i.fixname}[i]); - if (ret->{i.fixname}[i] == NULL) - return NULL; - }} - }} - ''', indent=1) - else: - raise Exception("Unimplemented type for clone: %s" % i.typ) + handler = get_type_handler(obj.typ) + if handler and hasattr(handler, 'emit_clone_body'): + handler.emit_clone_body(c_file, obj, prefix) c_file.append(" return move_ptr (ret);\n") c_file.append("}\n") @@ -1908,48 +2030,10 @@ def make_c_free (obj, c_file, prefix): if (ptr == NULL) return; ''', indent=0) - if obj.typ == 'mapStringObject': - child = obj.children[0] - if helpers.valid_basic_map_name(child.typ): - childname = helpers.make_basic_map_name(child.typ) - else: - if child.subtypname: - childname = child.subtypname - else: - childname = helpers.get_prefixed_name(child.name, prefix) - emit(c_file, f''' - if (ptr->keys != NULL && ptr->{child.fixname} != NULL) - {{ - size_t i; - for (i = 0; i < ptr->len; i++) - {{ - ''', indent=1) - - free_and_null(c_file, "ptr", "keys[i]", indent=3) - - emit(c_file, f''' - free_{childname} (ptr->{child.fixname}[i]); - ptr->{child.fixname}[i] = NULL; - }} - ''', indent=3) - free_and_null(c_file, "ptr", "keys", indent=2) - free_and_null(c_file, "ptr", child.fixname, indent=2) - - emit(c_file, ''' - } - ''', indent=1) - for i in objs or []: - handler = get_type_handler(i.typ) - if handler: - handler.emit_free(c_file, i, prefix, indent=1) - - if obj.typ == 'object': - if obj.children is not None: - emit(c_file, ''' - yajl_tree_free (ptr->_residual); - ptr->_residual = NULL; - ''', indent=1) + handler = get_type_handler(obj.typ) + if handler and hasattr(handler, 'emit_free_body'): + handler.emit_free_body(c_file, obj, prefix) emit(c_file, ''' free (ptr); From a425a846056183585aa068e08a76bd4caf3b7e11 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 12 Dec 2025 22:07:37 +0000 Subject: [PATCH 51/51] source: rename doublearray to nested_array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the 'doublearray' attribute to 'nested_array' for clarity. The previous name was confusing as it could be mistaken for the 'double' floating-point type, when it actually indicates an array of arrays (2D/nested array structure). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 Signed-off-by: Giuseppe Scrivano --- src/ocispec/generate.py | 4 ++-- src/ocispec/headers.py | 8 +++---- src/ocispec/helpers.py | 4 ++-- src/ocispec/sources.py | 52 ++++++++++++++++++++--------------------- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/ocispec/generate.py b/src/ocispec/generate.py index 530d69d7..b33517f8 100755 --- a/src/ocispec/generate.py +++ b/src/ocispec/generate.py @@ -404,7 +404,7 @@ def gen_type_arr_typnode(node_info, src, typ, refname): subtyp=item_type.subtyp, subtypobj=item_type.subtypobj, subtypname=item_type.subtypname, - required=item_type.required, doublearray=True), src + required=item_type.required, nested_array=True), src else: return helpers.SchemaNode(name, typ, @@ -650,7 +650,7 @@ def parse_schema(schema_info, schema, prefix): item_type, _ = resolve_type(schema_info, helpers.HierarchicalName(""), \ schema['items'], schema['items'], schema_info.name.name) if item_type.typ == 'array' and not helpers.valid_basic_map_name(item_type.subtyp): - item_type.doublearray = True + item_type.nested_array = True return item_type else: return helpers.SchemaNode(helpers.HierarchicalName(prefix), 'array', None, item_type.typ, \ diff --git a/src/ocispec/headers.py b/src/ocispec/headers.py index f1c93120..cb1fc147 100755 --- a/src/ocispec/headers.py +++ b/src/ocispec/headers.py @@ -100,7 +100,7 @@ def append_header_child_arr(child, header, prefix): c_typ = helpers.get_prefixed_pointer(child.name, child.subtyp, prefix) dflag = "" - if child.doublearray: + if child.nested_array: dflag = "*" if helpers.valid_basic_map_name(child.subtyp): @@ -110,7 +110,7 @@ def append_header_child_arr(child, header, prefix): else: header.append(f" {c_typ}{' ' if '*' not in c_typ else ''}**{dflag}{child.fixname};\n") - if child.doublearray and not helpers.valid_basic_map_name(child.subtyp): + if child.nested_array and not helpers.valid_basic_map_name(child.subtyp): header.append(f" size_t *{child.fixname + '_item_lens'};\n") header.append(f" size_t {child.fixname + '_len'};\n\n") @@ -180,7 +180,7 @@ def header_reflect_top_array(obj, prefix, header): c_typ = helpers.get_prefixed_pointer(obj.name, obj.subtyp, prefix) or \ helpers.get_map_c_types(obj.subtyp) if obj.subtypobj is not None: - if obj.doublearray and obj.subtypname is not None: + if obj.nested_array and obj.subtypname is not None: c_typ = obj.subtypname + " *" else: c_typ = helpers.get_name_substr(obj.name, prefix) + " *" @@ -189,7 +189,7 @@ def header_reflect_top_array(obj, prefix, header): typename = helpers.get_top_array_type_name(obj.name, prefix) header.append("typedef struct {\n") - if obj.doublearray: + if obj.nested_array: header.append(f" {c_typ}{' ' if '*' not in c_typ else ''}**items;\n") header.append(" size_t *subitem_lens;\n\n") else: diff --git a/src/ocispec/helpers.py b/src/ocispec/helpers.py index ce480f1d..3dda137f 100755 --- a/src/ocispec/helpers.py +++ b/src/ocispec/helpers.py @@ -254,7 +254,7 @@ class SchemaNode: History: 2019-06-17 ''' def __init__(self, name, typ, children, subtyp=None, subtypobj=None, subtypname=None, \ - required=None, doublearray=False): + required=None, nested_array=False): self.typ = typ self.children = children self.subtyp = subtyp @@ -264,7 +264,7 @@ def __init__(self, name, typ, children, subtyp=None, subtypobj=None, subtypname= self.name = conv_to_c_style(name.name.replace('.', '_')) self.origname = name.leaf or name.name self.fixname = conv_to_c_style(self.origname.replace('.', '_')) - self.doublearray = doublearray + self.nested_array = nested_array diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 3affbd8d..ff82184d 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -1210,7 +1210,7 @@ def emit_parse(self, c_file, obj, prefix, obj_typename): ''', indent=1) calloc_with_check(c_file, f'ret->{obj.fixname}', 'len + 1', f'*ret->{obj.fixname}', indent=3) - if obj.doublearray: + if obj.nested_array: calloc_with_check(c_file, f'ret->{obj.fixname}_item_lens', 'len + 1', 'size_t', indent=3) emit(c_file, ''' @@ -1219,7 +1219,7 @@ def emit_parse(self, c_file, obj, prefix, obj_typename): yajl_val val = values[i]; ''', indent=3) - if obj.doublearray: + 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})); @@ -1274,7 +1274,7 @@ def emit_generate(self, c_file, obj, prefix): { ''', indent=2) - if obj.doublearray: + if obj.nested_array: emit_gen_array_open(c_file, indent=3) check_gen_status(c_file, indent=3) emit(c_file, f''' @@ -1316,7 +1316,7 @@ def emit_free(self, c_file, obj, prefix): {{ ''', indent=1) - if obj.doublearray: + if obj.nested_array: emit(c_file, f''' size_t j; for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++) @@ -1339,7 +1339,7 @@ def emit_free(self, c_file, obj, prefix): } ''', indent=2) - if obj.doublearray: + if obj.nested_array: free_and_null(c_file, "ptr", f"{obj.fixname}_item_lens", indent=2) free_and_null(c_file, "ptr", obj.fixname, indent=2) @@ -1354,7 +1354,7 @@ def emit_clone(self, c_file, obj, prefix, indent): typename = obj.subtypname maybe_element = "_element" if obj.subtypname is None else "" - if obj.doublearray: + if obj.nested_array: emit(c_file, f''' ret->{obj.fixname}_item_lens[i] = src->{obj.fixname}_item_lens[i]; ret->{obj.fixname}[i] = calloc (ret->{obj.fixname}_item_lens[i] + 1, sizeof (**ret->{obj.fixname}[i])); @@ -1387,7 +1387,7 @@ def emit_parse(self, c_file, obj, prefix, obj_typename): {{ ''', indent=1) - if obj.doublearray: + if obj.nested_array: emit(c_file, f''' yajl_val *items = YAJL_GET_ARRAY_NO_CHECK(tmp)->values; ret->{obj.fixname} = calloc ( YAJL_GET_ARRAY_NO_CHECK(tmp)->len + 1, sizeof (*ret->{obj.fixname})); @@ -1433,7 +1433,7 @@ def emit_generate(self, c_file, obj, prefix): ''', indent=1) emit_gen_key_with_check(c_file, obj.origname, indent=2) - if obj.doublearray: + if obj.nested_array: emit_gen_array_open(c_file, indent=3) check_gen_status(c_file, indent=3) emit(c_file, f''' @@ -1494,7 +1494,7 @@ def emit_parse(self, c_file, obj, prefix, obj_typename): ''', indent=1) calloc_with_check(c_file, f'ret->{obj.fixname}', 'len + 1', f'*ret->{obj.fixname}', indent=3) - if obj.doublearray: + if obj.nested_array: calloc_with_check(c_file, f'ret->{obj.fixname}_item_lens', 'len + 1', 'size_t', indent=3) emit(c_file, ''' @@ -1502,7 +1502,7 @@ def emit_parse(self, c_file, obj, prefix, obj_typename): { ''', indent=3) - if obj.doublearray: + 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})); @@ -1551,7 +1551,7 @@ def emit_generate(self, c_file, obj, prefix): { ''', indent=2) - if obj.doublearray: + if obj.nested_array: emit_gen_array_open(c_file, indent=3) check_gen_status(c_file, indent=3) emit(c_file, f''' @@ -1592,7 +1592,7 @@ def _emit_free_string(self, c_file, obj): {{ ''', indent=1) - if obj.doublearray: + if obj.nested_array: emit(c_file, f''' size_t j; for (j = 0; j < ptr->{obj.fixname}_item_lens[i]; j++) @@ -1615,7 +1615,7 @@ def _emit_free_string(self, c_file, obj): } ''', indent=3) - if obj.doublearray: + if obj.nested_array: free_and_null(c_file, "ptr", f"{obj.fixname}_item_lens", indent=2) free_and_null(c_file, "ptr", obj.fixname, indent=2) @@ -1628,7 +1628,7 @@ def _emit_free_numeric(self, c_file, obj): emit(c_file, ''' { ''', indent=0) - if obj.doublearray: + if obj.nested_array: emit(c_file, f''' size_t i; for (i = 0; i < ptr->{obj.fixname}_len; i++) @@ -1654,7 +1654,7 @@ def emit_clone(self, c_file, obj, prefix, indent): ''', indent=indent+2) def _emit_clone_string(self, c_file, obj, indent): - if obj.doublearray: + if obj.nested_array: emit(c_file, f''' ret->{obj.fixname}[i] = calloc (ret->{obj.fixname}_item_lens[i] + 1, sizeof (**ret->{obj.fixname}[i])); if (ret->{obj.fixname}[i] == NULL) @@ -2102,7 +2102,7 @@ def get_c_epilog_for_array_make_parse(c_file, prefix, typ, obj): ptr->len = alen; ''', indent=0) - if obj.doublearray: + if obj.nested_array: emit(c_file, ''' ptr->subitem_lens = calloc ( alen + 1, sizeof (size_t)); if (ptr->subitem_lens == NULL) @@ -2122,7 +2122,7 @@ def get_c_epilog_for_array_make_parse(c_file, prefix, typ, obj): else: subtypename = helpers.get_name_substr(obj.name, prefix) - if obj.doublearray: + 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)); @@ -2144,7 +2144,7 @@ def get_c_epilog_for_array_make_parse(c_file, prefix, typ, obj): return NULL; ''', indent=2) elif obj.subtyp == 'byte': - if obj.doublearray: + if obj.nested_array: emit(c_file, ''' char *str = YAJL_GET_STRING (work); ptr->items[j] = (uint8_t *)strdup (str ? str : ""); @@ -2158,7 +2158,7 @@ def get_c_epilog_for_array_make_parse(c_file, prefix, typ, obj): break; ''', indent=2) else: - if obj.doublearray: + if obj.nested_array: emit(c_file, ''' ptr->items[i] = calloc ( YAJL_GET_ARRAY_NO_CHECK(work)->len + 1, sizeof (**ptr->items)); if (ptr->items[i] == NULL) @@ -2219,7 +2219,7 @@ def get_c_epilog_for_array_make_free(c_file, prefix, typ, obj): }} ''', indent=2) elif obj.subtyp == 'string': - if obj.doublearray: + if obj.nested_array: emit(c_file, ''' size_t j; for (j = 0; j < ptr->subitem_lens[i]; j++) @@ -2236,7 +2236,7 @@ def get_c_epilog_for_array_make_free(c_file, prefix, typ, obj): ptr->items[i] = NULL; ''', indent=2) elif not helpers.is_compound_type(obj.subtyp): - if obj.doublearray: + if obj.nested_array: emit(c_file, ''' free (ptr->items[i]); ptr->items[i] = NULL; @@ -2247,7 +2247,7 @@ def get_c_epilog_for_array_make_free(c_file, prefix, typ, obj): else: free_func = helpers.get_name_substr(obj.name, prefix) - if obj.doublearray: + if obj.nested_array: emit(c_file, f''' size_t j; for (j = 0; j < ptr->subitem_lens[i]; j++) @@ -2267,7 +2267,7 @@ def get_c_epilog_for_array_make_free(c_file, prefix, typ, obj): emit(c_file, ''' } ''', indent=1) - if obj.doublearray: + if obj.nested_array: emit(c_file, ''' free (ptr->subitem_lens); ptr->subitem_lens = NULL; @@ -2328,7 +2328,7 @@ def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): emit(c_file, ''' { ''', indent=1) - if obj.doublearray: + if obj.nested_array: emit_gen_array_open(c_file, indent=3) check_gen_status(c_file, indent=3) emit(c_file, f''' @@ -2357,7 +2357,7 @@ def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): { const char *str = NULL; ''', indent=1) - if obj.doublearray: + if obj.nested_array: emit_gen_array_open(c_file, indent=3) check_gen_status(c_file, indent=3) emit(c_file, ''' @@ -2396,7 +2396,7 @@ def get_c_epilog_for_array_make_gen(c_file, prefix, typ, obj): emit(c_file, ''' { ''', indent=2) - if obj.doublearray: + if obj.nested_array: emit_gen_array_open(c_file, indent=3) check_gen_status(c_file, indent=3) emit(c_file, '''