Commits

rkruppe  committed 9b4f98a

change rawbuf interface to support vectors (side effect: saner handling of scalar types)

  • Participants
  • Parent commits bb67500

Comments (0)

Files changed (4)

File ttgx/dodecs.py

         self.__indices = {}
         self.__entities = []
 {%- for name, t in attrs.items() %}
-        self._{{name}} = ttgx.rawbuf.Array(ttgx.rawbuf.{{t}})
+        self._{{name}} = ttgx.rawbuf.array({{t|repr}})
         self.{{name}} = _{{clsname}}AttributeView(
             self, self.__indices, self._{{name}})
 {%- endfor %}

File ttgx/rawbuf.py

 
 
 ffi = cffi.FFI()
-I32 = 'int32_t'
-U32 = 'uint32_t'
-F32 = 'double'
-F64 = 'double'
-Bool = '_Bool'
+dtype_alias = dict(
+    I32='int32_t',
+    U32='uint32_t',
+    F64='double',
+    Bool='_Bool',
+)
 
 
-def alloc(dtype, n):
-    return ffi.new(dtype + '[]', n)
+def array(dtype, size=0):
+    if isinstance(dtype, str):
+        lltype = dtype_alias[dtype] + '[]'
+        return _Array(dtype, lltype, size)
+    else:
+        item_dtype, width = dtype
+        item_lltype = dtype_alias[item_dtype]
+        lltype = item_lltype + '[][' + str(width) + ']'
+        return _VectorArray(dtype, lltype, size)
 
 
-class Array:
+class _Array:
     """An over-allocating dynamic array with primitive elements.
 
     Backed by a buffer whose size may exceed the size of the array
     dramatically, to perform some operations more efficiently.
     """
-    def __init__(self, dtype, size=0):
+    def __init__(self, dtype, lltype, size):
         self.dtype = dtype
+        self.lltype = lltype
         capacity = 0
         if size > 0:
             capacity = 2 ** size.bit_length()
-        self._data = alloc(dtype, capacity)
+        self._data = ffi.new(lltype, capacity)
         self._used = size
 
     @property
         """
         if self._used == self.capacity:
             new_capacity = max(self.capacity * 2, 1)
-            self._data, old_data = alloc(self.dtype, new_capacity), self._data
-            self._data[0:self._used] = old_data
+            new_data = ffi.new(self.lltype, new_capacity)
+            new_data[0:self._used] = self._data
+            self._data = new_data
         self._data[self._used] = x
         self._used += 1
 
 
     def __getitem__(self, i):
         if not (0 <= i < self._used):
-            raise IndexError("{} out of range(0, {})".format(i, self._used))
+            raise IndexError("{} out of range({})".format(i, self._used))
         return self._data[i]
 
     def __setitem__(self, i, x):
         if not (0 <= i < self._used):
-            raise IndexError("{} out of range(0, {})".format(i, self._used))
+            raise IndexError("{} out of range({})".format(i, self._used))
         self._data[i] = x
 
     def __repr__(self):
             yield ']'
         return ' '.join(parts())
 
+
+class _VectorArray(_Array):
+    # only inherits from _Array to avoid code duplication
+    def __getitem__(self, i):
+        return tuple(_Array.__getitem__(self, i))

File ttgx/tests/test_dodecs.py

 
 ValueC = component('ValueC', 'value', val='I32')
 PositionC = component('PositionC', 'position', x='U32', y='U32')
+VectorC = component('VectorC', 'vector', vec=['I32', 3])
 ScalarC = scalar_component('ScalarC', 'scalar', 'a b')
 
 ##

File ttgx/tests/test_rawbuf.py

 import ttgx.rawbuf as rawbuf
 
 
-_INT_TYPES = [rawbuf.I32, rawbuf.U32, rawbuf.Bool]
-_TYPES = _INT_TYPES + [rawbuf.F64, rawbuf.F32]
-_X = {rawbuf.Bool: 1, rawbuf.F32: 0.5, rawbuf.F64: -4.01}
-_Y = {rawbuf.Bool: 0, rawbuf.F32: -1.0, rawbuf.F64: 47.1}
+_TYPES = ['I32', 'U32', 'Bool', 'F64']
+_X = {'Bool': 1, 'F64': -4.01}
+_Y = {'Bool': 0, 'F64': 47.1}
 
 def pytest_generate_tests(metafunc):
     if 'empty_arr' in metafunc.fixturenames:
-        param, vals = 'empty_arr', [rawbuf.Array(t) for t in _TYPES]
+        param, vals = 'empty_arr', [rawbuf.array(t, 0) for t in _TYPES]
     elif 'arr' in metafunc.fixturenames:
-        param, vals = 'arr', [rawbuf.Array(t, 5) for t in _TYPES]
+        param, vals = 'arr', [rawbuf.array(t, 5) for t in _TYPES]
+    elif 'vecs' in metafunc.fixturenames:
+        param, vals = 'vecs', [rawbuf.array([t, 2], 5) for t in _TYPES]
     else:
         return
     metafunc.parametrize(param, vals, ids=_TYPES)
 
 
 def _val(arr, values, default, offset=None):
-    t = arr.dtype
+    if isinstance(arr.dtype, str):
+        t = arr.dtype
+    else:
+        t, _ = arr.dtype
     val = values.get(t, default)
     if offset is not None:
         val += offset
-    if arr.dtype == rawbuf.Bool:
+    if arr.dtype == 'Bool':
         val %= 2
     return val
 
         expected = 'Array[ {0} {1} {0} {0} {0} ]'.format(_x(arr), _y(arr))
         assert repr(arr) == expected
 
+
+class TestVectorArray:
+    #TODO switch from tuples to a proper vector type (numpy.array perhaps?)
+    def test_get_returns_tuple(self, vecs):
+        assert vecs[2] == (0, 0)
+
+    def test_set_accepts_tuple(self, vecs):
+        v = (_x(vecs), _y(vecs))
+        vecs[1] = v
+        assert vecs[1] == v