[pypy-commit] pypy null_byte_after_str: cffi: tentatively pass the raw address inside RPython strings

arigo pypy.commits at gmail.com
Fri Jul 29 14:10:03 EDT 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: null_byte_after_str
Changeset: r85916:f2f9031b0f4b
Date: 2016-07-29 20:11 +0200
http://bitbucket.org/pypy/pypy/changeset/f2f9031b0f4b/

Log:	cffi: tentatively pass the raw address inside RPython strings

diff --git a/pypy/module/_cffi_backend/ctypefunc.py b/pypy/module/_cffi_backend/ctypefunc.py
--- a/pypy/module/_cffi_backend/ctypefunc.py
+++ b/pypy/module/_cffi_backend/ctypefunc.py
@@ -177,9 +177,12 @@
                 if isinstance(argtype, W_CTypePointer):
                     data = rffi.ptradd(buffer, cif_descr.exchange_args[i])
                     flag = get_mustfree_flag(data)
+                    raw_cdata = rffi.cast(rffi.CCHARPP, data)[0]
                     if flag == 1:
-                        raw_cdata = rffi.cast(rffi.CCHARPP, data)[0]
                         lltype.free(raw_cdata, flavor='raw')
+                    elif flag >= 4:
+                        value = args_w[i].str_w(space)
+                        rffi.free_nonmovingbuffer(value, raw_cdata, chr(flag))
             lltype.free(buffer, flavor='raw')
             keepalive_until_here(args_w)
         return w_res
diff --git a/pypy/module/_cffi_backend/ctypeptr.py b/pypy/module/_cffi_backend/ctypeptr.py
--- a/pypy/module/_cffi_backend/ctypeptr.py
+++ b/pypy/module/_cffi_backend/ctypeptr.py
@@ -14,8 +14,8 @@
 
 
 class W_CTypePtrOrArray(W_CType):
-    _attrs_            = ['ctitem', 'can_cast_anything', 'length']
-    _immutable_fields_ = ['ctitem', 'can_cast_anything', 'length']
+    _attrs_            = ['ctitem', 'can_cast_anything', 'accept_str', 'length']
+    _immutable_fields_ = ['ctitem', 'can_cast_anything', 'accept_str', 'length']
     length = -1
 
     def __init__(self, space, size, extra, extra_position, ctitem,
@@ -28,6 +28,9 @@
         #  - for functions, it is the return type
         self.ctitem = ctitem
         self.can_cast_anything = could_cast_anything and ctitem.cast_anything
+        self.accept_str = (self.can_cast_anything or
+                            (ctitem.is_primitive_integer and
+                             ctitem.size == rffi.sizeof(lltype.Char)))
 
     def is_unichar_ptr_or_array(self):
         return isinstance(self.ctitem, ctypeprim.W_CTypePrimitiveUniChar)
@@ -70,9 +73,7 @@
                 pass
             else:
                 self._convert_array_from_listview(cdata, space.listview(w_ob))
-        elif (self.can_cast_anything or
-              (self.ctitem.is_primitive_integer and
-               self.ctitem.size == rffi.sizeof(lltype.Char))):
+        elif self.accept_str:
             if not space.isinstance_w(w_ob, space.w_str):
                 raise self._convert_error("str or list or tuple", w_ob)
             s = space.str_w(w_ob)
@@ -262,6 +263,15 @@
 
     def _prepare_pointer_call_argument(self, w_init, cdata):
         space = self.space
+        if self.accept_str and space.isinstance_w(w_init, space.w_str):
+            # special case to optimize strings passed to a "char *" argument
+            # WARNING: this relies on the fact that w_init.str_w() returns
+            # always the same object for the same w_init!
+            value = w_init.str_w(space)
+            buf, buf_flag = rffi.get_nonmovingbuffer_final_null(value)
+            rffi.cast(rffi.CCHARPP, cdata)[0] = buf
+            return ord(buf_flag)    # 4, 5 or 6
+        #
         if (space.isinstance_w(w_init, space.w_list) or
             space.isinstance_w(w_init, space.w_tuple)):
             length = space.int_w(space.len(w_init))
diff --git a/pypy/module/_ssl/interp_ssl.py b/pypy/module/_ssl/interp_ssl.py
--- a/pypy/module/_ssl/interp_ssl.py
+++ b/pypy/module/_ssl/interp_ssl.py
@@ -135,7 +135,7 @@
 
     def __init__(self, ctx, protos):
         self.protos = protos
-        self.buf, self.pinned, self.is_raw = rffi.get_nonmovingbuffer(protos)
+        self.buf, self.bufflag = rffi.get_nonmovingbuffer(protos)
         NPN_STORAGE.set(rffi.cast(lltype.Unsigned, self.buf), self)
 
         # set both server and client callbacks, because the context
@@ -147,7 +147,7 @@
 
     def __del__(self):
         rffi.free_nonmovingbuffer(
-            self.protos, self.buf, self.pinned, self.is_raw)
+            self.protos, self.buf, self.bufflag)
 
     @staticmethod
     def advertiseNPN_cb(s, data_ptr, len_ptr, args):
@@ -181,7 +181,7 @@
 
     def __init__(self, ctx, protos):
         self.protos = protos
-        self.buf, self.pinned, self.is_raw = rffi.get_nonmovingbuffer(protos)
+        self.buf, self.bufflag = rffi.get_nonmovingbuffer(protos)
         ALPN_STORAGE.set(rffi.cast(lltype.Unsigned, self.buf), self)
 
         with rffi.scoped_str2charp(protos) as protos_buf:
@@ -193,7 +193,7 @@
 
     def __del__(self):
         rffi.free_nonmovingbuffer(
-            self.protos, self.buf, self.pinned, self.is_raw)
+            self.protos, self.buf, self.bufflag)
 
     @staticmethod
     def selectALPN_cb(s, out_ptr, outlen_ptr, client, client_len, args):
diff --git a/pypy/objspace/std/bytesobject.py b/pypy/objspace/std/bytesobject.py
--- a/pypy/objspace/std/bytesobject.py
+++ b/pypy/objspace/std/bytesobject.py
@@ -451,6 +451,9 @@
         return self._value
 
     def str_w(self, space):
+        # WARNING: _cffi_backend/ctypeptr.py depends on the fact that
+        # w_obj.str_w() called twice on the same object returns the
+        # exact same string object!
         return self._value
 
     def buffer_w(self, space, flags):
diff --git a/rpython/rtyper/lltypesystem/rffi.py b/rpython/rtyper/lltypesystem/rffi.py
--- a/rpython/rtyper/lltypesystem/rffi.py
+++ b/rpython/rtyper/lltypesystem/rffi.py
@@ -816,9 +816,10 @@
         string is already nonmovable or could be pinned.  Must be followed by a
         free_nonmovingbuffer call.
 
-        First bool returned indicates if 'data' was pinned. Second bool returned
-        indicates if we did a raw alloc because pinning failed. Both bools
-        should never be true at the same time.
+        Also returns a char:
+         * \4: no pinning, returned pointer is inside 'data' which is nonmovable
+         * \5: 'data' was pinned, returned pointer is inside
+         * \6: pinning failed, returned pointer is raw malloced
 
         For strings (not unicodes), the len()th character of the resulting
         raw buffer is available, but not initialized.  Use
@@ -829,15 +830,16 @@
         lldata = llstrtype(data)
         count = len(data)
 
-        pinned = False
-        if rgc.can_move(data):
+        if not rgc.can_move(data):
+            flag = '\x04'
+        else:
             if rgc.pin(data):
-                pinned = True
+                flag = '\x05'
             else:
-                count += has_final_null_char(TYPEP)
-                buf = lltype.malloc(TYPEP.TO, count, flavor='raw')
+                buf = lltype.malloc(TYPEP.TO, count + (TYPEP is CCHARP),
+                                    flavor='raw')
                 copy_string_to_raw(lldata, buf, 0, count)
-                return buf, pinned, True
+                return buf, '\x06'
                 # ^^^ raw malloc used to get a nonmovable copy
         #
         # following code is executed if:
@@ -846,7 +848,7 @@
         data_start = cast_ptr_to_adr(lldata) + \
             offsetof(STRTYPE, 'chars') + itemoffsetof(STRTYPE.chars, 0)
 
-        return cast(TYPEP, data_start), pinned, False
+        return cast(TYPEP, data_start), flag
         # ^^^ already nonmovable. Therefore it's not raw allocated nor
         # pinned.
     get_nonmovingbuffer._always_inline_ = 'try' # get rid of the returned tuple
@@ -854,28 +856,28 @@
 
     @jit.dont_look_inside
     def get_nonmovingbuffer_final_null(data):
-        buf, is_pinned, is_raw = get_nonmovingbuffer(data)
+        buf, flag = get_nonmovingbuffer(data)
         buf[len(data)] = lastchar
-        return buf, is_pinned, is_raw
+        return buf, flag
     get_nonmovingbuffer_final_null._always_inline_ = 'try'
     get_nonmovingbuffer_final_null._annenforceargs_ = [strtype]
 
-    # (str, char*, bool, bool) -> None
+    # (str, char*, char) -> None
     # Can't inline this because of the raw address manipulation.
     @jit.dont_look_inside
-    def free_nonmovingbuffer(data, buf, is_pinned, is_raw):
+    def free_nonmovingbuffer(data, buf, flag):
         """
-        Keep 'data' alive and unpin it if it was pinned ('is_pinned' is true).
-        Otherwise free the non-moving copy ('is_raw' is true).
+        Keep 'data' alive and unpin it if it was pinned (flag==\5).
+        Otherwise free the non-moving copy (flag==\6).
         """
-        if is_pinned:
+        if flag == '\x05':
             rgc.unpin(data)
-        if is_raw:
+        if flag == '\x06':
             lltype.free(buf, flavor='raw')
-        # if is_pinned and is_raw are false: data was already nonmovable,
+        # if flag == '\x04': data was already nonmovable,
         # we have nothing to clean up
         keepalive_until_here(data)
-    free_nonmovingbuffer._annenforceargs_ = [strtype, None, bool, bool]
+    free_nonmovingbuffer._annenforceargs_ = [strtype, None, None]
 
     # int -> (char*, str, int)
     # Can't inline this because of the raw address manipulation.
@@ -1209,10 +1211,10 @@
     def __init__(self, data):
         self.data = data
     def __enter__(self):
-        self.buf, self.pinned, self.is_raw = get_nonmovingbuffer(self.data)
+        self.buf, self.flag = get_nonmovingbuffer(self.data)
         return self.buf
     def __exit__(self, *args):
-        free_nonmovingbuffer(self.data, self.buf, self.pinned, self.is_raw)
+        free_nonmovingbuffer(self.data, self.buf, self.flag)
     __init__._always_inline_ = 'try'
     __enter__._always_inline_ = 'try'
     __exit__._always_inline_ = 'try'
@@ -1221,11 +1223,10 @@
     def __init__(self, data):
         self.data = data
     def __enter__(self):
-        self.buf, self.pinned, self.is_raw = (
-            get_nonmovingbuffer_final_null(self.data))
+        self.buf, self.flag = get_nonmovingbuffer_final_null(self.data)
         return self.buf
     def __exit__(self, *args):
-        free_nonmovingbuffer(self.data, self.buf, self.pinned, self.is_raw)
+        free_nonmovingbuffer(self.data, self.buf, self.flag)
     __init__._always_inline_ = 'try'
     __enter__._always_inline_ = 'try'
     __exit__._always_inline_ = 'try'
@@ -1234,10 +1235,10 @@
     def __init__(self, data):
         self.data = data
     def __enter__(self):
-        self.buf, self.pinned, self.is_raw = get_nonmoving_unicodebuffer(self.data)
+        self.buf, self.flag = get_nonmoving_unicodebuffer(self.data)
         return self.buf
     def __exit__(self, *args):
-        free_nonmoving_unicodebuffer(self.data, self.buf, self.pinned, self.is_raw)
+        free_nonmoving_unicodebuffer(self.data, self.buf, self.flag)
     __init__._always_inline_ = 'try'
     __enter__._always_inline_ = 'try'
     __exit__._always_inline_ = 'try'
diff --git a/rpython/rtyper/lltypesystem/test/test_rffi.py b/rpython/rtyper/lltypesystem/test/test_rffi.py
--- a/rpython/rtyper/lltypesystem/test/test_rffi.py
+++ b/rpython/rtyper/lltypesystem/test/test_rffi.py
@@ -516,7 +516,7 @@
     def test_nonmovingbuffer(self):
         d = 'some cool data that should not move'
         def f():
-            buf, is_pinned, is_raw = get_nonmovingbuffer(d)
+            buf, flag = get_nonmovingbuffer(d)
             try:
                 counter = 0
                 for i in range(len(d)):
@@ -524,7 +524,7 @@
                         counter += 1
                 return counter
             finally:
-                free_nonmovingbuffer(d, buf, is_pinned, is_raw)
+                free_nonmovingbuffer(d, buf, flag)
         assert f() == len(d)
         fn = self.compile(f, [], gcpolicy='ref')
         assert fn() == len(d)
@@ -534,13 +534,13 @@
         def f():
             counter = 0
             for n in range(32):
-                buf, is_pinned, is_raw = get_nonmovingbuffer(d)
+                buf, flag = get_nonmovingbuffer(d)
                 try:
                     for i in range(len(d)):
                         if buf[i] == d[i]:
                             counter += 1
                 finally:
-                    free_nonmovingbuffer(d, buf, is_pinned, is_raw)
+                    free_nonmovingbuffer(d, buf, flag)
             return counter
         fn = self.compile(f, [], gcpolicy='semispace')
         # The semispace gc uses raw_malloc for its internal data structs
@@ -555,13 +555,13 @@
         def f():
             counter = 0
             for n in range(32):
-                buf, is_pinned, is_raw = get_nonmovingbuffer(d)
+                buf, flag = get_nonmovingbuffer(d)
                 try:
                     for i in range(len(d)):
                         if buf[i] == d[i]:
                             counter += 1
                 finally:
-                    free_nonmovingbuffer(d, buf, is_pinned, is_raw)
+                    free_nonmovingbuffer(d, buf, flag)
             return counter
         fn = self.compile(f, [], gcpolicy='incminimark')
         # The incminimark gc uses raw_malloc for its internal data structs


More information about the pypy-commit mailing list