diff --git a/src/cbexigen/SchemaAnalyzer.py b/src/cbexigen/SchemaAnalyzer.py index 9de9384c..192e4359 100644 --- a/src/cbexigen/SchemaAnalyzer.py +++ b/src/cbexigen/SchemaAnalyzer.py @@ -1004,6 +1004,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() @@ -1592,6 +1593,24 @@ def __apply_array_optimizations(self): log_write(f'{particle.name} max_occurs changed from {particle.max_occurs_old} to {particle.max_occurs}') log_write(f'{particle.name} type {particle.type} is complex: {particle.is_complex}, was array: {particle.was_array}') + 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 f1f59bc1..6bd14eba 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, @@ -659,6 +674,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'): @@ -791,7 +812,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 e2dabc8d..2dd67ba0 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 = '' @@ -540,6 +551,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') @@ -548,7 +567,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: @@ -901,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(): 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/elementData.py b/src/cbexigen/elementData.py index 15c737d0..50be59f1 100644 --- a/src/cbexigen/elementData.py +++ b/src/cbexigen/elementData.py @@ -37,6 +37,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 2f107619..4e358865 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 = '' @@ -542,7 +554,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 @@ -568,6 +580,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: @@ -898,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(): 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 65884f4b..c7b2103a 100644 --- a/src/config.py +++ b/src/config.py @@ -76,6 +76,29 @@ '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 +# 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. # NOTE! There may be problems when comparing the signature of the eMAID. @@ -121,6 +144,13 @@ 'DC_ChargeParameterDiscoveryRes', ] +# 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 # 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 }}}