Author: Carl Friedrich Bolz <cfb...@gmx.de> Branch: Changeset: r47509:cba07b6bbd87 Date: 2011-05-24 12:00 +0200 http://bitbucket.org/pypy/pypy/changeset/cba07b6bbd87/
Log: merge diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py --- a/pypy/config/pypyoption.py +++ b/pypy/config/pypyoption.py @@ -242,6 +242,10 @@ "(the empty string and potentially single-char strings)", default=False), + BoolOption("withsmalltuple", + "use small tuples", + default=False), + BoolOption("withrope", "use ropes as the string implementation", default=False, requires=[("objspace.std.withstrslice", False), diff --git a/pypy/doc/config/objspace.std.withsmalltuple.txt b/pypy/doc/config/objspace.std.withsmalltuple.txt new file mode 100644 --- /dev/null +++ b/pypy/doc/config/objspace.std.withsmalltuple.txt @@ -0,0 +1,1 @@ +Use small tuple objects for sizes from 1 to 3 diff --git a/pypy/module/cpyext/methodobject.py b/pypy/module/cpyext/methodobject.py --- a/pypy/module/cpyext/methodobject.py +++ b/pypy/module/cpyext/methodobject.py @@ -73,29 +73,29 @@ rffi.charp2str(self.ml.c_ml_name) + "() takes no keyword arguments")) func = rffi.cast(PyCFunction, self.ml.c_ml_meth) + length = space.int_w(space.len(w_args)) if flags & METH_KEYWORDS: func = rffi.cast(PyCFunctionKwArgs, self.ml.c_ml_meth) return generic_cpy_call(space, func, w_self, w_args, w_kw) elif flags & METH_NOARGS: - if len(w_args.wrappeditems) == 0: + if length == 0: return generic_cpy_call(space, func, w_self, None) raise OperationError(space.w_TypeError, space.wrap( rffi.charp2str(self.ml.c_ml_name) + "() takes no arguments")) elif flags & METH_O: - assert isinstance(w_args, W_TupleObject) - if len(w_args.wrappeditems) != 1: + if length != 1: raise OperationError(space.w_TypeError, space.wrap("%s() takes exactly one argument (%d given)" % ( rffi.charp2str(self.ml.c_ml_name), - len(w_args.wrappeditems)))) - w_arg = w_args.wrappeditems[0] + length))) + w_arg = space.getitem(w_args, space.wrap(0)) return generic_cpy_call(space, func, w_self, w_arg) elif flags & METH_VARARGS: return generic_cpy_call(space, func, w_self, w_args) else: # METH_OLDARGS, the really old style - size = len(w_args.wrappeditems) + size = length if size == 1: - w_arg = w_args.wrappeditems[0] + w_arg = space.getitem(w_args, space.wrap(0)) elif size == 0: w_arg = None else: diff --git a/pypy/module/cpyext/test/test_tupleobject.py b/pypy/module/cpyext/test/test_tupleobject.py --- a/pypy/module/cpyext/test/test_tupleobject.py +++ b/pypy/module/cpyext/test/test_tupleobject.py @@ -3,8 +3,10 @@ from pypy.module.cpyext.pyobject import PyObject, PyObjectP, make_ref, from_ref from pypy.module.cpyext.test.test_api import BaseApiTest from pypy.rpython.lltypesystem import rffi, lltype +from pypy.conftest import gettestobjspace class TestTupleObject(BaseApiTest): + def test_tupleobject(self, space, api): assert not api.PyTuple_Check(space.w_None) assert api.PyTuple_SetItem(space.w_None, 0, space.w_None) == -1 @@ -20,11 +22,23 @@ ar[0] = rffi.cast(PyObject, make_ref(space, py_tuple)) api._PyTuple_Resize(ar, 2) py_tuple = from_ref(space, ar[0]) - assert len(py_tuple.wrappeditems) == 2 + assert space.int_w(space.len(py_tuple)) == 2 api._PyTuple_Resize(ar, 10) py_tuple = from_ref(space, ar[0]) - assert len(py_tuple.wrappeditems) == 10 + assert space.int_w(space.len(py_tuple)) == 10 api.Py_DecRef(ar[0]) lltype.free(ar, flavor='raw') + + def test_setitem(self, space, api): + atuple = space.newtuple([space.wrap(0), space.wrap("hello")]) + assert api.PyTuple_Size(atuple) == 2 + assert space.eq_w(space.getitem(atuple, space.wrap(0)), space.wrap(0)) + assert space.eq_w(space.getitem(atuple, space.wrap(1)), space.wrap("hello")) + w_obj = space.wrap(1) + api.Py_IncRef(w_obj) + api.PyTuple_SetItem(atuple, 1, w_obj) + assert api.PyTuple_Size(atuple) == 2 + assert space.eq_w(space.getitem(atuple, space.wrap(0)), space.wrap(0)) + assert space.eq_w(space.getitem(atuple, space.wrap(1)), space.wrap(1)) diff --git a/pypy/module/cpyext/tupleobject.py b/pypy/module/cpyext/tupleobject.py --- a/pypy/module/cpyext/tupleobject.py +++ b/pypy/module/cpyext/tupleobject.py @@ -6,7 +6,7 @@ borrow_from, make_ref, from_ref) from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall from pypy.objspace.std.tupleobject import W_TupleObject - +from pypy.objspace.std.smalltupleobject import W_SmallTupleObject PyTuple_Check, PyTuple_CheckExact = build_type_checkers("Tuple") @@ -19,25 +19,30 @@ if not PyTuple_Check(space, w_t): # XXX this should also steal a reference, test it!!! PyErr_BadInternalCall(space) - assert isinstance(w_t, W_TupleObject) - w_t.wrappeditems[pos] = w_obj + _setitem_tuple(w_t, pos, w_obj) Py_DecRef(space, w_obj) # SetItem steals a reference! return 0 +def _setitem_tuple(w_t, pos, w_obj): + if isinstance(w_t, W_TupleObject): + w_t.wrappeditems[pos] = w_obj + elif isinstance(w_t, W_SmallTupleObject): + w_t.setitem(pos, w_obj) + else: + assert False + @cpython_api([PyObject, Py_ssize_t], PyObject) def PyTuple_GetItem(space, w_t, pos): if not PyTuple_Check(space, w_t): PyErr_BadInternalCall(space) - assert isinstance(w_t, W_TupleObject) - w_obj = w_t.wrappeditems[pos] + w_obj = space.getitem(w_t, space.wrap(pos)) return borrow_from(w_t, w_obj) @cpython_api([PyObject], Py_ssize_t, error=CANNOT_FAIL) def PyTuple_GET_SIZE(space, w_t): """Return the size of the tuple p, which must be non-NULL and point to a tuple; no error checking is performed. """ - assert isinstance(w_t, W_TupleObject) - return len(w_t.wrappeditems) + return space.int_w(space.len(w_t)) @cpython_api([PyObject], Py_ssize_t, error=-1) def PyTuple_Size(space, ref): @@ -63,15 +68,14 @@ py_tuple = from_ref(space, ref[0]) if not PyTuple_Check(space, py_tuple): PyErr_BadInternalCall(space) - assert isinstance(py_tuple, W_TupleObject) py_newtuple = PyTuple_New(space, newsize) to_cp = newsize - oldsize = len(py_tuple.wrappeditems) + oldsize = space.int_w(space.len(py_tuple)) if oldsize < newsize: to_cp = oldsize for i in range(to_cp): - py_newtuple.wrappeditems[i] = py_tuple.wrappeditems[i] + _setitem_tuple(py_newtuple, i, space.getitem(py_tuple, space.wrap(i))) Py_DecRef(space, ref[0]) ref[0] = make_ref(space, py_newtuple) return 0 diff --git a/pypy/objspace/std/model.py b/pypy/objspace/std/model.py --- a/pypy/objspace/std/model.py +++ b/pypy/objspace/std/model.py @@ -15,6 +15,7 @@ _registered_implementations.add(implcls) option_to_typename = { + "withsmalltuple" : ["smalltupleobject.W_SmallTupleObject"], "withsmallint" : ["smallintobject.W_SmallIntObject"], "withsmalllong" : ["smalllongobject.W_SmallLongObject"], "withstrslice" : ["strsliceobject.W_StringSliceObject"], @@ -71,6 +72,7 @@ from pypy.objspace.std import smallintobject from pypy.objspace.std import smalllongobject from pypy.objspace.std import tupleobject + from pypy.objspace.std import smalltupleobject from pypy.objspace.std import listobject from pypy.objspace.std import dictmultiobject from pypy.objspace.std import stringobject @@ -253,6 +255,9 @@ (listobject.W_ListObject, rangeobject.delegate_range2list), ] + if config.objspace.std.withsmalltuple: + self.typeorder[smalltupleobject.W_SmallTupleObject] += [ + (tupleobject.W_TupleObject, smalltupleobject.delegate_SmallTuple2Tuple)] # put W_Root everywhere self.typeorder[W_Root] = [] diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py --- a/pypy/objspace/std/objspace.py +++ b/pypy/objspace/std/objspace.py @@ -295,9 +295,10 @@ return newlong(self, val) def newtuple(self, list_w): + from pypy.objspace.std.tupletype import wraptuple assert isinstance(list_w, list) make_sure_not_resized(list_w) - return W_TupleObject(list_w) + return wraptuple(self, list_w) def newlist(self, list_w): return W_ListObject(list_w) diff --git a/pypy/objspace/std/smalltupleobject.py b/pypy/objspace/std/smalltupleobject.py new file mode 100644 --- /dev/null +++ b/pypy/objspace/std/smalltupleobject.py @@ -0,0 +1,157 @@ +from pypy.interpreter.error import OperationError +from pypy.objspace.std.model import registerimplementation, W_Object +from pypy.objspace.std.register_all import register_all +from pypy.objspace.std.inttype import wrapint +from pypy.objspace.std.multimethod import FailedToImplement +from pypy.rlib.rarithmetic import intmask +from pypy.objspace.std.sliceobject import W_SliceObject, normalize_simple_slice +from pypy.objspace.std import slicetype +from pypy.interpreter import gateway +from pypy.rlib.debug import make_sure_not_resized +from pypy.rlib.unroll import unrolling_iterable +from pypy.objspace.std.tupleobject import W_TupleObject + +class W_SmallTupleObject(W_Object): + from pypy.objspace.std.tupletype import tuple_typedef as typedef + + def tolist(self): + raise NotImplementedError + + def length(self): + raise NotImplementedError + + def getitem(self, index): + raise NotImplementedError + + def hash(self, space): + raise NotImplementedError + + def eq(self, space, w_other): + raise NotImplementedError + + def setitem(self, index, w_item): + raise NotImplementedError + + def unwrap(w_tuple, space): + items = [space.unwrap(w_item) for w_item in w_tuple.tolist()] # XXX generic mixed types unwrap + return tuple(items) + +def make_specialized_class(n): + iter_n = unrolling_iterable(range(n)) + class cls(W_SmallTupleObject): + + def __init__(self, values): + assert len(values) == n + for i in iter_n: + setattr(self, 'w_value%s' % i, values[i]) + + def tolist(self): + l = [None] * n + for i in iter_n: + l[i] = getattr(self, 'w_value%s' % i) + return l + + def length(self): + return n + + def getitem(self, index): + for i in iter_n: + if index == i: + return getattr(self,'w_value%s' % i) + raise IndexError + + def setitem(self, index, w_item): + for i in iter_n: + if index == i: + setattr(self, 'w_value%s' % i, w_item) + return + raise IndexError + + def eq(self, space, w_other): + if self.length() != w_other.length(): + return space.w_False + for i in iter_n: + item1 = self.getitem(i) + item2 = w_other.getitem(i) + if not space.eq_w(item1, item2): + return space.w_False + return space.w_True + + def hash(self, space): + mult = 1000003 + x = 0x345678 + z = self.length() + for i in iter_n: + w_item = self.getitem(i) + y = space.int_w(space.hash(w_item)) + x = (x ^ y) * mult + z -= 1 + mult += 82520 + z + z + x += 97531 + return space.wrap(intmask(x)) + + cls.__name__ = "W_SmallTupleObject%s" % n + return cls + +W_SmallTupleObject2 = make_specialized_class(2) +W_SmallTupleObject3 = make_specialized_class(3) +W_SmallTupleObject4 = make_specialized_class(4) +W_SmallTupleObject5 = make_specialized_class(5) +W_SmallTupleObject6 = make_specialized_class(6) +W_SmallTupleObject7 = make_specialized_class(7) +W_SmallTupleObject8 = make_specialized_class(8) + +registerimplementation(W_SmallTupleObject) + +def delegate_SmallTuple2Tuple(space, w_small): + return W_TupleObject(w_small.tolist()) + +def len__SmallTuple(space, w_tuple): + return space.wrap(w_tuple.length()) + +def getitem__SmallTuple_ANY(space, w_tuple, w_index): + index = space.getindex_w(w_index, space.w_IndexError, "tuple index") + if index < 0: + index += w_tuple.length() + try: + return w_tuple.getitem(index) + except IndexError: + raise OperationError(space.w_IndexError, + space.wrap("tuple index out of range")) + +def getitem__SmallTuple_Slice(space, w_tuple, w_slice): + length = w_tuple.length() + start, stop, step, slicelength = w_slice.indices4(space, length) + assert slicelength >= 0 + subitems = [None] * slicelength + for i in range(slicelength): + subitems[i] = w_tuple.getitem(start) + start += step + return space.newtuple(subitems) + +def mul_smalltuple_times(space, w_tuple, w_times): + try: + times = space.getindex_w(w_times, space.w_OverflowError) + except OperationError, e: + if e.match(space, space.w_TypeError): + raise FailedToImplement + raise + if times == 1 and space.type(w_tuple) == space.w_tuple: + return w_tuple + items = w_tuple.tolist() + return space.newtuple(items * times) + +def mul__SmallTuple_ANY(space, w_tuple, w_times): + return mul_smalltuple_times(space, w_tuple, w_times) + +def mul__ANY_SmallTuple(space, w_times, w_tuple): + return mul_smalltuple_times(space, w_tuple, w_times) + +def eq__SmallTuple_SmallTuple(space, w_tuple1, w_tuple2): + return w_tuple1.eq(space, w_tuple2) + +def hash__SmallTuple(space, w_tuple): + return w_tuple.hash(space) + +from pypy.objspace.std import tupletype +register_all(vars(), tupletype) diff --git a/pypy/objspace/std/test/test_smalltupleobject.py b/pypy/objspace/std/test/test_smalltupleobject.py new file mode 100644 --- /dev/null +++ b/pypy/objspace/std/test/test_smalltupleobject.py @@ -0,0 +1,86 @@ +from pypy.objspace.std.tupleobject import W_TupleObject +from pypy.objspace.std.smalltupleobject import W_SmallTupleObject +from pypy.interpreter.error import OperationError +from pypy.objspace.std.test.test_tupleobject import AppTestW_TupleObject +from pypy.conftest import gettestobjspace + +class AppTestW_SmallTupleObject(AppTestW_TupleObject): + + def setup_class(cls): + cls.space = gettestobjspace(**{"objspace.std.withsmalltuple": True}) + cls.w_issmall = cls.space.appexec([], """(): + import __pypy__ + def issmall(obj): + assert "SmallTuple" in __pypy__.internal_repr(obj) + return issmall + """) + + def test_smalltuple(self): + self.issmall((1,2)) + self.issmall((1,2,3)) + + def test_slicing_to_small(self): + self.issmall((1, 2, 3)[0:2]) # SmallTuple2 + self.issmall((1, 2, 3)[0:2:1]) + + self.issmall((1, 2, 3, 4)[0:3]) # SmallTuple3 + self.issmall((1, 2, 3, 4)[0:3:1]) + + def test_adding_to_small(self): + self.issmall((1,)+(2,)) # SmallTuple2 + self.issmall((1,1)+(2,)) # SmallTuple3 + self.issmall((1,)+(2,3)) + + def test_multiply_to_small(self): + self.issmall((1,)*2) + self.issmall((1,)*3) + + def test_slicing_from_small(self): + assert (1,2)[0:1:1] == (1,) + assert (1,2,3)[0:2:1] == (1,2) + + def test_eq(self): + a = (1,2,3) + b = (1,2,3) + assert a == b + + c = (1,3,2) + assert a != c + + def test_hash(self): + a = (1,2,3) + b = (1,2,3) + assert hash(a) == hash(b) + + c = (1,3,2) + assert hash(a) != hash(c) + +class TestW_SmallTupleObject(): + + def setup_class(cls): + cls.space = gettestobjspace(**{"objspace.std.withsmalltuple": True}) + + def test_issmalltupleobject(self): + w_tuple = self.space.newtuple([self.space.wrap(1), self.space.wrap(2)]) + assert isinstance(w_tuple, W_SmallTupleObject) + + def test_hash_agains_normal_tuple(self): + normalspace = gettestobjspace(**{"objspace.std.withsmalltuple": False}) + w_tuple = normalspace.newtuple([self.space.wrap(1), self.space.wrap(2)]) + + smallspace = gettestobjspace(**{"objspace.std.withsmalltuple": True}) + w_smalltuple = smallspace.newtuple([self.space.wrap(1), self.space.wrap(2)]) + + assert isinstance(w_smalltuple, W_SmallTupleObject) + assert isinstance(w_tuple, W_TupleObject) + assert not normalspace.is_true(normalspace.eq(w_tuple, w_smalltuple)) + assert smallspace.is_true(smallspace.eq(w_tuple, w_smalltuple)) + assert smallspace.is_true(smallspace.eq(normalspace.hash(w_tuple), smallspace.hash(w_smalltuple))) + + def test_setitem(self): + w_smalltuple = self.space.newtuple([self.space.wrap(1), self.space.wrap(2)]) + w_smalltuple.setitem(0, self.space.wrap(5)) + list_w = w_smalltuple.tolist() + assert len(list_w) == 2 + assert self.space.eq_w(list_w[0], self.space.wrap(5)) + assert self.space.eq_w(list_w[1], self.space.wrap(2)) diff --git a/pypy/objspace/std/tupleobject.py b/pypy/objspace/std/tupleobject.py --- a/pypy/objspace/std/tupleobject.py +++ b/pypy/objspace/std/tupleobject.py @@ -56,12 +56,12 @@ for i in range(slicelength): subitems[i] = items[start] start += step - return W_TupleObject(subitems) + return space.newtuple(subitems) def getslice__Tuple_ANY_ANY(space, w_tuple, w_start, w_stop): length = len(w_tuple.wrappeditems) start, stop = normalize_simple_slice(space, length, w_start, w_stop) - return W_TupleObject(w_tuple.wrappeditems[start:stop]) + return space.newtuple(w_tuple.wrappeditems[start:stop]) def contains__Tuple_ANY(space, w_tuple, w_obj): for w_item in w_tuple.wrappeditems: @@ -76,7 +76,7 @@ def add__Tuple_Tuple(space, w_tuple1, w_tuple2): items1 = w_tuple1.wrappeditems items2 = w_tuple2.wrappeditems - return W_TupleObject(items1 + items2) + return space.newtuple(items1 + items2) def mul_tuple_times(space, w_tuple, w_times): try: @@ -88,7 +88,7 @@ if times == 1 and space.type(w_tuple) == space.w_tuple: return w_tuple items = w_tuple.wrappeditems - return W_TupleObject(items * times) + return space.newtuple(items * times) def mul__Tuple_ANY(space, w_tuple, w_times): return mul_tuple_times(space, w_tuple, w_times) @@ -162,7 +162,7 @@ return intmask(x) def getnewargs__Tuple(space, w_tuple): - return space.newtuple([W_TupleObject(w_tuple.wrappeditems)]) + return space.newtuple([space.newtuple(w_tuple.wrappeditems)]) def tuple_count__Tuple_ANY(space, w_tuple, w_obj): count = 0 diff --git a/pypy/objspace/std/tupletype.py b/pypy/objspace/std/tupletype.py --- a/pypy/objspace/std/tupletype.py +++ b/pypy/objspace/std/tupletype.py @@ -3,6 +3,31 @@ from pypy.objspace.std.register_all import register_all from pypy.objspace.std.stdtypedef import StdTypeDef, SMM +def wraptuple(space, list_w): + from pypy.objspace.std.tupleobject import W_TupleObject + from pypy.objspace.std.smalltupleobject import W_SmallTupleObject2 + from pypy.objspace.std.smalltupleobject import W_SmallTupleObject3 + from pypy.objspace.std.smalltupleobject import W_SmallTupleObject4 + from pypy.objspace.std.smalltupleobject import W_SmallTupleObject5 + from pypy.objspace.std.smalltupleobject import W_SmallTupleObject6 + from pypy.objspace.std.smalltupleobject import W_SmallTupleObject7 + from pypy.objspace.std.smalltupleobject import W_SmallTupleObject8 + if space.config.objspace.std.withsmalltuple: + if len(list_w) == 2: + return W_SmallTupleObject2(list_w) + if len(list_w) == 3: + return W_SmallTupleObject3(list_w) + if len(list_w) == 4: + return W_SmallTupleObject4(list_w) + if len(list_w) == 5: + return W_SmallTupleObject5(list_w) + if len(list_w) == 6: + return W_SmallTupleObject6(list_w) + if len(list_w) == 7: + return W_SmallTupleObject7(list_w) + if len(list_w) == 8: + return W_SmallTupleObject8(list_w) + return W_TupleObject(list_w) tuple_count = SMM("count", 2, doc="count(obj) -> number of times obj appears in the tuple") _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit