[Numpy-svn] r8316 - in trunk/numpy/core: . tests

numpy-svn at scipy.org numpy-svn at scipy.org
Sun Apr 4 16:20:48 EDT 2010


Author: ptvirtan
Date: 2010-04-04 15:20:48 -0500 (Sun, 04 Apr 2010)
New Revision: 8316

Modified:
   trunk/numpy/core/_internal.py
   trunk/numpy/core/tests/test_multiarray.py
Log:
ENH: core: improve PEP 3118 parser's alignment handling

Now, padding will be more forcefully inserted for native-aligned items.

Modified: trunk/numpy/core/_internal.py
===================================================================
--- trunk/numpy/core/_internal.py	2010-04-04 20:20:40 UTC (rev 8315)
+++ trunk/numpy/core/_internal.py	2010-04-04 20:20:48 UTC (rev 8316)
@@ -403,9 +403,23 @@
 
     fields = {}
     offset = 0
-    findex = 0
     explicit_name = False
+    this_explicit_name = False
+    common_alignment = 1
+    is_padding = False
+    last_offset = 0
 
+    dummy_name_index = [0]
+    def next_dummy_name():
+        dummy_name_index[0] += 1
+    def get_dummy_name():
+        while True:
+            name = 'f%d' % dummy_name_index[0]
+            if name not in fields:
+                return name
+            next_dummy_name()
+
+    # Parse spec
     while spec:
         value = None
 
@@ -448,11 +462,9 @@
         is_padding = False
 
         if spec[:2] == 'T{':
-            value, spec = _dtype_from_pep3118(spec[2:], byteorder=byteorder,
-                                              is_subdtype=True)
-            if itemsize != 1:
-                # Not supported
-                raise ValueError("Non item-size 1 structures not supported")
+            value, spec, align = _dtype_from_pep3118(spec[2:],
+                                                     byteorder=byteorder,
+                                                     is_subdtype=True)
         elif spec[0] in type_map_chars:
             if spec[0] == 'Z':
                 j = 2
@@ -467,6 +479,7 @@
                 itemsize = 1
             numpy_byteorder = {'@': '=', '^': '='}.get(byteorder, byteorder)
             value = dtype(numpy_byteorder + dtypechar)
+            align = value.alignment
         else:
             raise ValueError("Unknown PEP 3118 data type specifier %r" % spec)
 
@@ -476,8 +489,8 @@
         #      that the start of the array is *also* aligned.
         extra_offset = 0
         if byteorder == '@':
-            start_padding = (-offset) % value.alignment
-            intra_padding = (-value.itemsize) % value.alignment
+            start_padding = (-offset) % align
+            intra_padding = (-value.itemsize) % align
 
             offset += start_padding
 
@@ -488,6 +501,10 @@
                 else:
                     extra_offset += intra_padding
 
+            # Update common alignment
+            common_alignment = (align*common_alignment
+                                / _gcd(align, common_alignment))
+
         # Convert itemsize to sub-array
         if itemsize != 1:
             value = dtype((value, (itemsize,)))
@@ -505,20 +522,37 @@
             explicit_name = True
             this_explicit_name = True
         else:
-            name = 'f%d' % findex
-            findex += 1
+            name = get_dummy_name()
 
         if not is_padding or this_explicit_name:
+            if name in fields:
+                raise RuntimeError("Duplicate field name '%s' in PEP3118 format"
+                                   % name)
             fields[name] = (value, offset)
+            if not this_explicit_name:
+                next_dummy_name()
+
+        last_offset = offset
         offset += value.itemsize
         offset += extra_offset
 
+    if is_padding and not this_explicit_name:
+        # Trailing padding must be made explicit
+        name = get_dummy_name()
+        fields[name] = ('V%d' % (offset - last_offset), last_offset)
+
     if len(fields.keys()) == 1 and not explicit_name and fields['f0'][1] == 0:
         ret = fields['f0'][0]
     else:
         ret = dtype(fields)
 
     if is_subdtype:
-        return ret, spec
+        return ret, spec, common_alignment
     else:
         return ret
+
+def _gcd(a, b):
+    """Calculate the greatest common divisor of a and b"""
+    while b:
+        a, b = b, a%b
+    return a

Modified: trunk/numpy/core/tests/test_multiarray.py
===================================================================
--- trunk/numpy/core/tests/test_multiarray.py	2010-04-04 20:20:40 UTC (rev 8315)
+++ trunk/numpy/core/tests/test_multiarray.py	2010-04-04 20:20:48 UTC (rev 8316)
@@ -1456,6 +1456,32 @@
     if sys.version_info[:2] == (2, 6):
         from numpy.core.multiarray import memorysimpleview as memoryview
 
+    from numpy.core._internal import _dtype_from_pep3118
+
+    class TestPEP3118Dtype(object):
+        def _check(self, spec, wanted):
+            assert_equal(_dtype_from_pep3118(spec), np.dtype(wanted),
+                         err_msg="spec %r != dtype %r" % (spec, wanted))
+
+        def test_native_padding(self):
+            align = np.dtype('i').alignment
+            for j in xrange(8):
+                if j == 0:
+                    s = 'bi'
+                else:
+                    s = 'b%dxi' % j
+                self._check('@'+s, {'f0': ('i1', 0),
+                                    'f1': ('i', align*(1 + j//align))})
+                self._check('='+s, {'f0': ('i1', 0),
+                                    'f1': ('i', 1+j)})
+
+        def test_native_padding_2(self):
+            self._check('x3T{xi}', {'f0': (({'f0': ('i', 4)}, (3,)), 4)})
+            self._check('=x3T{xi}', {'f0': (({'f0': ('i', 1)}, (3,)), 1)})
+
+        def test_trailing_padding(self):
+            self._check('ix', [('f0', 'i'), ('f1', 'V1')])
+
     class TestNewBufferProtocol(object):
         def _check_roundtrip(self, obj):
             obj = np.asarray(obj)




More information about the Numpy-svn mailing list