From 438ec42574407b7c5a54aa9d21a78b9c0f86cba1 Mon Sep 17 00:00:00 2001 From: Peter Hamilton Date: Thu, 7 Mar 2019 16:29:36 -0500 Subject: [PATCH] Add bit mask enumeration utilities This changes adds several utilities for working with bit mask enumerations, including functions to compute bit masks from lists of enumeration values and vice versa. Unit tests have been added to cover these new utilities. --- kmip/core/enums.py | 79 ++++++++++++++++++++++++++++-- kmip/tests/unit/core/test_enums.py | 57 +++++++++++++++++++++ 2 files changed, 133 insertions(+), 3 deletions(-) diff --git a/kmip/core/enums.py b/kmip/core/enums.py index dd0d888..d2e9921 100644 --- a/kmip/core/enums.py +++ b/kmip/core/enums.py @@ -18,6 +18,7 @@ import copy import enum +import functools import six @@ -1814,14 +1815,86 @@ def convert_attribute_tag_to_name(value): raise ValueError("Unrecognized attribute tag: {}".format(value)) +def get_bit_mask_from_enumerations(enumerations): + """ + A utility function that computes a bit mask from a collection of + enumeration values. -def is_enum_value(enum_class, potential_value): + Args: + enumerations (list): A list of enumeration values to be combined in a + composite bit mask. + + Returns: + int: The composite bit mask. + """ + return functools.reduce( + lambda x, y: x | y, [z.value for z in enumerations] + ) + + +def get_enumerations_from_bit_mask(enumeration, mask): + """ + A utility function that creates a list of enumeration values from a bit + mask for a specific mask enumeration class. + + Args: + enumeration (class): The enumeration class from which to draw + enumeration values. + mask (int): The bit mask from which to identify enumeration values. + + Returns: + list: A list of enumeration values corresponding to the bit mask. + """ + return [x for x in enumeration if (x.value & mask) == x.value] + + +def is_bit_mask(enumeration, potential_mask): + """ + A utility function that checks if the provided value is a composite bit + mask of enumeration values in the specified enumeration class. + + Args: + enumeration (class): One of the mask enumeration classes found in this + file. These include: + * Cryptographic Usage Mask + * Protection Storage Mask + * Storage Status Mask + potential_mask (int): A potential bit mask composed of enumeration + values belonging to the enumeration class. + + Returns: + True: if the potential mask is a valid bit mask of the mask enumeration + False: otherwise + """ + if not isinstance(potential_mask, six.integer_types): + return False + + mask_enumerations = ( + CryptographicUsageMask, + ProtectionStorageMask, + StorageStatusMask + ) + if enumeration not in mask_enumerations: + return False + + mask = 0 + for value in [e.value for e in enumeration]: + if (value & potential_mask) == value: + mask |= value + + if mask != potential_mask: + return False + + return True + + +def is_enum_value(enumeration, potential_value): """ A utility function that checks if the enumeration class contains the provided value. Args: - enum_class (class): One of the enumeration classes found in this file. + enumeration (class): One of the enumeration classes found in this file. potential_value (int, string): A potential value of the enumeration class. @@ -1830,7 +1903,7 @@ def is_enum_value(enum_class, potential_value): False: otherwise """ try: - enum_class(potential_value) + enumeration(potential_value) except ValueError: return False diff --git a/kmip/tests/unit/core/test_enums.py b/kmip/tests/unit/core/test_enums.py index db67943..c0c9197 100644 --- a/kmip/tests/unit/core/test_enums.py +++ b/kmip/tests/unit/core/test_enums.py @@ -87,6 +87,63 @@ class TestEnumUtilityFunctions(testtools.TestCase): def tearDown(self): super(TestEnumUtilityFunctions, self).tearDown() + def test_get_bit_mask_from_enumerations(self): + self.assertEqual( + 7, + enums.get_bit_mask_from_enumerations( + [ + enums.StorageStatusMask.ARCHIVAL_STORAGE, + enums.StorageStatusMask.DESTROYED_STORAGE, + enums.StorageStatusMask.ONLINE_STORAGE + ] + ) + ) + + def test_get_enumerations_from_bit_mask(self): + expected = [ + enums.StorageStatusMask.ARCHIVAL_STORAGE, + enums.StorageStatusMask.DESTROYED_STORAGE, + enums.StorageStatusMask.ONLINE_STORAGE + ] + observed = enums.get_enumerations_from_bit_mask( + enums.StorageStatusMask, + 7 + ) + + self.assertEqual(len(expected), len(observed)) + for x in expected: + self.assertIn(x, observed) + + def test_is_bit_mask(self): + self.assertTrue( + enums.is_bit_mask( + enums.StorageStatusMask, + enums.StorageStatusMask.ARCHIVAL_STORAGE.value | + enums.StorageStatusMask.ONLINE_STORAGE.value + ) + ) + + self.assertFalse( + enums.is_bit_mask( + enums.StorageStatusMask, + enums.StorageStatusMask.DESTROYED_STORAGE + ) + ) + + self.assertFalse( + enums.is_bit_mask( + enums.WrappingMethod, + enums.WrappingMethod.ENCRYPT.value + ) + ) + + self.assertFalse( + enums.is_bit_mask( + enums.ProtectionStorageMask, + 0x80000000 + ) + ) + def test_is_enum_value(self): result = enums.is_enum_value( enums.CryptographicAlgorithm,