[pypy-commit] pypy faster-rstruct: make sure that struct.unpack_from uses the fast path as well

antocuni noreply at buildbot.pypy.org
Fri Nov 20 12:00:34 EST 2015


Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: faster-rstruct
Changeset: r80808:f2275be59406
Date: 2015-11-20 17:37 +0100
http://bitbucket.org/pypy/pypy/changeset/f2275be59406/

Log:	make sure that struct.unpack_from uses the fast path as well

diff --git a/pypy/module/struct/formatiterator.py b/pypy/module/struct/formatiterator.py
--- a/pypy/module/struct/formatiterator.py
+++ b/pypy/module/struct/formatiterator.py
@@ -154,9 +154,8 @@
         return self.pos
 
     def get_buffer_as_string_maybe(self):
-        # XXX: if self.buf is something different that StringBuffer, this has
-        # the effect to copy the whole string at each unpack!
-        return self.buf.as_str()
+        string, pos = self.buf.as_str_and_offset_maybe()
+        return string, pos+self.pos
 
     def skip(self, size):
         self.read(size) # XXX, could avoid taking the slice
diff --git a/pypy/module/struct/test/test_struct.py b/pypy/module/struct/test/test_struct.py
--- a/pypy/module/struct/test/test_struct.py
+++ b/pypy/module/struct/test/test_struct.py
@@ -462,3 +462,29 @@
         assert self.struct.unpack_from("ii", b, 2) == (17, 42)
         b[:sz] = self.struct.pack("ii", 18, 43)
         assert self.struct.unpack_from("ii", b) == (18, 43)
+
+
+class AppTestFastPath(object):
+    spaceconfig = dict(usemodules=['struct', '__pypy__'])
+
+    def setup_class(cls):
+        from rpython.rlib.rstruct import standardfmttable
+        standardfmttable.ALLOW_SLOWPATH = False
+        #
+        cls.w_struct = cls.space.appexec([], """():
+            import struct
+            return struct
+        """)
+        cls.w_bytebuffer = cls.space.appexec([], """():
+            import __pypy__
+            return __pypy__.bytebuffer
+        """)
+
+    def teardown_class(cls):
+        from rpython.rlib.rstruct import standardfmttable
+        standardfmttable.ALLOW_SLOWPATH = True
+
+    def test_unpack_from(self):
+        buf = self.struct.pack("iii", 0, 42, 43)
+        offset = self.struct.calcsize("i")
+        assert self.struct.unpack_from("ii", buf, offset) == (42, 43)
diff --git a/rpython/rlib/buffer.py b/rpython/rlib/buffer.py
--- a/rpython/rlib/buffer.py
+++ b/rpython/rlib/buffer.py
@@ -22,6 +22,14 @@
         # May be overridden.
         return self.getslice(0, self.getlength(), 1, self.getlength())
 
+    def as_str_and_offset_maybe(self):
+        """
+        If the buffer is backed by a string, return a pair (string, offset), where
+        offset is the offset inside the string where the buffer start.
+        Else, return (None, 0).
+        """
+        return None, 0
+
     def getitem(self, index):
         "Returns the index'th character in the buffer."
         raise NotImplementedError   # Must be overriden.  No bounds checks.
@@ -66,6 +74,9 @@
     def as_str(self):
         return self.value
 
+    def as_str_and_offset_maybe(self):
+        return self.value, 0
+
     def getitem(self, index):
         return self.value[index]
 
@@ -99,6 +110,12 @@
         else:
             return 0
 
+    def as_str_and_offset_maybe(self):
+        string, offset = self.buffer.as_str_and_offset_maybe()
+        if string is not None:
+            return string, offset+self.offset
+        return None, 0
+
     def getitem(self, index):
         return self.buffer.getitem(self.offset + index)
 
diff --git a/rpython/rlib/rstruct/standardfmttable.py b/rpython/rlib/rstruct/standardfmttable.py
--- a/rpython/rlib/rstruct/standardfmttable.py
+++ b/rpython/rlib/rstruct/standardfmttable.py
@@ -130,7 +130,8 @@
 
 # ____________________________________________________________
 
-ALLOW_FASTPATH = True # set to False by TestNoFastPath
+USE_FASTPATH = True    # set to False by some tests
+ALLOW_SLOWPATH = True  # set to False by some tests
 
 class CannotUnpack(Exception):
     pass
@@ -139,7 +140,7 @@
 def unpack_fastpath(TYPE, fmtiter):
     size = rffi.sizeof(TYPE)
     strbuf, pos = fmtiter.get_buffer_as_string_maybe()
-    if pos % size != 0 or strbuf is None or not ALLOW_FASTPATH:
+    if strbuf is None or pos % size != 0 or not USE_FASTPATH:
         raise CannotUnpack
     fmtiter.skip(size)
     return str_storage_getitem(TYPE, strbuf, pos)
@@ -226,6 +227,9 @@
         if unpack_int_fastpath_maybe(fmtiter):
             return
         # slow path
+        if not ALLOW_SLOWPATH:
+            # we enter here only on some tests
+            raise ValueError("fastpath not taken :(")
         intvalue = inttype(0)
         s = fmtiter.read(size)
         idx = 0
diff --git a/rpython/rlib/rstruct/test/test_runpack.py b/rpython/rlib/rstruct/test/test_runpack.py
--- a/rpython/rlib/rstruct/test/test_runpack.py
+++ b/rpython/rlib/rstruct/test/test_runpack.py
@@ -94,7 +94,7 @@
 class TestNoFastPath(TestRStruct):
 
     def setup_method(self, meth):
-        standardfmttable.ALLOW_FASTPATH = False
+        standardfmttable.USE_FASTPATH = False
 
     def teardown_method(self, meth):
-        standardfmttable.ALLOW_FASTPATH = True
+        standardfmttable.USE_FASTPATH = True
diff --git a/rpython/rlib/test/test_buffer.py b/rpython/rlib/test/test_buffer.py
--- a/rpython/rlib/test/test_buffer.py
+++ b/rpython/rlib/test/test_buffer.py
@@ -32,3 +32,16 @@
     a = RPythonAnnotator()
     s = a.build_types(func, [int])
     assert s == SomeInteger(nonneg=True)
+
+
+def test_as_str_and_offset_maybe():
+    buf = StringBuffer('hello world')
+    assert buf.as_str_and_offset_maybe() == ('hello world', 0)
+    #
+    sbuf = SubBuffer(buf, 6, 5)
+    assert sbuf.getslice(0, 5, 1, 5) == 'world'
+    assert sbuf.as_str_and_offset_maybe() == ('hello world', 6)
+    #
+    ssbuf = SubBuffer(sbuf, 3, 2)
+    assert ssbuf.getslice(0, 2, 1, 2) == 'ld'
+    assert ssbuf.as_str_and_offset_maybe() == ('hello world', 9)


More information about the pypy-commit mailing list