From 73d7afc5c3b565a25e78a25dd73c433193ce9482 Mon Sep 17 00:00:00 2001 From: Michael Mezger Date: Wed, 26 Jun 2024 11:51:12 +0200 Subject: [PATCH 1/2] Add feature from request #75: - only create xmldsig framgnets which are listed (like other fragments) - remove unused fields for optimization (per default remove "shall not be used" from standard) removed elements are not in structure and will result in en/decoder error NOT_IMPLEMENTED when "isUsed" flag is set. Signed-off-by: Michael Mezger --- src/cbexigen/SchemaAnalyzer.py | 19 +++++++++++++ src/cbexigen/datatype_classes.py | 27 ++++++++++++++++--- src/cbexigen/decoder_classes.py | 26 ++++++++++++++++-- src/cbexigen/elementData.py | 1 + src/cbexigen/encoder_classes.py | 25 +++++++++++++++-- src/config.py | 27 +++++++++++++++++++ .../code_templates/c/SubStructOptimized.jinja | 3 +++ .../EncodeEventOptionalElementOptimized.jinja | 14 ++++++++++ 8 files changed, 135 insertions(+), 7 deletions(-) create mode 100644 src/input/code_templates/c/SubStructOptimized.jinja create mode 100644 src/input/code_templates/c/encoder/EncodeEventOptionalElementOptimized.jinja diff --git a/src/cbexigen/SchemaAnalyzer.py b/src/cbexigen/SchemaAnalyzer.py index ace95314..2917290e 100644 --- a/src/cbexigen/SchemaAnalyzer.py +++ b/src/cbexigen/SchemaAnalyzer.py @@ -992,6 +992,7 @@ def analyze_schema_elements(self): # Check memory option and make optimization if self.config['apply_optimizations'] == 1: self.__apply_array_optimizations() + self.__mark_field_optimizations() # Do the preparations for type generation self.__prepare_for_type_generation() @@ -1552,6 +1553,24 @@ def __apply_array_optimizations(self): particle.max_occurs = optimizations[particle.type_short] particle.max_occurs_changed = True + def __mark_field_optimizations(self): + config_module = get_config_module() + parameter = self.__schema_prefix + 'field_optimizations' + if hasattr(config_module, parameter): + optimizations = getattr(config_module, parameter) + else: + return + + for element in self.__generate_elements: + if element.name_short in optimizations.keys() and len(optimizations[element.name_short]) == 0: + msg_write(f"Optimized element: {element.name_short}") + + for particle in element.particles: + if particle.name in optimizations.keys(): + if len(optimizations[particle.name]) == 0 or element.name_short in optimizations[particle.name]: + particle.is_optimized = True + msg_write(f"Optimized particle: {particle.name} in element: {element.name}") + def __prepare_for_type_generation(self): # Sort the list of elements to be generated by level and count self.__generate_elements.sort(key=lambda item: item.count, reverse=False) diff --git a/src/cbexigen/datatype_classes.py b/src/cbexigen/datatype_classes.py index c27ec7c8..b4aad116 100644 --- a/src/cbexigen/datatype_classes.py +++ b/src/cbexigen/datatype_classes.py @@ -36,6 +36,12 @@ def __init__(self, current_scheme: XMLSchema11, parameters, analyzer_data: Analy self.__fragments = get_fragment_parameter_for_schema(self.__schema_prefix) self.__generate_fragment = len(self.__fragments) > 0 + self.__xmldsigfragments = [] + self.__generate_all_xmldsig_fragment = True + if self.config['generate_fragments'] == 1: + self.__xmldsigfragments = get_fragment_parameter_for_schema('xmldsig_') + self.__generate_all_xmldsig_fragment = len(self.__xmldsigfragments) == 0 + if self.logging_enabled: self.logger_name = str(self.h_params['filename']) if self.logger_name.casefold().endswith('.h') or self.logger_name.casefold().endswith('.c'): @@ -276,12 +282,19 @@ def __generate_sequence_content(self, name, comment, content, indent_level=1): return temp.render(indent=indent, level=indent_level, sequence_comment=comment, sequence_name=name, sequence_content=content) + def __generate_optimized_struct(self, name, comment, indent_level=1): + indent = ' ' * self.config['c_code_indent_chars'] + temp = self.generator.get_template('SubStructOptimized.jinja') + return temp.render(indent=indent, level=indent_level, name=name, comment=comment) + def __get_particle_content(self, particle: Particle, elements, indent_level=1): content = '' last = None + if particle.is_optimized is True: + content += self.__generate_optimized_struct(particle.name, ": Optimized out element", indent_level) # particle type is in list, so a separate type is generated - if particle.type in self.analyzer_data.known_elements: + elif particle.type in self.analyzer_data.known_elements: if particle.max_occurs > 1: # generate struct for array with length variable if particle.is_enum: @@ -449,7 +462,7 @@ def __get_xmldsig_fragment_content(self): fragment: FragmentData for fragment in self.analyzer_data.known_fragments.values(): - if 'xmldsig' in fragment.namespace.casefold(): + if 'xmldsig' in fragment.namespace.casefold() and (fragment.name in self.__xmldsigfragments or self.__generate_all_xmldsig_fragment is True): fragment_type = fragment.type if fragment.type == 'AnonType': fragment_type = fragment.name @@ -460,6 +473,8 @@ def __get_xmldsig_fragment_content(self): else: self.log(f'xmldsig Fragment {fragment.name} ({fragment.type}) ' f'is not in the list of known elements.') + else: + self.log(f"Skipped xmlsigFragment: {fragment.name}") temp = self.generator.get_template('BaseStructWithUnionAndUsed.jinja') content = temp.render(struct_name=name, @@ -657,6 +672,12 @@ def __init__(self, current_scheme, parameters, analyzer_data: AnalyzerData, enab self.__fragments = get_fragment_parameter_for_schema(self.__schema_prefix) self.__generate_fragment = len(self.__fragments) > 0 + self.__xmldsigfragments = [] + self.__generate_all_xmldsig_fragment = True + if self.config['generate_fragments'] == 1: + self.__xmldsigfragments = get_fragment_parameter_for_schema('xmldsig_') + self.__generate_all_xmldsig_fragment = len(self.__xmldsigfragments) == 0 + if self.logging_enabled: self.logger_name = str(self.c_params['filename']) if self.logger_name.casefold().endswith('.h') or self.logger_name.casefold().endswith('.c'): @@ -789,7 +810,7 @@ def __get_xmldsig_fragment_content(self): fragment: FragmentData for fragment in self.analyzer_data.known_fragments.values(): - if 'xmldsig' in fragment.namespace.casefold(): + if 'xmldsig' in fragment.namespace.casefold() and (fragment.name in self.__xmldsigfragments or self.__generate_all_xmldsig_fragment is True): fragment_type = fragment.type if fragment.type == 'AnonType': fragment_type = fragment.name diff --git a/src/cbexigen/decoder_classes.py b/src/cbexigen/decoder_classes.py index cfaf1f2f..9b6984ce 100644 --- a/src/cbexigen/decoder_classes.py +++ b/src/cbexigen/decoder_classes.py @@ -28,6 +28,12 @@ def __init__(self, parameters, enable_logging=True): self.__fragments = get_fragment_parameter_for_schema(self.__schema_prefix) self.__generate_fragment = len(self.__fragments) > 0 + self.__xmldsigfragments = [] + self.__generate_all_xmldsig_fragment = True + if self.config['generate_fragments'] == 1: + self.__xmldsigfragments = get_fragment_parameter_for_schema('xmldsig_') + self.__generate_all_xmldsig_fragment = len(self.__xmldsigfragments) == 0 + self.__include_content = '' self.__code_content = '' @@ -102,6 +108,11 @@ def __init__(self, parameters, analyzer_data, enable_logging=True): if self.config['generate_fragments'] == 1: self.__fragments = get_fragment_parameter_for_schema(self.__schema_prefix) self.__generate_fragment = len(self.__fragments) > 0 + self.__xmldsigfragments = [] + self.__generate_all_xmldsig_fragment = True + if self.config['generate_fragments'] == 1: + self.__xmldsigfragments = get_fragment_parameter_for_schema('xmldsig_') + self.__generate_all_xmldsig_fragment = len(self.__xmldsigfragments) == 0 self.__include_content = '' self.__code_content = '' @@ -511,6 +522,14 @@ def __get_content_decode_not_implemented(self, element_typename, detail: Element return decode_content + def __get_content_decode_optimized_out(self, element_typename, detail: ElementGrammarDetail, level): + decode_comment = f"// decode removed with optimization: '{detail.particle.type_short}', base type '{detail.particle.typename}" + temp = self.generator.get_template('DecodeTypeNotImplemented.jinja') + decode_content = temp.render(decode_comment=decode_comment, + indent=self.indent, level=level) + + return decode_content + def __get_type_content(self, grammar: ElementGrammar, detail: ElementGrammarDetail, level): if detail.particle is None: temp = self.generator.get_template('BaseDecodeEndElement.jinja') @@ -519,7 +538,10 @@ def __get_type_content(self, grammar: ElementGrammar, detail: ElementGrammarDeta # default content for types not covered below type_content = self.__get_content_decode_not_implemented(grammar.element_typename, detail, level) - if detail.is_any and detail.any_is_dummy: + if detail.particle.is_optimized is True: + # use "not implemented" content when optimized + type_content = self.__get_content_decode_optimized_out(grammar.element_typename, detail, level) + elif detail.is_any and detail.any_is_dummy: type_content = self.__get_content_decode_no_event(grammar.element_typename, detail, level) elif detail.particle.is_enum: if detail.particle.is_array: @@ -870,7 +892,7 @@ def __get_xmldsig_fragment_content(self): decode_fn = [] for fragment in self.analyzer_data.known_fragments.values(): - if 'xmldsig' in fragment.namespace.casefold(): + if 'xmldsig' in fragment.namespace.casefold() and (fragment.name in self.__xmldsigfragments or self.__generate_all_xmldsig_fragment is True): if fragment.type in self.analyzer_data.known_elements.values(): function = f'{CONFIG_PARAMS["decode_function_prefix"]}{self.__schema_prefix}{fragment.type}' parameter = f'{parameter_name}->{fragment.name}' diff --git a/src/cbexigen/elementData.py b/src/cbexigen/elementData.py index 8bc35490..34219740 100644 --- a/src/cbexigen/elementData.py +++ b/src/cbexigen/elementData.py @@ -35,6 +35,7 @@ class Particle: is_enum: bool = False is_attribute: bool = False is_simple_content: bool = False + is_optimized: bool = False enum_count: int = -1 # additional flag if content model is choice and changed min occurrence content_model_changed_restrictions: bool = False diff --git a/src/cbexigen/encoder_classes.py b/src/cbexigen/encoder_classes.py index ec09067d..410713ee 100644 --- a/src/cbexigen/encoder_classes.py +++ b/src/cbexigen/encoder_classes.py @@ -28,6 +28,12 @@ def __init__(self, parameters, enable_logging=True): self.__fragments = get_fragment_parameter_for_schema(self.__schema_prefix) self.__generate_fragment = len(self.__fragments) > 0 + self.__xmldsigfragments = [] + self.__generate_all_xmldsig_fragment = True + if self.config['generate_fragments'] == 1: + self.__xmldsigfragments = get_fragment_parameter_for_schema('xmldsig_') + self.__generate_all_xmldsig_fragment = len(self.__xmldsigfragments) == 0 + self.__include_content = '' self.__code_content = '' @@ -103,6 +109,12 @@ def __init__(self, parameters, analyzer_data, enable_logging=True): self.__fragments = get_fragment_parameter_for_schema(self.__schema_prefix) self.__generate_fragment = len(self.__fragments) > 0 + self.__xmldsigfragments = [] + self.__generate_all_xmldsig_fragment = True + if self.config['generate_fragments'] == 1: + self.__xmldsigfragments = get_fragment_parameter_for_schema('xmldsig_') + self.__generate_all_xmldsig_fragment = len(self.__xmldsigfragments) == 0 + self.__include_content = '' self.__code_content = '' self.__function_content = '' @@ -531,7 +543,7 @@ def __get_event_content_for_optional_element(self, detail: ElementGrammarDetail, if detail.flag == GrammarFlag.END: content += self.__get_event_content_for_end_element(detail, grammar.bits_to_write, False, level) else: - if detail.particle is not None and not (detail.is_any and detail.any_is_dummy): + if detail.particle is not None and not (detail.is_any and detail.any_is_dummy) and detail.particle.is_optimized is False: if detail.is_extra_grammar: option = -2 @@ -557,6 +569,15 @@ def __get_event_content_for_optional_element(self, detail: ElementGrammarDetail, add_debug_code=self.get_status_for_add_debug_code(detail.particle.prefixed_name), type_parameter=type_parameter, indent=self.indent, level=level) + elif detail.particle.is_optimized is True: + parameter = grammar.element_typename + '->' + detail.particle.name + event_comment = f'// Event optimized out: {detail.particle_name} (index={detail.event_index}); next={detail.next_grammar}' + temp = self.generator.get_template('EncodeEventOptionalElementOptimized.jinja') + content += temp.render(option=option, + parameter=parameter, + event_comment=event_comment, + indent=self.indent, level=level) + else: # unsupported particle which appears in the event list if detail.is_any and detail.any_is_dummy: @@ -885,7 +906,7 @@ def __get_xmldsig_fragment_content(self): encode_fn = [] for fragment in self.analyzer_data.known_fragments.values(): - if 'xmldsig' in fragment.namespace.casefold(): + if 'xmldsig' in fragment.namespace.casefold() and (fragment.name in self.__xmldsigfragments or self.__generate_all_xmldsig_fragment is True): if fragment.type in self.analyzer_data.known_elements.values(): function = f'{CONFIG_PARAMS["encode_function_prefix"]}{self.__schema_prefix}{fragment.type}' parameter = f'{parameter_name}->{fragment.name}' diff --git a/src/config.py b/src/config.py index d818094b..b7b59e86 100644 --- a/src/config.py +++ b/src/config.py @@ -76,6 +76,28 @@ 'ParameterType': 8 } +# optimizations for fields, which shall be excluded. The name to exclude and a list of parent elements [V2G2-771] +# - Id (attribute in SignedInfo) +# - ##any in SignedInfo – CanonicalizationMethod +# - HMACOutputLength in SignedInfo – SignatureMethod +# - ##other in SignedInfo – SignatureMethod +# - Type (attribute in SignedInfo-Reference) +# - ##other in SignedInfo – Reference – Transforms – Transform +# - XPath in SignedInfo – Reference – Transforms – Transform +# - ##other in SignedInfo – Reference – DigestMethod +# - Id (attribute in SignatureValue) +# - Object (in Signature) +# - KeyInfo +iso2_field_optimizations = { + 'Id': ['SignedInfo', 'SignatureValue'], # remove Id from SignedInfo and SignatureValue + 'ANY': ['CanonicalizationMethod', 'SignatureMethod', 'Transform', 'DigestMethod'], # remove ##any from these elements + 'HMACOutputLength': ['SignatureMethod'], # remove HMACOutputLength from SignatureMethod + 'Type': ['Reference'], # remove Type from Reference + 'XPath': ['Transform'], # remove XPath from Transform + 'Object': ['Signature'], # remove Object from Signature + 'KeyInfo': [] # remove generally +} + # if fragment de- and encoder should be generated, set this value to 1. # Currently only complex elements can be added to the fragment coders. # NOTE! There may be problems when comparing the signature of the eMAID. @@ -121,6 +143,11 @@ 'DC_ChargeParameterDiscoveryRes', ] +# mxldsig fragments which should be generated +xmldsig_fragments = [ + 'SignedInfo' # creates only signedInfo fragment +] + # general C code style c_code_indent_chars = 4 # these characters will be replaced by an underscore in generated code diff --git a/src/input/code_templates/c/SubStructOptimized.jinja b/src/input/code_templates/c/SubStructOptimized.jinja new file mode 100644 index 00000000..0a2f3ad0 --- /dev/null +++ b/src/input/code_templates/c/SubStructOptimized.jinja @@ -0,0 +1,3 @@ + +{{ indent * level }}// {{ name }} {{ comment }} +{{ indent * level }}unsigned int {{ name }}_isUsed:1; diff --git a/src/input/code_templates/c/encoder/EncodeEventOptionalElementOptimized.jinja b/src/input/code_templates/c/encoder/EncodeEventOptionalElementOptimized.jinja new file mode 100644 index 00000000..52e31f8d --- /dev/null +++ b/src/input/code_templates/c/encoder/EncodeEventOptionalElementOptimized.jinja @@ -0,0 +1,14 @@ +{%- if option == -2 %} +{{ indent * level }}if (1 == 0) +{%- elif option == 0 %} +{{ indent * level }}if ({{ parameter }}_isUsed == 1u) +{%- elif option > 0 %} +{{ indent * level }}else if ({{ parameter }}_isUsed == 1u) +{%- else %} +{{ indent * level }}else +{%- endif %} +{{ indent * level }}{ +{{ indent * (level + 1) }}{{ event_comment }} +{{ indent * (level + 1) }}error = EXI_ERROR__ENCODER_NOT_IMPLEMENTED; +{{ indent * (level + 1) }}done = 1; +{{ indent * level }}} From 04404f0b717a7f5563056db8775818444000c8d4 Mon Sep 17 00:00:00 2001 From: Michael Mezger Date: Wed, 15 Apr 2026 13:09:49 +0200 Subject: [PATCH 2/2] Fix generated eventcodes of xmlsig en/decoder functions Remove optimization flags from default config and only add them as comments. Remove "illegal" characters from comments. Signed-off-by: Michael Mezger --- src/cbexigen/decoder_classes.py | 23 ++++++++++++------ src/cbexigen/encoder_classes.py | 23 ++++++++++++------ src/config.py | 41 ++++++++++++++++++--------------- 3 files changed, 54 insertions(+), 33 deletions(-) diff --git a/src/cbexigen/decoder_classes.py b/src/cbexigen/decoder_classes.py index a79b62d8..2dd67ba0 100644 --- a/src/cbexigen/decoder_classes.py +++ b/src/cbexigen/decoder_classes.py @@ -923,19 +923,28 @@ def __get_xmldsig_fragment_content(self): init_fn = (f'{CONFIG_PARAMS["init_function_prefix"]}{self.__schema_prefix}' f'{CONFIG_PARAMS["xmldsig_fragment_struct_name"]}') - decode_fn = [] + all_fragments = [] for fragment in self.analyzer_data.known_fragments.values(): - if 'xmldsig' in fragment.namespace.casefold() and (fragment.name in self.__xmldsigfragments or self.__generate_all_xmldsig_fragment is True): + if 'xmldsig' in fragment.namespace.casefold(): if fragment.type in self.analyzer_data.known_elements.values(): function = f'{CONFIG_PARAMS["decode_function_prefix"]}{self.__schema_prefix}{fragment.type}' parameter = f'{parameter_name}->{fragment.name}' - decode_fn.append([fragment.name, fragment.namespace, function, parameter]) + all_fragments.append([fragment.name, fragment.namespace, function, parameter]) else: - decode_fn.append([fragment.name, fragment.namespace, '', '']) + all_fragments.append([fragment.name, fragment.namespace, '', '']) - decode_fn.sort() - end_fragment = len(decode_fn) + 1 - bits = tools.get_bits_to_decode(len(decode_fn)) + all_fragments.sort() + + decode_fn = [] + for name, namespace, function, parameter in all_fragments: + if 'xmldsig' in namespace.casefold() and (name in self.__xmldsigfragments + or self.__generate_all_xmldsig_fragment is True): + decode_fn.append([name, namespace, function, parameter]) + else: + decode_fn.append([name, namespace, '', '']) + + end_fragment = len(all_fragments) + 1 + bits = tools.get_bits_to_decode(len(all_fragments)) temp = self.generator.get_template('DecodeFragmentFunction.jinja') content += temp.render(function_comment=comment, diff --git a/src/cbexigen/encoder_classes.py b/src/cbexigen/encoder_classes.py index 7fd20786..4e358865 100644 --- a/src/cbexigen/encoder_classes.py +++ b/src/cbexigen/encoder_classes.py @@ -919,19 +919,28 @@ def __get_xmldsig_fragment_content(self): struct_type = f'{self.__schema_prefix}{CONFIG_PARAMS["xmldsig_fragment_struct_name"]}' parameter_name = CONFIG_PARAMS['xmldsig_fragment_parameter_name'] - encode_fn = [] + all_fragments = [] for fragment in self.analyzer_data.known_fragments.values(): - if 'xmldsig' in fragment.namespace.casefold() and (fragment.name in self.__xmldsigfragments or self.__generate_all_xmldsig_fragment is True): + if 'xmldsig' in fragment.namespace.casefold(): if fragment.type in self.analyzer_data.known_elements.values(): function = f'{CONFIG_PARAMS["encode_function_prefix"]}{self.__schema_prefix}{fragment.type}' parameter = f'{parameter_name}->{fragment.name}' - encode_fn.append([fragment.name, fragment.namespace, function, parameter]) + all_fragments.append([fragment.name, fragment.namespace, function, parameter]) else: - encode_fn.append([fragment.name, fragment.namespace, '', '']) + all_fragments.append([fragment.name, fragment.namespace, '', '']) - encode_fn.sort() - end_fragment = len(encode_fn) + 1 - bits = tools.get_bits_to_decode(len(encode_fn)) + all_fragments.sort() + + encode_fn = [] + for name, namespace, function, parameter in all_fragments: + if 'xmldsig' in namespace.casefold() and (name in self.__xmldsigfragments + or self.__generate_all_xmldsig_fragment is True): + encode_fn.append([name, namespace, function, parameter]) + else: + encode_fn.append([name, namespace, '', '']) + + end_fragment = len(all_fragments) + 1 + bits = tools.get_bits_to_decode(len(all_fragments)) temp = self.generator.get_template('EncodeFragmentFunction.jinja') content += temp.render(function_comment=comment, diff --git a/src/config.py b/src/config.py index fc6ee3c4..c7b2103a 100644 --- a/src/config.py +++ b/src/config.py @@ -78,25 +78,26 @@ # optimizations for fields, which shall be excluded. The name to exclude and a list of parent elements [V2G2-771] # - Id (attribute in SignedInfo) -# - ##any in SignedInfo – CanonicalizationMethod -# - HMACOutputLength in SignedInfo – SignatureMethod -# - ##other in SignedInfo – SignatureMethod +# - ##any in SignedInfo - CanonicalizationMethod +# - HMACOutputLength in SignedInfo - SignatureMethod +# - ##other in SignedInfo - SignatureMethod # - Type (attribute in SignedInfo-Reference) -# - ##other in SignedInfo – Reference – Transforms – Transform -# - XPath in SignedInfo – Reference – Transforms – Transform -# - ##other in SignedInfo – Reference – DigestMethod +# - ##other in SignedInfo - Reference - Transforms - Transform +# - XPath in SignedInfo - Reference - Transforms - Transform +# - ##other in SignedInfo - Reference - DigestMethod # - Id (attribute in SignatureValue) # - Object (in Signature) # - KeyInfo -iso2_field_optimizations = { - 'Id': ['SignedInfo', 'SignatureValue'], # remove Id from SignedInfo and SignatureValue - 'ANY': ['CanonicalizationMethod', 'SignatureMethod', 'Transform', 'DigestMethod'], # remove ##any from these elements - 'HMACOutputLength': ['SignatureMethod'], # remove HMACOutputLength from SignatureMethod - 'Type': ['Reference'], # remove Type from Reference - 'XPath': ['Transform'], # remove XPath from Transform - 'Object': ['Signature'], # remove Object from Signature - 'KeyInfo': [] # remove generally -} +# e.g.: +# iso2_field_optimizations = { +# 'Id': ['SignedInfo', 'SignatureValue'], # remove Id from SignedInfo and SignatureValue +# 'ANY': ['CanonicalizationMethod', 'SignatureMethod', 'Transform', 'DigestMethod'], # remove ##any from these elements +# 'HMACOutputLength': ['SignatureMethod'], # remove HMACOutputLength from SignatureMethod +# 'Type': ['Reference'], # remove Type from Reference +# 'XPath': ['Transform'], # remove XPath from Transform +# 'Object': ['Signature'], # remove Object from Signature +# 'KeyInfo': [] # remove generally +# } # if fragment de- and encoder should be generated, set this value to 1. # Currently only complex elements can be added to the fragment coders. @@ -143,10 +144,12 @@ 'DC_ChargeParameterDiscoveryRes', ] -# mxldsig fragments which should be generated -xmldsig_fragments = [ - 'SignedInfo' # creates only signedInfo fragment -] +# xmldsig fragments which should be generated into en/decoder and datastructures. +# This reduces the size of the xmlsig frgment structure immensely for ISO15118-2 and -20. +# Usage e.g.: +# xmldsig_fragments = [ +# 'SignedInfo' # creates only signedInfo fragment +# ] # general C code style c_code_indent_chars = 4