asn1rt: first step to CLASS set / table constraint with non-unique key

This commit is contained in:
mich 2018-12-03 17:13:27 +01:00
parent 2344b27e33
commit 199666cfdf
4 changed files with 169 additions and 1 deletions

View File

@ -1127,6 +1127,17 @@ def verify_modules(**kwargs):
if not _verify_const_val(val, consts_glob, mod, name, Objs.index(O)):
warn('{0}.{1}: internal object {2}, value outside of the constraint'\
.format(mod, name, Objs.index(O)), raising)
#
# 4) for SEQ / SET, in case it contains an OPEN object with a table constraint
# check the unicity of values associated to the key to the table
elif O._mode == MODE_TYPE and O.TYPE in (TYPE_SET, TYPE_SEQ) and O._cont:
for comp in O._cont.values():
if comp.TYPE in (TYPE_OPEN, TYPE_ANY) and comp._const:
for const in comp._const:
if const['type'] == CONST_TABLE:
if not _verify_seq_const_tab(O, comp._name, const):
asnlog('WNG: {0}.{1}: internal object {2}, non-unique key subvalue '\
'within a table constraint'.format(mod, name, Objs.index(O)))
def _verify_const_size(val, consts):
@ -1169,6 +1180,47 @@ def _verify_const_val(val, consts, mod, name, ind):
return False
def _verify_seq_const_tab(Obj, open_name, const_tab):
# check within a sequence with one of its component being an OPEN type
# that the key to the table constraint has unique values
comp_open = Obj._cont[open_name]
tab_key = const_tab['at']
if not tab_key or len(tab_key) != 2 or tab_key[0] != '..' or tab_key[1] not in Obj._cont:
# no key component defined or
# complex path to key component, unable to make the verification
return True
# get the full tab put into a single list of values
tab = const_tab['tab']._val['root']
if const_tab['tab']._val['ext']:
# create a new list concatenating the root and ext part
tab = tab + const_tab['tab']._val['ext']
# for all values, check the one associated to the key component
comp_key = Obj._cont[tab_key[1]]
id_key = comp_key.get_typeref()._name
val_key, val_all, ret = [], [], True
for val in tab:
if id_key not in val:
#asnlog('[WNG] constraint table value without key subvalue %s: %r' % (id_key, val))
pass
else:
if val[id_key] in val_key:
# duplicated key subvalue, get the corresponding complete value
if val != val_all[ val_key.index(val[id_key]) ]:
# Houston, we got a problem !
#asnlog('[WNG] constraint table with duplicated key subvalue %s: %r, '\
# 'and different associated value' % (id_key, val[id_key]))
#assert()
ret = False
else:
# duplicated key subvalue, with hopefully same value
# nothing to do here
pass
else:
val_key.append( val[id_key] )
val_all.append( val )
return ret
#------------------------------------------------------------------------------#
# ASN.1 modules generation
#------------------------------------------------------------------------------#

View File

@ -82,6 +82,11 @@ Specific method:
TYPE = TYPE_CLASS
TAG = None
# this is to always enumerate all class set of values,
# for when the UNIQUE field is actually not unique and the class set is
# not defined at the module root (and hence has not _lut attribute)
_CLASET_MULT = False
def _safechk_val(self, val):
if not isinstance(val, dict) or not all([k in self._cont for k in val]):
raise(ASN1ObjErr('{0}: invalid value, {1!r}'.format(self.fullname(), val)))
@ -160,6 +165,61 @@ Specific method:
pass
return None
def get(self, key, val):
# this is using the _lut attribute, which is built at module init
# for every CLASS set defined at the root of a module
if hasattr(self, '_lut'):
if key == self._lut['__lut__']:
return self._lut.get(val, (CLASET_NONE, None))
if self._CLASET_MULT:
return self.get_mult(key, val)
else:
return self.get_uniq(key, val)
def get_uniq(self, name, val):
# this is using an enumeration of all CLASS set of values,
# and returns the first corresponding value found
ret = None
if self._mode != MODE_SET:
return ret
if self._val.root:
for v in self._val.root:
try:
if v[name] == val:
return v
except KeyError:
pass
if self._val.ext:
for v in self._val.ext:
try:
if v[name] == val:
return v
except KeyError:
pass
return ret
def get_mult(self, name, val):
# this is using a complete enumeration of all CLASS set of values,
# and returns the list of corresponding values found
ret = []
if self._mode != MODE_SET:
return ret
if self._val.root:
for v in self._val.root:
try:
if v[name] == val:
ret.append(v)
except KeyError:
pass
if self._val.ext:
for v in self._val.ext:
try:
if v[name] == val:
ret.append(v)
except KeyError:
pass
return ret
# this is very experimental... and may certainly raise() in case of
# MODE_SET or MODE_TYPE field setting
def from_asn1(self, txt):

View File

@ -203,10 +203,16 @@ def init_modules(*args, **kwargs):
elif Obj.TYPE == TYPE_OID and Obj._mode == MODE_VALUE:
if Obj._val in GLOB.OID and GLOB.OID[Obj._val] != Obj._name:
if not Obj._SILENT:
asnlog('init_modules: different OID objects (%s, %s) with same OID value (%r)'\
asnlog('init_modules: different OID objects (%s, %s) with same OID value %r'\
% (Obj._name, GLOB.OID[Obj._val], Obj._val))
elif Obj._val is not None:
GLOB.OID[Obj._val] = Obj._name
#
elif Obj.TYPE == TYPE_CLASS and Obj._mode == MODE_SET and Obj._val:
# this should not conflict with the previous check on TYPE_CLASS
# which must have self._cont defined (hence being MODE_TYPE)
build_classset_dict(Obj)
#
# lists all objects defined
Objs = [Obj for Mod in args for Obj in Mod._all_]
@ -351,6 +357,7 @@ def get_typeref(Obj, GLOB=GLOBAL):
#
return tr
def get_tag_chain(Obj):
"""
returns the list of tags from self up to the last referred object
@ -372,6 +379,7 @@ def get_tag_chain(Obj):
tagc.append( (0, Obj.TAG) )
return tagc
def get_cont_tags_dict(Obj):
"""
returns the ASN1Dict of tags, object for the content of Obj (SET and CHOICE)
@ -403,6 +411,7 @@ def get_cont_tags_dict(Obj):
tagd[Comp._tagc[0]] = ident
return tagd
def get_cont_tags_canon(Obj):
"""
returns the list of components in the canonical order of their tags
@ -430,6 +439,7 @@ def get_cont_tags_canon(Obj):
# order the whole stuff
return [tagcan[k] for k in sorted(tagcan.keys())]
def bind_attrs(Obj, *attrs):
attr = attrs[0]
if getattr(Obj, attr) is None:
@ -454,6 +464,7 @@ def bind_attrs(Obj, *attrs):
else:
tr = tr._tr if (tr._tr is not None and not tr._tr._param) else None
def bind_all_attrs(Obj):
# bind content and constraints from typeref objects
# _cont, _root, _ext, _root_mand, _root_opt, _ext_ident, _ext_group, cont_tags
@ -491,3 +502,43 @@ def bind_all_attrs(Obj):
bind_attrs(Obj, '_cont', '_root', '_root_mand', '_root_opt')
bind_attrs(Obj, '_ext')
def build_classset_dict(Obj):
key = None
tr = get_typeref(Obj)
while not tr._cont:
tr = get_typeref(tr)
if tr is None:
break
if tr._cont is None:
# something is screwed somewhere
return
for Comp in tr._cont.values():
if Comp._uniq:
key = Comp._name
if key is None:
return
# check the key (UNIQUE) component
Obj._lut = {'__key__': key}
__build_classet_dict(Obj, key, Obj._val.root)
if Obj._val.ext:
__build_classet_dict(Obj, key, Obj._val.ext)
def __build_classet_dict(Obj, key, valset):
for val in valset:
if key in val:
keyval = val[key]
if keyval in Obj._lut:
# this is not as UNIQUE as one can think...
lutval = Obj._lut[keyval]
if lutval[0] == CLASET_UNIQ:
# switching to MULT
Obj._lut[keyval] = (CLASET_MULT, [lutval, val])
else:
# already defined as MULT
lutval[1].append(val)
else:
# this is the first (and hopefully UNIQUE) value
Obj._lut[keyval] = (CLASET_UNIQ, val)

View File

@ -291,6 +291,11 @@ FLAG_UNIQ = 'UNIQUE'
FLAG_DEF = 'DEFAULT'
FLAG_DEFBY = 'DEFINED BY'
# specific flags for CLASS set of values, for specific use as table constraint
CLASET_UNIQ = 'U'
CLASET_MULT = 'M'
CLASET_NONE = 'N'
#------------------------------------------------------------------------------#
# asn1rt naming routine
#------------------------------------------------------------------------------#