From 73d7afc5c3b565a25e78a25dd73c433193ce9482 Mon Sep 17 00:00:00 2001 From: Michael Mezger Date: Wed, 26 Jun 2024 11:51:12 +0200 Subject: [PATCH] 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 ace9531..2917290 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 c27ec7c..b4aad11 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 cfaf1f2..9b6984c 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 8bc3549..3421974 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 ec09067..410713e 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 d818094..b7b59e8 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 0000000..0a2f3ad --- /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 0000000..52e31f8 --- /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 }}}