Skip to content

Commit

Permalink
Add feature from request EVerest#75:
Browse files Browse the repository at this point in the history
- 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 <[email protected]>
  • Loading branch information
Michael Mezger committed Jun 27, 2024
1 parent 9f0b060 commit 73d7afc
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 7 deletions.
19 changes: 19 additions & 0 deletions src/cbexigen/SchemaAnalyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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)
Expand Down
27 changes: 24 additions & 3 deletions src/cbexigen/datatype_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'):
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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'):
Expand Down Expand Up @@ -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
Expand Down
26 changes: 24 additions & 2 deletions src/cbexigen/decoder_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ''

Expand Down Expand Up @@ -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 = ''
Expand Down Expand Up @@ -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')
Expand All @@ -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:
Expand Down Expand Up @@ -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}'
Expand Down
1 change: 1 addition & 0 deletions src/cbexigen/elementData.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 23 additions & 2 deletions src/cbexigen/encoder_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ''

Expand Down Expand Up @@ -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 = ''
Expand Down Expand Up @@ -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

Expand All @@ -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:
Expand Down Expand Up @@ -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}'
Expand Down
27 changes: 27 additions & 0 deletions src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/input/code_templates/c/SubStructOptimized.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

{{ indent * level }}// {{ name }} {{ comment }}
{{ indent * level }}unsigned int {{ name }}_isUsed:1;
Original file line number Diff line number Diff line change
@@ -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 }}}

0 comments on commit 73d7afc

Please sign in to comment.