The class slots define component fields in a more grounded way.
This greatly simplifies definition of parseable compoments.

The first `__slots__` tuple in each class defines a constant list of
fields and also the corresponding binary ones in the C struct.
`_BIN_FIELD_NUM` ensures that subsequent slot additions are ignored by
CStruct as they must be constructed by the owning class itself.

For complex parsing the class method `parse` needs to be overridden,
see `CellConfig`.

Signed-off-by: Andrej Utz <andrej....@st.oth-regensburg.de>
---
 pyjailhouse/config_parser.py | 169 +++++++++++++++++++----------------
 1 file changed, 92 insertions(+), 77 deletions(-)

diff --git a/pyjailhouse/config_parser.py b/pyjailhouse/config_parser.py
index a6780d61..961d3062 100644
--- a/pyjailhouse/config_parser.py
+++ b/pyjailhouse/config_parser.py
@@ -23,6 +23,47 @@ from .extendedenum import ExtendedEnum
 _CONFIG_REVISION = 13
 
 
+class CStruct:
+    def _slots(self):
+        attrs = []
+        all_attr = [getattr(cls, '__slots__', ())
+                    for cls in type(self).__mro__]
+        for cls in all_attr:
+            num = getattr(self, '_BIN_FIELD_NUM', 0)
+            if len(cls) < num:
+                continue
+
+            for i in range(num):
+                attrs += [cls[i]]
+
+        return attrs
+
+    @classmethod
+    def parse(cls, data):
+        obj, data = cls.parse_class(cls, data)
+        return obj, data
+
+    @staticmethod
+    def parse_class(cls, data):
+        obj = cls()
+        slots = obj._slots()
+        if len(slots) > 0:
+            data_tuple = cls._BIN_FMT.unpack_from(data)
+            for assigment in zip(slots, data_tuple):
+                setattr(obj, *assigment)
+
+        return obj, data[cls._BIN_FMT.size:]
+
+    @staticmethod
+    def parse_array(cls, num, data):
+        array = []
+        for i in range(num):
+            obj, data = cls.parse(data)
+            array += [obj]
+
+        return array, data
+
+
 def flag_str(enum_class, value, separator=' | '):
     flags = []
     while value:
@@ -51,7 +92,9 @@ class JAILHOUSE_MEM(ExtendedEnum, int):
     }
 
 
-class MemRegion:
+class MemRegion(CStruct):
+    __slots__ = 'phys_start', 'virt_start', 'size', 'flags',
+    _BIN_FIELD_NUM = len(__slots__)
     _BIN_FMT = struct.Struct('QQQQ')
 
     def __init__(self):
@@ -66,15 +109,6 @@ class MemRegion:
                ("  size:       0x%016x\n" % self.size) + \
                ("  flags:      " + flag_str(JAILHOUSE_MEM, self.flags))
 
-    @classmethod
-    def parse(cls, data):
-        self = cls()
-        (self.phys_start,
-         self.virt_start,
-         self.size,
-         self.flags) = cls._BIN_FMT.unpack_from(data)
-        return self
-
     def is_ram(self):
         return ((self.flags & (JAILHOUSE_MEM.READ |
                                JAILHOUSE_MEM.WRITE |
@@ -112,11 +146,15 @@ class MemRegion:
             self.virt_address_in_region(region.virt_start)
 
 
-class CacheRegion:
+class CacheRegion(CStruct):
+    __slots__ = 'start', 'size', 'type', 'flags',
+    _BIN_FIELD_NUM = len(__slots__)
     _BIN_FMT = struct.Struct('IIBxH')
 
 
-class Irqchip:
+class Irqchip(CStruct):
+    __slots__ = 'address', 'id', 'pin_base', 'pin_bitmap_lo', 'pin_bitmap_hi',
+    _BIN_FIELD_NUM = len(__slots__)
     _BIN_FMT = struct.Struct('QIIQQ')
 
     def __init__(self):
@@ -126,36 +164,29 @@ class Irqchip:
         self.pin_bitmap_lo = 0
         self.pin_bitmap_hi = 0
 
-    @classmethod
-    def parse(cls, data):
-        self = cls()
-        (self.address,
-         self.id,
-         self.pin_base,
-         self.pin_bitmap_lo,
-         self.pin_bitmap_hi) = cls._BIN_FMT.unpack_from(data)
-        return self
-
     def is_standard(self):
         return self.address == 0xfec00000
 
 
-class PIORegion:
+class PIORegion(CStruct):
+    __slots__ = 'base', 'length',
+    _BIN_FIELD_NUM = len(__slots__)
     _BIN_FMT = struct.Struct('HH')
 
     def __init__(self):
         self.base = 0
         self.length = 0
 
-    @classmethod
-    def parse(cls, data):
-        self = cls()
-        (self.base, self.length) = cls._BIN_FMT.unpack_from(data)
-        return self
-
 
-class CellConfig:
-    _BIN_FMT = struct.Struct('=6sH32s4xIIIIIIIIIIQ8x32x')
+class CellConfig(CStruct):
+    # slots with a '_' prefix in name are private
+    __slots__ = 'name', '_flags', '_cpu_sets', \
+                'memory_regions', 'cache_regions', 'irqchips', 'pio_regions', \
+                '_pci_devices', '_pci_caps', '_stream_ids', \
+                'vpci_irq_base', 'cpu_reset_address',
+    _BIN_FIELD_NUM = len(__slots__)
+    _BIN_FMT_HDR = struct.Struct('=6sH')
+    _BIN_FMT = struct.Struct('=32s4xIIIIIIIIIIQ8x32x')
 
     def __init__(self):
         self.name = ""
@@ -167,78 +198,62 @@ class CellConfig:
 
     @classmethod
     def parse(cls, data, root_cell=False):
-        self = cls()
         try:
-            (signature,
-             revision,
-             name,
-             self.flags,
-             self.cpu_set_size,
-             self.num_memory_regions,
-             self.num_cache_regions,
-             self.num_irqchips,
-             self.num_pio_regions,
-             self.num_pci_devices,
-             self.num_pci_caps,
-             self.num_stream_ids,
-             self.vpci_irq_base,
-             self.cpu_reset_address) = cls._BIN_FMT.unpack_from(data)
             if not root_cell:
+                (signature, revision) = cls._BIN_FMT_HDR.unpack_from(data)
                 if signature != b'JHCELL':
                     raise RuntimeError('Not a cell configuration')
                 if revision != _CONFIG_REVISION:
                     raise RuntimeError('Configuration file revision mismatch')
-            self.name = str(name.decode().strip('\0'))
-
-            mem_region_offs = cls._BIN_FMT.size + self.cpu_set_size
-            for n in range(self.num_memory_regions):
-                
self.memory_regions.append(MemRegion.parse(data[mem_region_offs:]))
-                mem_region_offs += MemRegion._BIN_FMT.size
-
-            irqchip_offs = mem_region_offs + \
-                self.num_cache_regions * CacheRegion._BIN_FMT.size
-            for n in range(self.num_irqchips):
-                self.irqchips.append(Irqchip.parse(data[irqchip_offs:]))
-                irqchip_offs += Irqchip._BIN_FMT.size
-
-            pioregion_offs = irqchip_offs
-            for n in range(self.num_pio_regions):
-                self.pio_regions.append(PIORegion.parse(data[pioregion_offs:]))
-                pioregion_offs += PIORegion._BIN_FMT.size
+                data = data[cls._BIN_FMT_HDR.size:]
+
+            self, data = cls.parse_class(cls, data)
+            self.name = self.name.decode().strip('\0')
+            data = data[self._cpu_sets:] # skip CPU set
+
+            self.memory_regions, data = \
+                cls.parse_array(MemRegion, self.memory_regions, data)
+            self.cache_regions, data = \
+                cls.parse_array(CacheRegion, self.cache_regions, data)
+            self.irqchips, data = \
+                cls.parse_array(Irqchip, self.irqchips, data)
+            self.pio_regions, data = \
+                cls.parse_array(PIORegion, self.pio_regions, data)
+
+            return self
         except struct.error:
             raise RuntimeError('Not a %scell configuration' %
                                ('root ' if root_cell else ''))
 
-        return self
-
 
-class SystemConfig:
-    _BIN_FMT = struct.Struct('=6sH4x')
+class SystemConfig(CStruct):
+    _BIN_FMT = struct.Struct('=4x')
     # ...followed by MemRegion as hypervisor memory
     _BIN_FMT_CONSOLE_AND_PLATFORM = struct.Struct('32x12x224x44x')
 
+    # constructed fields
+    __slots__ = 'hypervisor_memory', 'root_cell',
+
     def __init__(self):
         self.hypervisor_memory = MemRegion()
         self.root_cell = CellConfig()
 
     @classmethod
     def parse(cls, data):
-        self = cls()
         try:
-            (signature, revision) = cls._BIN_FMT.unpack_from(data)
-
+            hdr_fmt = CellConfig._BIN_FMT_HDR
+            (signature, revision) = hdr_fmt.unpack_from(data)
             if signature != b'JHSYST':
                 raise RuntimeError('Not a root cell configuration')
             if revision != _CONFIG_REVISION:
                 raise RuntimeError('Configuration file revision mismatch')
 
-            offs = cls._BIN_FMT.size
-            self.hypervisor_memory = MemRegion.parse(data[offs:])
+            self, data = cls.parse_class(cls, data[hdr_fmt.size:])
+            self.hypervisor_memory, data = MemRegion.parse(data)
 
-            offs += MemRegion._BIN_FMT.size
-            offs += SystemConfig._BIN_FMT_CONSOLE_AND_PLATFORM.size
+            offs = cls._BIN_FMT_CONSOLE_AND_PLATFORM.size
+            offs += CellConfig._BIN_FMT_HDR.size # skip header inside rootcell
+            self.root_cell = CellConfig.parse(data[offs:], root_cell=True)
+            return self
         except struct.error:
             raise RuntimeError('Not a root cell configuration')
-
-        self.root_cell = CellConfig.parse(data[offs:], root_cell=True)
-        return self
-- 
2.28.0

-- 
You received this message because you are subscribed to the Google Groups 
"Jailhouse" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to jailhouse-dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/jailhouse-dev/20200825145032.115837-4-andrej.utz%40st.oth-regensburg.de.

Reply via email to