[pypy-commit] pypy nonmovable-list: The hardest bit is to give a consistent behavior before translation...

arigo pypy.commits at gmail.com
Thu Jun 2 09:05:37 EDT 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: nonmovable-list
Changeset: r84880:3013b870792c
Date: 2016-06-02 15:05 +0200
http://bitbucket.org/pypy/pypy/changeset/3013b870792c/

Log:	The hardest bit is to give a consistent behavior before
	translation...

diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py
--- a/rpython/rlib/rgc.py
+++ b/rpython/rlib/rgc.py
@@ -1001,3 +1001,194 @@
     def specialize_call(self, hop):
         hop.exception_cannot_occur()
         return hop.genop('gc_gettypeid', hop.args_v, resulttype=lltype.Signed)
+
+# ____________________________________________________________
+
+
+class _rawptr_missing_item(object):
+    pass
+_rawptr_missing_item = _rawptr_missing_item()
+
+
+class ListSupportingRawPtr(list):
+    """Calling this class is a no-op after translation.  Before
+    translation, it returns a new instance of ListSupportingRawPtr,
+    on which rgc.nonmoving_raw_ptr_for_resizable_list() might be
+    used if needed.  For now, only supports lists of chars.
+    """
+    __slots__ = ('_raw_items',)   # either None or a rffi.CArray(Char)
+
+    def __init__(self, lst):
+        self._raw_items = None
+        self.__from_list(lst)
+
+    def __resize(self):
+        """Called before an operation changes the size of the list"""
+        if self._raw_items is not None:
+            list.__init__(self, self.__as_list())
+            self._raw_items = None
+
+    def __from_list(self, lst):
+        """Initialize the list from a copy of the list 'lst'."""
+        assert isinstance(lst, list)
+        for x in lst:
+            assert isinstance(x, str) and len(x) == 1
+        if self is lst:
+            return
+        if len(self) != len(lst):
+            self.__resize()
+        if self._raw_items is None:
+            list.__init__(self, lst)
+        else:
+            assert len(self) == self._raw_items._obj.getlength() == len(lst)
+            for i in range(len(self)):
+                self._raw_items[i] = lst[i]
+
+    def __as_list(self):
+        """Return a list (the same or a different one) which contains the
+        items in the regular way."""
+        if self._raw_items is None:
+            return self
+        length = self._raw_items._obj.getlength()
+        assert length == len(self)
+        return [self._raw_items[i] for i in range(length)]
+
+    def __getitem__(self, index):
+        if self._raw_items is None:
+            return list.__getitem__(self, index)
+        if index < 0:
+            index += len(self)
+        if not (0 <= index < len(self)):
+            raise IndexError
+        return self._raw_items[index]
+
+    def __setitem__(self, index, new):
+        if self._raw_items is None:
+            return list.__setitem__(self, index, new)
+        if index < 0:
+            index += len(self)
+        if not (0 <= index < len(self)):
+            raise IndexError
+        self._raw_items[index] = new
+
+    def __delitem__(self, index):
+        self.__resize()
+        list.__delitem__(self, index)
+
+    def __getslice__(self, i, j):
+        return list.__getslice__(self.__as_list(), i, j)
+
+    def __setslice__(self, i, j, new):
+        lst = self.__as_list()
+        list.__setslice__(lst, i, j, new)
+        self.__from_list(lst)
+
+    def __delslice__(self, i, j):
+        lst = self.__as_list()
+        list.__delslice__(lst, i, j)
+        self.__from_list(lst)
+
+    def __iter__(self):
+        try:
+            i = 0
+            while True:
+                yield self[i]
+                i += 1
+        except IndexError:
+            pass
+
+    def __reversed__(self):
+        i = len(self)
+        while i > 0:
+            i -= 1
+            yield self[i]
+
+    def __contains__(self, item):
+        return list.__contains__(self.__as_list(), item)
+
+    def __add__(self, other):
+        return list.__add__(self.__as_list(), other)
+
+    def __radd__(self, other):
+        other = list(other)
+        return list.__add__(other, self)
+
+    def __iadd__(self, other):
+        self.__resize()
+        return list.__iadd__(self, other)
+
+    def __eq__(self, other):
+        return list.__eq__(self.__as_list(), other)
+    def __ne__(self, other):
+        return list.__ne__(self.__as_list(), other)
+    def __ge__(self, other):
+        return list.__ge__(self.__as_list(), other)
+    def __gt__(self, other):
+        return list.__gt__(self.__as_list(), other)
+    def __le__(self, other):
+        return list.__le__(self.__as_list(), other)
+    def __lt__(self, other):
+        return list.__lt__(self.__as_list(), other)
+
+    def __mul__(self, other):
+        return list.__mul__(self.__as_list(), other)
+
+    def __rmul__(self, other):
+        return list.__mul__(self.__as_list(), other)
+
+    def __imul__(self, other):
+        self.__resize()
+        return list.__imul__(self, other)
+
+    def __repr__(self):
+        return 'ListSupportingRawPtr(%s)' % (list.__repr__(self.__as_list()),)
+
+    def append(self, object):
+        self.__resize()
+        return list.append(self, object)
+
+    def count(self, value):
+        return list.count(self.__as_list(), value)
+
+    def extend(self, iterable):
+        self.__resize()
+        return list.extend(self, iterable)
+
+    def index(self, value, *start_stop):
+        return list.index(self.__as_list(), value, *start_stop)
+
+    def insert(self, index, object):
+        self.__resize()
+        return list.insert(self, index, object)
+
+    def pop(self, *opt_index):
+        self.__resize()
+        return list.pop(self, *opt_index)
+
+    def remove(self, value):
+        self.__resize()
+        return list.remove(self, value)
+
+    def reverse(self):
+        lst = self.__as_list()
+        list.reverse(lst)
+        self.__from_list(lst)
+
+    def sort(self, *args, **kwds):
+        lst = self.__as_list()
+        list.sort(lst, *args, **kwds)
+        self.__from_list(lst)
+
+    def _nonmoving_raw_ptr_for_resizable_list(self):
+        if self._raw_items is None:
+            existing_items = list(self)
+            from rpython.rtyper.lltypesystem import lltype, rffi
+            self._raw_items = lltype.malloc(rffi.CArray(lltype.Char), len(self),
+                                           flavor='raw', immortal=True)
+            self.__from_list(existing_items)
+            assert self._raw_items is not None
+        return self._raw_items
+
+def nonmoving_raw_ptr_for_resizable_list(lst):
+    assert isinstance(lst, ListSupportingRawPtr)
+    return lst._nonmoving_raw_ptr_for_resizable_list()
diff --git a/rpython/rlib/test/test_rgc.py b/rpython/rlib/test/test_rgc.py
--- a/rpython/rlib/test/test_rgc.py
+++ b/rpython/rlib/test/test_rgc.py
@@ -1,6 +1,6 @@
 from rpython.rtyper.test.test_llinterp import gengraph, interpret
 from rpython.rtyper.error import TyperError
-from rpython.rtyper.lltypesystem import lltype, llmemory
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
 from rpython.rlib import rgc # Force registration of gc.collect
 import gc
 import py, sys
@@ -254,6 +254,36 @@
 
     assert typer.custom_trace_funcs == [(TP, trace_func)]
 
+def test_nonmoving_raw_ptr_for_resizable_list():
+    def f(n):
+        lst = ['a', 'b', 'c']
+        lst = rgc.ListSupportingRawPtr(lst)
+        lst.append(chr(n))
+        assert lst[3] == chr(n)
+        assert lst[-1] == chr(n)
+        #
+        ptr = rgc.nonmoving_raw_ptr_for_resizable_list(lst)
+        assert lst[:] == ['a', 'b', 'c', chr(n)]
+        assert lltype.typeOf(ptr) == rffi.CArrayPtr(lltype.Char)
+        assert [ptr[i] for i in range(4)] == ['a', 'b', 'c', chr(n)]
+        #
+        lst[-3] = 'X'
+        assert ptr[1] == 'X'
+        ptr[2] = 'Y'
+        assert lst[-2] == 'Y'
+        #
+        addr = rffi.cast(lltype.Signed, ptr)
+        ptr = rffi.cast(rffi.CArrayPtr(lltype.Char), addr)
+        lst[-4] = 'g'
+        assert ptr[0] == 'g'
+        ptr[3] = 'H'
+        assert lst[-1] == 'H'
+        return lst
+    #
+    # direct untranslated run
+    lst = f(35)
+    assert isinstance(lst, rgc.ListSupportingRawPtr)
+
 
 # ____________________________________________________________
 
@@ -368,7 +398,6 @@
         assert fq._triggered == 1
 
     def test_finalizer_trigger_calls_too_much(self):
-        from rpython.rtyper.lltypesystem import lltype, rffi
         external_func = rffi.llexternal("foo", [], lltype.Void)
         # ^^^ with release_gil=True
         class X(object):


More information about the pypy-commit mailing list