def object_path_component_cmp(comp1, comp2): """ Compare a string/int to another string/int; this induces an ordering over all strings and ints. It is used to perform a lexicographical sort on object paths. Ints and strings compare as usual to each other; ints compare less than strings. :param comp1: An object path component (string or int) :param comp2: An object path component (string or int) :return: <0, 0, or >0 depending on whether the first arg is less, equal or greater than the second """ # both ints or both strings: use builtin comparison operators if (isinstance(comp1, int) and isinstance(comp2, int)) \ or (isinstance(comp1, str) and isinstance(comp2, str)): result = generic_cmp(comp1, comp2) # one is int, one is string. Let's say ints come before strings. elif isinstance(comp1, int): result = -1 else: result = 1 return result
def generic_constant_cmp(const1, const2): """ Generic comparator for most _Constant instances. They must have a "value" attribute whose value supports the builtin comparison operators. :param const1: The first _Constant instance :param const2: The second _Constant instance :return: <0, 0, or >0 depending on whether the first arg is less, equal or greater than the second """ return generic_cmp(const1.value, const2.value)
def hex_cmp(value1, value2): """ Compare two STIX "hex" values. This decodes to bytes and compares that. It does *not* do a string compare on the hex representations. :param value1: The first HexConstant :param value2: The second HexConstant :return: <0, 0, or >0 depending on whether the first arg is less, equal or greater than the second """ bytes1 = bytes.fromhex(value1.value) bytes2 = bytes.fromhex(value2.value) return generic_cmp(bytes1, bytes2)
def bin_cmp(value1, value2): """ Compare two STIX "binary" values. This decodes to bytes and compares that. It does *not* do a string compare on the base64 representations. :param value1: The first BinaryConstant :param value2: The second BinaryConstant :return: <0, 0, or >0 depending on whether the first arg is less, equal or greater than the second """ bytes1 = base64.standard_b64decode(value1.value) bytes2 = base64.standard_b64decode(value2.value) return generic_cmp(bytes1, bytes2)
def comparison_operator_cmp(op1, op2): """ Compare two comparison operators. :param op1: The first comparison operator (a string) :param op2: The second comparison operator (a string) :return: <0, 0, or >0 depending on whether the first arg is less, equal or greater than the second """ op1_idx = _COMPARISON_OP_ORDER.index(op1) op2_idx = _COMPARISON_OP_ORDER.index(op2) result = generic_cmp(op1_idx, op2_idx) return result
def constant_cmp(value1, value2): """ Compare two constants. Args: value1: The first _Constant instance value2: The second _Constant instance Returns: <0, 0, or >0 depending on whether the first arg is less, equal or greater than the second """ # Special handling for ints/floats: treat them generically as numbers, # ordered before all other types. if isinstance(value1, (IntegerConstant, FloatConstant)) \ and isinstance(value2, (IntegerConstant, FloatConstant)): result = generic_constant_cmp(value1, value2) elif isinstance(value1, (IntegerConstant, FloatConstant)): result = -1 elif isinstance(value2, (IntegerConstant, FloatConstant)): result = 1 else: type1 = type(value1) type2 = type(value2) type1_idx = _CONSTANT_TYPE_ORDER.index(type1) type2_idx = _CONSTANT_TYPE_ORDER.index(type2) result = generic_cmp(type1_idx, type2_idx) if result == 0: # Types are the same; must compare values cmp_func = _CONSTANT_COMPARATORS.get(type1) if not cmp_func: raise TypeError("Don't know how to compare " + type1.__name__) result = cmp_func(value1, value2) return result
def observation_expression_cmp(expr1, expr2): """ Compare two observation expression ASTs. This is sensitive to the order of the expressions' sub-components. To achieve an order-insensitive comparison, the ASTs must be canonically ordered first. Args: expr1: The first observation expression expr2: The second observation expression Returns: <0, 0, or >0 depending on whether the first arg is less, equal or greater than the second """ type1 = type(expr1) type2 = type(expr2) type1_idx = _OBSERVATION_EXPRESSION_TYPE_ORDER.index(type1) type2_idx = _OBSERVATION_EXPRESSION_TYPE_ORDER.index(type2) if type1_idx != type2_idx: result = generic_cmp(type1_idx, type2_idx) # else, both exprs are of same type. # If they're simple, use contained comparison expression order elif type1 is ObservationExpression: result = comparison_expression_cmp( expr1.operand, expr2.operand, ) elif isinstance(expr1, _CompoundObservationExpression): # Both compound, and of same type (and/or/followedby): sort according # to contents. result = iter_lex_cmp( expr1.operands, expr2.operands, observation_expression_cmp, ) else: # QualifiedObservationExpression # Both qualified. Check qualifiers first; if they are the same, # use order of the qualified expressions. qual1_type = type(expr1.qualifier) qual2_type = type(expr2.qualifier) qual1_type_idx = _QUALIFIER_TYPE_ORDER.index(qual1_type) qual2_type_idx = _QUALIFIER_TYPE_ORDER.index(qual2_type) result = generic_cmp(qual1_type_idx, qual2_type_idx) if result == 0: # Same qualifier type; compare qualifier details qual_cmp = _QUALIFIER_COMPARATORS.get(qual1_type) if qual_cmp: result = qual_cmp(expr1.qualifier, expr2.qualifier) else: raise TypeError( "Can't compare qualifier type: " + qual1_type.__name__, ) if result == 0: # Same qualifier type and details; use qualified expression order result = observation_expression_cmp( expr1.observation_expression, expr2.observation_expression, ) return result