801 lines
27 KiB
Python
801 lines
27 KiB
Python
# -*- coding: UTF-8 -*-
|
|
#/**
|
|
# * Software Name : pycrate
|
|
# * Version : 0.4
|
|
# *
|
|
# * Copyright 2016. Benoit Michau. ANSSI.
|
|
# *
|
|
# * This library is free software; you can redistribute it and/or
|
|
# * modify it under the terms of the GNU Lesser General Public
|
|
# * License as published by the Free Software Foundation; either
|
|
# * version 2.1 of the License, or (at your option) any later version.
|
|
# *
|
|
# * This library is distributed in the hope that it will be useful,
|
|
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# * Lesser General Public License for more details.
|
|
# *
|
|
# * You should have received a copy of the GNU Lesser General Public
|
|
# * License along with this library; if not, write to the Free Software
|
|
# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
# * MA 02110-1301 USA
|
|
# *
|
|
# *--------------------------------------------------------
|
|
# * File Name : pycrate_asn1c/rangeobj.py
|
|
# * Created : 2016-07-12
|
|
# * Authors : Benoit Michau
|
|
# *--------------------------------------------------------
|
|
#*/
|
|
|
|
from functools import reduce
|
|
|
|
from .utils import *
|
|
from .err import *
|
|
from .refobj import ASN1Ref
|
|
|
|
#------------------------------------------------------------------------------#
|
|
# range for integers, reals and some character strings
|
|
#------------------------------------------------------------------------------#
|
|
|
|
class ASN1Range(object):
|
|
"""
|
|
Special class to handle range of values for ASN.1 types that support
|
|
ordering their values (e.g. INTEGER and REAL)
|
|
|
|
Init args:
|
|
type : str (TYPE_*), type of the ASN.1 object handling the value range
|
|
lb : single value according to the type, lower bound of the range
|
|
lb_incl: bool, indicate if the lower bound is part of the range
|
|
ub : single value according to the type, upper bound of the range
|
|
ub_incl: bool, indicate if the upper bound is part of the range
|
|
|
|
When type is TYPE_INT or TYPE_STR_*, lb_incl and ub_incl shall always be True
|
|
"""
|
|
|
|
_TYPE_STR = (TYPE_STR_IA5, TYPE_STR_PRINT, TYPE_STR_VIS)
|
|
_TYPE = (TYPE_INT, TYPE_REAL) + _TYPE_STR
|
|
|
|
# methods for emulating a dictionnary
|
|
# this enables to reference path within ASN.1 objects in the form of
|
|
# list of str or int; e.g. path = ['const', 0, 'root', 0, 'ub']
|
|
def __getitem__(self, kw):
|
|
if kw in self.KW:
|
|
return getattr(self, kw)
|
|
else:
|
|
return object.__getitem__(self, kw)
|
|
|
|
def __setitem__(self, kw, arg):
|
|
if kw in self.KW:
|
|
return setattr(self, kw, arg)
|
|
else:
|
|
return object.__setitem__(self, kw, arg)
|
|
|
|
def copy(self):
|
|
"""
|
|
returns an equal but independent copy of self
|
|
"""
|
|
if isinstance(self.lb, ASN1Ref):
|
|
lb = self.lb.copy()
|
|
else:
|
|
lb = self.lb
|
|
if isinstance(self.ub, ASN1Ref):
|
|
ub = self.ub.copy()
|
|
else:
|
|
ub = self.ub
|
|
if isinstance(self, (ASN1RangeInt, ASN1RangeStr)):
|
|
return self.__class__(self.lb, self.ub)
|
|
elif isinstance(self, ASN1RangeReal):
|
|
return self.__class__(self.lb, self.ub, self.lb_incl, self.ub_incl)
|
|
|
|
def expand(self):
|
|
raise(ASN1Err('{0!r}: unable to expand this type of range'.format(self)))
|
|
|
|
|
|
class ASN1RangeInt(ASN1Range):
|
|
|
|
KW = ('lb', 'ub')
|
|
|
|
_EXP_MAX = 2**18 # max range that can be expanded
|
|
|
|
def __init__(self, lb=None, ub=None):
|
|
self.lb = lb
|
|
self.ub = ub
|
|
|
|
def _safechk(self):
|
|
if not isinstance(self.lb, integer_types + (NoneType, )) or \
|
|
not isinstance(self.ub, integer_types + (NoneType, )) or \
|
|
(self.ub is not None and self.lb is not None and self.ub < self.lb):
|
|
raise(ASN1Err('{0!r}: invalid bounds'.format(self)))
|
|
|
|
def __repr__(self):
|
|
return 'ASN1RangeInt({0!r}..{1!r})'.format(self.lb, self.ub)
|
|
|
|
def expand(self):
|
|
"""
|
|
returns a list of integers
|
|
"""
|
|
if self.lb is None or self.ub is None:
|
|
raise(ASN1Err('{0!r}: unable to expand infinite range'.format(self)))
|
|
elif self.ub - self.lb < self._EXP_MAX:
|
|
return list(range(self.lb, 1+self.ub))
|
|
else:
|
|
raise(ASN1Err('{0!r}: range too large for expansion'.format(self)))
|
|
|
|
def __contains__(self, item):
|
|
if not isinstance(item, integer_types):
|
|
return None
|
|
elif self.lb is None:
|
|
if self.ub is None:
|
|
return True
|
|
else:
|
|
return item <= self.ub
|
|
elif self.ub is None:
|
|
return self.lb <= item
|
|
else:
|
|
return self.lb <= item <= self.ub
|
|
|
|
def intersect(self, ra):
|
|
"""
|
|
returns a single ASN1RangeInt which is the intersection of self and `ra'
|
|
in case they intersect, None otherwise
|
|
"""
|
|
if not isinstance(ra, ASN1RangeInt):
|
|
return None
|
|
#
|
|
# disjoint sets:
|
|
if self.ub is not None and ra.lb is not None and self.ub < ra.lb:
|
|
return None
|
|
elif ra.ub is not None and self.lb is not None and ra.ub < self.lb:
|
|
return None
|
|
#
|
|
# intersecting sets:
|
|
elif ra.lb in self:
|
|
if ra.ub in self:
|
|
return ASN1RangeInt(ra.lb, ra.ub)
|
|
else:
|
|
return ASN1RangeInt(ra.lb, self.ub)
|
|
elif ra.ub in self:
|
|
return ASN1RangeInt(self.lb, ra.ub)
|
|
else:
|
|
return ASN1RangeInt(self.lb, self.ub)
|
|
|
|
def unite(self, ra):
|
|
"""
|
|
returns a single ASN1RangeInt which is the union of self and `ra'
|
|
in case they intersect, None otherwise
|
|
"""
|
|
if not isinstance(ra, ASN1RangeInt):
|
|
return None
|
|
#
|
|
# disjoint sets:
|
|
if self.ub is not None and ra.lb is not None and self.ub < ra.lb:
|
|
return None
|
|
elif ra.ub is not None and self.lb is not None and ra.ub < self.lb:
|
|
return None
|
|
#
|
|
# intersecting sets:
|
|
if self.lb is None or ra.lb is None:
|
|
lb = None
|
|
else:
|
|
lb = min(self.lb, ra.lb)
|
|
if self.ub is None or ra.ub is None:
|
|
ub = None
|
|
else:
|
|
ub = max(self.ub, ra.ub)
|
|
return ASN1RangeInt(lb, ub)
|
|
|
|
def diff(self, ra):
|
|
"""
|
|
returns a 2-tuple of ASN1RangeStr or None, which are the exclusive
|
|
parts of each self and `ra' ranges
|
|
"""
|
|
if not isinstance(ra, ASN1RangeInt):
|
|
return self, ra
|
|
#
|
|
# disjoint sets:
|
|
if self.ub is not None and ra.lb is not None and self.ub < ra.lb:
|
|
return self, ra
|
|
elif ra.ub is not None and self.lb is not None and ra.ub < self.lb:
|
|
return ra, self
|
|
#
|
|
# intersecting sets:
|
|
# lower set
|
|
if self.lb == ra.lb:
|
|
lset = None
|
|
else:
|
|
lset = ASN1RangeStr(min(self.lb, ra.lb), max(self.lb, ra.lb) - 1)
|
|
# upper set
|
|
if self.lb == ra.lb:
|
|
lset = None
|
|
else:
|
|
if None in (self.lb, ra.lb):
|
|
lset = ASN1RangeInt(None, max(self.lb, ra.lb) - 1)
|
|
else:
|
|
lset = ASN1RangeInt(min(self.lb, ra.lb), max(self.lb, ra.lb) - 1)
|
|
if self.ub == ra.ub:
|
|
uset = None
|
|
else:
|
|
if None in (self.ub, ra.ub):
|
|
uset = ASN1RangeInt(min(self.ub, ra.ub) + 1, None)
|
|
else:
|
|
uset = ASN1RangeInt(min(self.ub, ra.ub) + 1, max(self.ub, ra.ub))
|
|
return lset, uset
|
|
|
|
|
|
class ASN1RangeStr(ASN1Range):
|
|
|
|
KW = ('lb', 'ub')
|
|
|
|
def __init__(self, lb=chr(0), ub=chr(0xff)):
|
|
self.lb = lb
|
|
self.ub = ub
|
|
|
|
def _safechk(self):
|
|
if not isinstance(self.lb, str_types) or \
|
|
not isinstance(self.ub, str_types) or \
|
|
len(self.lb) != 1 or len(self.ub) != 1 or ord(self.ub) < ord(self.lb):
|
|
raise(ASN1Err('{0!r}!: invalid bounds'.format(self)))
|
|
|
|
def __repr__(self):
|
|
return 'ASN1RangeStr("{0}".."{1}")'.format(self.lb, self.ub)
|
|
|
|
def expand(self):
|
|
"""
|
|
returns a list of characters
|
|
"""
|
|
return list(map(chr, range(ord(self.lb), 1+ord(self.ub))))
|
|
|
|
def __contains__(self, item):
|
|
if not isinstance(item, str_types) or len(item) > 1:
|
|
return False
|
|
else:
|
|
return ord(self.lb) <= ord(item) <= ord(self.ub)
|
|
|
|
def intersect(self, ra):
|
|
"""
|
|
returns a single ASN1RangeStr which is the intersection of self and `ra'
|
|
in case they intersect, None otherwise
|
|
"""
|
|
if not isinstance(ra, ASN1RangeStr):
|
|
return None
|
|
#
|
|
lb, ub, ralb, raub = ord(self.lb), ord(self.ub), ord(ra.lb), ord(ra.ub)
|
|
# disjoint sets:
|
|
if ub < ralb or raub < lb:
|
|
return None
|
|
#
|
|
# intersecting sets:
|
|
else:
|
|
return ASN1RangeStr(chr(max(lb, ralb)), chr(min(ub, raub)))
|
|
|
|
def unite(self, ra):
|
|
"""
|
|
returns a single ASN1RangeInt which is the union of self and `ra'
|
|
in case they intersect, None otherwise
|
|
"""
|
|
if not isinstance(ra, ASN1RangeStr):
|
|
return None
|
|
#
|
|
lb, ub, ralb, raub = ord(self.lb), ord(self.ub), ord(ra.lb), ord(ra.ub)
|
|
# disjoint sets:
|
|
if ub < ralb or raub < lb:
|
|
return None
|
|
#
|
|
# intersecting sets:
|
|
else:
|
|
return ASN1RangeStr(chr(min(lb, ralb)), chr(max(ub, raub)))
|
|
|
|
def diff(self, ra):
|
|
"""
|
|
returns a 2-tuple of ASN1RangeStr or None, which are the exclusive
|
|
parts of each self and `ra' ranges
|
|
"""
|
|
if not isinstance(ra, ASN1RangeStr):
|
|
return self, ra
|
|
#
|
|
lb, ub, ralb, raub = ord(self.lb), ord(self.ub), ord(ra.lb), ord(ra.ub)
|
|
# disjoint sets:
|
|
if ub < ralb:
|
|
return self, ra
|
|
elif raub < lb:
|
|
return ra, self
|
|
#
|
|
# intersecting sets:
|
|
# lower set
|
|
if self.lb == ra.lb:
|
|
lset = None
|
|
else:
|
|
lset = ASN1RangeStr(min(self.lb, ra.lb), max(self.lb, ra.lb) - 1)
|
|
# upper set
|
|
if self.ub == ra.ub:
|
|
uset = None
|
|
else:
|
|
uset = ASN1RangeStr(min(self.ub, ra.ub) + 1, max(self.ub, ra.ub))
|
|
return lset, uset
|
|
|
|
|
|
MINUS_INF = (-1, None, None)
|
|
PLUS_INF = ( 1, None, None)
|
|
NAN = ( 0, None, None)
|
|
|
|
def real_to_float(realtuple):
|
|
# TODO: Python float default precision is quite bad
|
|
# it would be nice to use the Decimal module to set the floating point
|
|
# precision as required
|
|
# or even better, to use a module dedicated to arbitrary precision floating
|
|
# point operation
|
|
# TODO: moreover, for very large exponent, it will raises an OverflowError
|
|
# as the integral exponentiation will fail
|
|
return float(realtuple[0]*(realtuple[1]**realtuple[2]))
|
|
|
|
def real_lowest(rt1, rt2):
|
|
if rt1 == MINUS_INF or rt2 == MINUS_INF:
|
|
return MINUS_INF
|
|
elif rt1 == PLUS_INF:
|
|
return rt2
|
|
elif rt2 == PLUS_INF:
|
|
return rt1
|
|
#
|
|
rt1f, rt2f = real_to_float(rt1), real_to_float(rt2)
|
|
if rt1f <= rt2f:
|
|
return rt1
|
|
else:
|
|
return rt2
|
|
|
|
def real_highest(rt1, rt2):
|
|
if rt1 == PLUS_INF or rt2 == PLUS_INF:
|
|
return PLUS_INF
|
|
elif rt1 == MINUS_INF:
|
|
return rt2
|
|
elif rt2 == MINUS_INF:
|
|
return rt1
|
|
#
|
|
rt1f, rt2f = real_to_float(rt1), real_to_float(rt2)
|
|
if rt1f <= rt2f:
|
|
return rt2
|
|
else:
|
|
return rt1
|
|
|
|
class ASN1RangeReal(ASN1Range):
|
|
|
|
KW = ('lb', 'ub', 'lb_incl', 'ub_incl')
|
|
|
|
def __init__(self, lb=MINUS_INF, ub=PLUS_INF, lb_incl=True, ub_incl=True):
|
|
self.lb = lb
|
|
self.lb_incl = lb_incl
|
|
self.ub = ub
|
|
self.ub_incl = ub_incl
|
|
|
|
def _safechk(self):
|
|
if not isinstance(self.lb, tuple) or not isinstance(self.ub, tuple) or \
|
|
len(self.lb) != 3 or len(self.ub) != 3 or \
|
|
not isinstance(self.lb[0], integer_types) or \
|
|
not isinstance(self.ub[0], integer_types) or \
|
|
not all([isinstance(b, integer_types + (NoneType, )) for b in \
|
|
(self.lb[1], self.lb[2], self.ub[1], self.ub[2])]):
|
|
raise(ASN1Err('{0!r}: invalid bounds'.format(self.__class__.__name__)))
|
|
elif self.lb in (NAN, PLUS_INF) or self.ub in (NAN, MINUS_INF):
|
|
raise(ASN1Err('{0!r}: invalid inifinite bound'.format(self)))
|
|
elif self.lb != MINUS_INF and self.ub != PLUS_INF:
|
|
lb, ub = real_to_float(self.lb), real_to_float(self.ub)
|
|
if ub < lb:
|
|
raise(ASN1Err('{0!r}: invalid bounds'.format(self)))
|
|
elif lb == ub and not (self.lb_incl and self.ub_incl):
|
|
raise(ASN1Err('{0!r}: invalid bounds'.format(self)))
|
|
|
|
def __repr__(self):
|
|
if self.lb == MINUS_INF:
|
|
lb = 'MINUS-INFINITY'
|
|
elif self.lb[1] == 10:
|
|
lb = '{0!r}e{1!r}'.format(self.lb[0], self.lb[2])
|
|
else:
|
|
lb = '{0!r}*{1!r}**{2!r}'.format(self.lb[0], self.lb[1], self.lb[2])
|
|
if not self.lb_incl:
|
|
lb = lb + '<'
|
|
if self.ub == PLUS_INF:
|
|
ub = 'PLUS-INFINITY'
|
|
elif self.ub[1] == 10:
|
|
ub = '{0!r}e{1!r}'.format(self.ub[0], self.ub[2])
|
|
else:
|
|
ub = '{0!r}*{1!r}**{2!r}'.format(self.lb[0], self.lb[1], self.lb[2])
|
|
if not self.ub_incl:
|
|
ub = '<' + ub
|
|
return 'ASN1RangeReal({0}..{1})'.format(lb, ub)
|
|
|
|
def __contains__(self, item):
|
|
if not isinstance(item, tuple) or len(item) != 3:
|
|
return False
|
|
elif not all([isinstance(i, integer_types) for i in item]) or \
|
|
item not in (MINUS_INF, PLUS_INF):
|
|
return False
|
|
#
|
|
if item == MINUS_INF and self.lb == MINUS_INF:
|
|
return self.lb_incl
|
|
elif item == PLUS_INF and self.ub == PLUS_INF:
|
|
return self.ub_incl
|
|
elif real_lowest(self.lb, item) == item:
|
|
return False
|
|
elif real_highest(self.ub, item) == item:
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
def intersect(self, ra):
|
|
"""
|
|
returns a single ASN1RangeReal which is the intersection of self and `ra'
|
|
in case they intersect, None otherwise
|
|
"""
|
|
if not isinstance(ra, ASN1RangeReal):
|
|
return None
|
|
#
|
|
if self.lb != MINUS_INF:
|
|
slb = real_to_float(self.lb)
|
|
if self.ub != PLUS_INF:
|
|
sub = real_to_float(self.ub)
|
|
if ra.lb != MINUS_INF:
|
|
ralb = real_to_float(ra.lb)
|
|
if ra.ub != PLUS_INF:
|
|
raub = real_to_float(ra.ub)
|
|
#
|
|
# disjoint sets:
|
|
if self.ub is not PLUS_INF and ra.lb is not MINUS_INF and sub < ralb:
|
|
return None
|
|
elif ra.ub is not PLUS_INF and self.lb is not MINUS_INF and raub < slb:
|
|
return None
|
|
#
|
|
# intersecting sets:
|
|
# lower bound
|
|
if MINUS_INF == self.lb == ra.lb:
|
|
lb, lb_incl = MINUS_INF, self.lb_incl & ra.lb_incl
|
|
elif MINUS_INF not in (self.lb, ra.lb) and slb == ralb:
|
|
lb, lb_incl = self.lb, self.lb_incl & ra.lb_incl
|
|
else:
|
|
lb = real_highest(self.lb, ra.lb)
|
|
if lb == self.lb:
|
|
lb_incl = self.lb_incl
|
|
else:
|
|
lb_incl = ra.lb_incl
|
|
# upper bound
|
|
if PLUS_INF == self.ub == ra.ub:
|
|
ub, ub_incl = PLUS_INF, self.ub_incl & ra.ub_incl
|
|
elif PLUS_INF not in (self.ub, ra.ub) and sub == raub:
|
|
ub, ub_incl = self.ub, self.ub_incl & ra.ub_incl
|
|
else:
|
|
ub = real_lowest(self.ub, ra.ub)
|
|
if ub == self.ub:
|
|
ub_incl = self.ub_incl
|
|
else:
|
|
ub_incl = ra.ub_incl
|
|
return ASN1RangeReal(lb, ub, lb_incl, ub_incl)
|
|
|
|
def unite(self, ra):
|
|
"""
|
|
returns a single ASN1RangeReal which is the union of self and `ra'
|
|
in case they intersect, None otherwise
|
|
"""
|
|
if not isinstance(ra, ASN1RangeReal):
|
|
return None
|
|
#
|
|
if self.lb != MINUS_INF:
|
|
slb = real_to_float(self.lb)
|
|
if self.ub != PLUS_INF:
|
|
sub = real_to_float(self.ub)
|
|
if ra.lb != MINUS_INF:
|
|
ralb = real_to_float(ra.lb)
|
|
if ra.ub != PLUS_INF:
|
|
raub = real_to_float(ra.ub)
|
|
#
|
|
# disjoint sets:
|
|
if self.ub is not PLUS_INF and ra.lb is not MINUS_INF and sub < ralb:
|
|
return None
|
|
elif ra.ub is not PLUS_INF and self.lb is not MINUS_INF and raub < slb:
|
|
return None
|
|
#
|
|
# intersecting sets:
|
|
# lower bound
|
|
if MINUS_INF == self.lb == ra.lb:
|
|
lb, lb_incl = MINUS_INF, self.lb_incl | ra.lb_incl
|
|
elif MINUS_INF not in (self.lb, ra.lb) and slb == ralb:
|
|
lb, lb_incl = self.lb, self.lb_incl | ra.lb_incl
|
|
else:
|
|
lb = real_lowest(self.lb, ra.lb)
|
|
if lb == self.lb:
|
|
lb_incl = self.lb_incl
|
|
else:
|
|
lb_incl = ra.lb_incl
|
|
# upper bound
|
|
if PLUS_INF == self.ub == ra.ub:
|
|
ub, ub_incl = PLUS_INF, self.ub_incl | ra.ub_incl
|
|
elif PLUS_INF not in (self.ub, ra.ub) and sub == raub:
|
|
ub, ub_incl = self.ub, self.ub_incl | ra.ub_incl
|
|
else:
|
|
ub = real_highest(self.ub, ra.ub)
|
|
if ub == self.ub:
|
|
ub_incl = self.ub_incl
|
|
else:
|
|
ub_incl = ra.ub_incl
|
|
return ASN1RangeReal(lb, ub, lb_incl, ub_incl)
|
|
|
|
def diff(self, ra):
|
|
"""
|
|
returns a 2-tuple of ASN1RangeReal or None, which are the exclusive
|
|
parts of each self and `ra' ranges
|
|
"""
|
|
if not isinstance(ra, ASN1RangeReal):
|
|
return self, ra
|
|
#
|
|
if self.lb != MINUS_INF:
|
|
slb = real_to_float(self.lb)
|
|
if self.ub != PLUS_INF:
|
|
sub = real_to_float(self.ub)
|
|
if ra.lb != MINUS_INF:
|
|
ralb = real_to_float(ra.lb)
|
|
if ra.ub != PLUS_INF:
|
|
raub = real_to_float(ra.ub)
|
|
#
|
|
# disjoint sets:
|
|
if self.ub is not PLUS_INF and ra.lb is not MINUS_INF and sub < ralb:
|
|
return self, ra
|
|
elif ra.ub is not PLUS_INF and self.lb is not MINUS_INF and raub < slb:
|
|
return ra, self
|
|
#
|
|
# intersecting sets
|
|
# lower set
|
|
if MINUS_INF == self.lb == ra.lb or \
|
|
MINUS_INF not in (self.lb, ra.lb) and slb == ralb:
|
|
# no lower set
|
|
lset = None
|
|
else:
|
|
lset_lb = real_lowest(self.lb, ra.lb)
|
|
if lset_lb == self.lb:
|
|
lset_lb_incl = self.lb_incl
|
|
lset_ub = ra.lb
|
|
lset_ub_incl = not ra.lb_incl
|
|
else:
|
|
lset_lb_incl = ra.lb_incl
|
|
lset_ub = self.lb
|
|
lset_ub_incl = not self.lb_incl
|
|
lset = ASN1RangeReal(lset_lb, lset_ub, lset_lb_incl, lset_ub_incl)
|
|
# upper set
|
|
if PLUS_INF == self.ub == ra.ub or \
|
|
PLUS_INF not in (self.ub, ra.ub) and sub == raub:
|
|
uset = None
|
|
else:
|
|
uset_ub = real_highest(self.ub, ra.ub)
|
|
if uset_ub == self.ub:
|
|
uset_ub_incl = self.ub_incl
|
|
uset_lb = ra.ub
|
|
uset_lb_incl = not ra.ub_incl
|
|
else:
|
|
uset_ub_incl = ra.ub_incl
|
|
uset_lb = self.ub
|
|
uset_lb_incl = self.ub_incl
|
|
uset = ASN1RangeReal(uset_lb, uset_ub, uset_lb_incl, uset_ub_incl)
|
|
#
|
|
return lset, uset
|
|
|
|
|
|
def reduce_rangelist(rl=[]):
|
|
"""
|
|
reduces a list of ranges by reuniting intersecting ones
|
|
"""
|
|
# reduced list, to be returned
|
|
red = []
|
|
#
|
|
for r in rl:
|
|
if r is None:
|
|
pass
|
|
else:
|
|
# check in case this range can get united within some previous one(s)
|
|
u, united = None, []
|
|
for rr in red:
|
|
u = rr.unite(r)
|
|
if u is not None:
|
|
united.append(red.index(rr))
|
|
# r is growing...
|
|
r = u
|
|
# remove from red all ranges united with r
|
|
for i in united[::-1]:
|
|
del red[i]
|
|
red.append(r)
|
|
return red
|
|
|
|
|
|
#------------------------------------------------------------------------------#
|
|
# set of ASN.1 values or range of values
|
|
#------------------------------------------------------------------------------#
|
|
# WNG: in case of TYPE_REAL values, they are managerd in their 3-tuple format
|
|
# hence test for inclusion may fail
|
|
|
|
class ASN1Set(object):
|
|
"""
|
|
Class to handle a set of (range of) values for any ASN.1 types
|
|
|
|
_rr : list with all individual values in the root set
|
|
_rv : list with all ranges of values in the root set
|
|
_ev : None (if not extendable) or list with all individual values in the
|
|
extension set
|
|
_er : list with all ranges of values in the extension set
|
|
|
|
root : ordered list with all individual and ranges of values in the root set
|
|
ext : ordered list with all individual and ranges of values in the
|
|
extension set
|
|
"""
|
|
def __init__(self, d={'root':[], 'ext':None}):
|
|
self._rr = reduce_rangelist([v for v in d['root'] if isinstance(v, ASN1Range)])
|
|
self._rv = []
|
|
self._rv = [v for v in d['root'] if not isinstance(v, ASN1Range) and not self.in_root(v)]
|
|
if d['ext'] is not None:
|
|
self._er = reduce_rangelist([v for v in d['ext'] if isinstance(v, ASN1Range)])
|
|
self._ev = []
|
|
self._ev = [v for v in d['ext'] if not isinstance(v, ASN1Range) and \
|
|
not self.in_root(v) and not self.in_ext(v)]
|
|
else:
|
|
self._er = []
|
|
self._ev = None
|
|
self._init()
|
|
|
|
def _init(self):
|
|
"""
|
|
creates the `root' and `ext' attributes which lists all values and
|
|
ranges of their domain in order
|
|
"""
|
|
self.root = []
|
|
rv, rr = self._rv, [_rr.lb for _rr in self._rr]
|
|
rv_off = 0
|
|
if rr and rr[0] is None:
|
|
self.root.append(self._rr[0])
|
|
rr_off = 1
|
|
else:
|
|
rr_off = 0
|
|
while rv_off < len(rv) and rr_off < len(rr):
|
|
if rv[rv_off] < rr[rr_off]:
|
|
self.root.append(self._rv[rv_off])
|
|
rv_off += 1
|
|
else:
|
|
self.root.append(self._rr[rr_off])
|
|
rr_off += 1
|
|
if rv_off < len(rv):
|
|
self.root.extend( self._rv[rv_off:] )
|
|
elif rr_off < len(rr):
|
|
self.root.extend( self._rr[rr_off:] )
|
|
#
|
|
if self._ev is not None:
|
|
self.ext = []
|
|
ev, er = self._ev, [_er.lb for _er in self._er]
|
|
ev_off = 0
|
|
if er and er[0] is None:
|
|
self.ext.append(self._er[0])
|
|
er_off = 1
|
|
else:
|
|
er_off = 0
|
|
while ev_off < len(ev) and er_off < len(er):
|
|
if ev[ev_off] < er[er_off]:
|
|
self.ext.append(self._ev[ev_off])
|
|
ev_off += 1
|
|
else:
|
|
self.ext.append(self._er[er_off])
|
|
er_off += 1
|
|
if ev_off < len(ev):
|
|
self.ext.extend( self._ev[ev_off:] )
|
|
elif er_off < len(er):
|
|
self.ext.extend( self._er[er_off:] )
|
|
else:
|
|
self.ext = None
|
|
|
|
def __repr__(self):
|
|
root = '[' + ', '.join([repr(r) for r in self.root]) + ']'
|
|
if self.ext is None:
|
|
ext = 'None'
|
|
else:
|
|
ext = '[' + ', '.join([repr(e) for e in self.ext]) + ']'
|
|
return 'ASN1Set(root={0}, ext={1})'.format(root, ext)
|
|
|
|
def is_empty(self):
|
|
return self._rv == [] and self._rr == []
|
|
|
|
def is_ext(self):
|
|
return self._ev is not None
|
|
|
|
def __contains__(self, v):
|
|
if self._CONTAIN_WEXT:
|
|
return self.in_root(v) or self.in_ext(v)
|
|
else:
|
|
return self.in_root(v)
|
|
|
|
def in_root(self, v):
|
|
for r in self._rr:
|
|
if v in r:
|
|
return True
|
|
if v in self._rv:
|
|
return True
|
|
return False
|
|
|
|
def in_ext(self, v):
|
|
if self._ev is None:
|
|
return False
|
|
for r in self._er:
|
|
if v in r:
|
|
return True
|
|
if v in self._ev:
|
|
return True
|
|
return False
|
|
|
|
def intersect(self, S):
|
|
"""
|
|
returns an ASN1Set which root part is the intersection or the root parts
|
|
of self and S, and ext part contains all remaining defined values if self
|
|
and S are extensible
|
|
"""
|
|
ret_root, ret_root_r, = [], []
|
|
# 1) check if ret is extensible
|
|
if self.is_ext() and S.is_ext():
|
|
ret_ext = []
|
|
else:
|
|
ret_ext = None
|
|
# 2) get the intersection of root ranges
|
|
if self._rr and S._rr:
|
|
for r in S._rr:
|
|
# list of intersection with all root ranges of self
|
|
inter = [r.intersect(sr) for sr in self._rr]
|
|
for r in inter:
|
|
if r is not None:
|
|
if r.lb is not None and r.lb == r.ub:
|
|
# range with a single value
|
|
ret_root.append(r.lb)
|
|
else:
|
|
ret_root_r.append(r)
|
|
ret = ASN1Set()
|
|
ret._rv, ret._rr, ret._ev = ret_root, ret_root_r, ret_ext
|
|
ret._init()
|
|
# 3) check the root individual values
|
|
for v in self._rv:
|
|
if S.in_root(v) and not ret.in_root(v):
|
|
ret._rv.append(v)
|
|
for v in S.root:
|
|
if self.in_root(v) and not ret.in_root(v):
|
|
ret._rv.append(v)
|
|
# 4) build ret extension
|
|
if ret_ext is not None:
|
|
# 4.1) gather both self and S root and extension ranges and remove
|
|
# ret._rv and ret._rr parts of it to put in ret._er
|
|
union = reduce_rangelist(self._rr + S._rr + self._er + S._er)
|
|
ret._er = union
|
|
# TODO: doing holes in ext_r ...
|
|
# ret.ext_r = union - (ret.root_r + ret.root)
|
|
#
|
|
# 4.2) gather both self and S extension individual values
|
|
for v in self._ev:
|
|
if not ret.in_root(v) and not ret.in_ext(v):
|
|
ret._ev.append(v)
|
|
for v in S._ev:
|
|
if not ret.in_root(v) and not ret.in_ext(v):
|
|
ret._ev.append(v)
|
|
# 4.3) add self and S root values not intersecting
|
|
for v in self.root:
|
|
if not ret.in_root(v) and not ret.in_ext(v):
|
|
ret._ev.append(v)
|
|
for v in S.root:
|
|
if not ret.in_root(v) and not ret.in_ext(v):
|
|
ret._ev.append(v)
|
|
ret._init()
|
|
return ret
|
|
|
|
|
|
def reduce_setdicts(sdl):
|
|
"""
|
|
gets a list of set dicts of a given type (i.e. constraints of type CONST_VAL
|
|
or CONST_SIZE), and reduce them by intersecting their root part,
|
|
and extending their ext part, into a single ASN1Set that is to be returned
|
|
"""
|
|
return reduce_sets([ASN1Set(sd) for sd in sdl])
|
|
|
|
def reduce_sets(sl):
|
|
"""
|
|
gets a list of ASN1Set for a given type, and reduce them by intersecting
|
|
their root part, and extending their ext part, into a single ASN1Set that
|
|
is to be returned
|
|
"""
|
|
return reduce(lambda a, b: a.intersect(b), sl[::-1])
|
|
|