Author: Ronan Lamy <ronan.l...@gmail.com> Branch: py3.5 Changeset: r91408:66108bb2353d Date: 2017-05-24 17:35 +0100 http://bitbucket.org/pypy/pypy/changeset/66108bb2353d/
Log: hg merge default diff too long, truncating to 2000 out of 4286 lines diff --git a/pypy/doc/test/test_whatsnew.py b/pypy/doc/test/test_whatsnew.py --- a/pypy/doc/test/test_whatsnew.py +++ b/pypy/doc/test/test_whatsnew.py @@ -102,6 +102,8 @@ assert not not_documented if branch == 'py3k': assert not not_merged + else: + assert branch in documented, 'Please document this branch before merging: %s' % branch def test_startrev_on_default(): doc = ROOT.join('pypy', 'doc') diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -56,3 +56,15 @@ Remove faulty fastpath from ctypes +.. branch: sockopt_zero + +Passing a buffersize of 0 to socket.getsockopt + +.. branch: better-test-whatsnew + +.. branch: faster-rstruct-2 + +Improve the performance of struct.pack and struct.pack_into by using raw_store +or gc_store_indexed whenever possible. Moreover, enable the existing +struct.unpack fast path to all the existing buffer types, whereas previously +it was enabled only for strings diff --git a/pypy/doc/windows.rst b/pypy/doc/windows.rst --- a/pypy/doc/windows.rst +++ b/pypy/doc/windows.rst @@ -23,7 +23,7 @@ Installing Visual Compiler v9 (for Python 2.7) ---------------------------------------------- -This compiler, while the standard one for Python 2.7, is depricated. Microsoft has +This compiler, while the standard one for Python 2.7, is deprecated. Microsoft has made it available as the `Microsoft Visual C++ Compiler for Python 2.7`_ (the link was checked in Nov 2016). Note that the compiler suite will be installed in ``C:\Users\<user name>\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python``. diff --git a/pypy/interpreter/buffer.py b/pypy/interpreter/buffer.py --- a/pypy/interpreter/buffer.py +++ b/pypy/interpreter/buffer.py @@ -1,5 +1,6 @@ from rpython.rlib.rstruct.error import StructError from rpython.rlib.buffer import StringBuffer, SubBuffer +from rpython.rlib.mutbuffer import MutableStringBuffer from pypy.interpreter.error import oefmt @@ -72,14 +73,15 @@ def bytes_from_value(self, space, w_val): from pypy.module.struct.formatiterator import PackFormatIterator itemsize = self.getitemsize() - fmtiter = PackFormatIterator(space, [w_val], itemsize) + buf = MutableStringBuffer(itemsize) + fmtiter = PackFormatIterator(space, buf, [w_val]) try: fmtiter.interpret(self.getformat()) except StructError as e: raise oefmt(space.w_TypeError, "memoryview: invalid type for format '%s'", self.getformat()) - return fmtiter.result.build() + return buf.finish() def _copy_buffer(self): if self.getndim() == 0: diff --git a/pypy/module/_cffi_backend/cbuffer.py b/pypy/module/_cffi_backend/cbuffer.py --- a/pypy/module/_cffi_backend/cbuffer.py +++ b/pypy/module/_cffi_backend/cbuffer.py @@ -6,13 +6,13 @@ from pypy.module._cffi_backend import ctypestruct from pypy.interpreter.buffer import SimpleView -from rpython.rlib.buffer import Buffer +from rpython.rlib.buffer import RawBuffer from rpython.rtyper.annlowlevel import llstr from rpython.rtyper.lltypesystem import rffi from rpython.rtyper.lltypesystem.rstr import copy_string_to_raw -class LLBuffer(Buffer): +class LLBuffer(RawBuffer): _immutable_ = True def __init__(self, raw_cdata, size): @@ -35,7 +35,7 @@ def getslice(self, start, stop, step, size): if step == 1: return rffi.charpsize2str(rffi.ptradd(self.raw_cdata, start), size) - return Buffer.getslice(self, start, stop, step, size) + return RawBuffer.getslice(self, start, stop, step, size) def setslice(self, start, string): raw_cdata = rffi.ptradd(self.raw_cdata, start) diff --git a/pypy/module/_rawffi/buffer.py b/pypy/module/_rawffi/buffer.py --- a/pypy/module/_rawffi/buffer.py +++ b/pypy/module/_rawffi/buffer.py @@ -1,11 +1,10 @@ from rpython.rtyper.lltypesystem import rffi - -from rpython.rlib.buffer import Buffer +from rpython.rlib.buffer import RawBuffer # XXX not the most efficient implementation -class RawFFIBuffer(Buffer): +class RawFFIBuffer(RawBuffer): _immutable_ = True def __init__(self, datainstance): diff --git a/pypy/module/_socket/interp_socket.py b/pypy/module/_socket/interp_socket.py --- a/pypy/module/_socket/interp_socket.py +++ b/pypy/module/_socket/interp_socket.py @@ -367,20 +367,19 @@ except SocketError as e: raise converted_error(space, e) - @unwrap_spec(level=int, optname=int) - def getsockopt_w(self, space, level, optname, w_buflen=None): + @unwrap_spec(level=int, optname=int, buflen=int) + def getsockopt_w(self, space, level, optname, buflen=0): """getsockopt(level, option[, buffersize]) -> value Get a socket option. See the Unix manual for level and option. If a nonzero buffersize argument is given, the return value is a string of that length; otherwise it is an integer. """ - if w_buflen is None: + if buflen == 0: try: return space.newint(self.sock.getsockopt_int(level, optname)) except SocketError as e: raise converted_error(space, e) - buflen = space.int_w(w_buflen) return space.newbytes(self.sock.getsockopt(level, optname, buflen)) def gettimeout_w(self, space): diff --git a/pypy/module/_socket/test/test_sock_app.py b/pypy/module/_socket/test/test_sock_app.py --- a/pypy/module/_socket/test/test_sock_app.py +++ b/pypy/module/_socket/test/test_sock_app.py @@ -514,6 +514,19 @@ (reuse,) = struct.unpack('i', reusestr) assert reuse != 0 + def test_getsetsockopt_zero(self): + # related to issue #2561: when specifying the buffer size param: + # if 0 or None, should return the setted value, + # otherwise an empty buffer of the specified size + import _socket + s = _socket.socket() + assert s.getsockopt(_socket.IPPROTO_TCP, _socket.TCP_NODELAY, 0) == 0 + assert s.getsockopt(_socket.IPPROTO_TCP, _socket.TCP_NODELAY, 2) == b'\x00\x00' + s.setsockopt(_socket.IPPROTO_TCP, _socket.TCP_NODELAY, True) + assert s.getsockopt(_socket.IPPROTO_TCP, _socket.TCP_NODELAY, 0) == 1 + s.setsockopt(_socket.IPPROTO_TCP, _socket.TCP_NODELAY, 1) + assert s.getsockopt(_socket.IPPROTO_TCP, _socket.TCP_NODELAY, 0) == 1 + def test_socket_ioctl(self): import _socket, sys if sys.platform != 'win32': diff --git a/pypy/module/array/interp_array.py b/pypy/module/array/interp_array.py --- a/pypy/module/array/interp_array.py +++ b/pypy/module/array/interp_array.py @@ -1,11 +1,11 @@ from rpython.rlib import jit, rgc +from rpython.rlib.buffer import RawBuffer from rpython.rlib.objectmodel import keepalive_until_here from rpython.rlib.rarithmetic import ovfcheck, widen from rpython.rlib.unroll import unrolling_iterable from rpython.rtyper.annlowlevel import llstr from rpython.rtyper.lltypesystem import lltype, rffi from rpython.rtyper.lltypesystem.rstr import copy_string_to_raw -from rpython.rlib.buffer import Buffer from pypy.interpreter.buffer import BufferView from pypy.interpreter.baseobjspace import W_Root @@ -848,7 +848,7 @@ v.typecode = k unroll_typecodes = unrolling_iterable(types.keys()) -class ArrayData(Buffer): +class ArrayData(RawBuffer): _immutable_ = True readonly = False def __init__(self, w_array): diff --git a/pypy/module/bz2/interp_bz2.py b/pypy/module/bz2/interp_bz2.py --- a/pypy/module/bz2/interp_bz2.py +++ b/pypy/module/bz2/interp_bz2.py @@ -324,6 +324,7 @@ to compress, call the flush() method to finish the compression process, and return what is left in the internal buffers.""" + assert data is not None try: self.lock() datasize = len(data) diff --git a/pypy/module/cpyext/slotdefs.py b/pypy/module/cpyext/slotdefs.py --- a/pypy/module/cpyext/slotdefs.py +++ b/pypy/module/cpyext/slotdefs.py @@ -22,8 +22,7 @@ from pypy.interpreter.buffer import BufferView from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter.argument import Arguments - -from rpython.rlib.buffer import Buffer +from rpython.rlib.buffer import RawBuffer from rpython.rlib.unroll import unrolling_iterable from rpython.rlib.objectmodel import specialize, not_rpython from rpython.tool.sourcetools import func_renamer @@ -428,7 +427,7 @@ fq = FQ() -class CBuffer(Buffer): +class CBuffer(RawBuffer): _immutable_ = True def __init__(self, view): self.view = view diff --git a/pypy/module/micronumpy/concrete.py b/pypy/module/micronumpy/concrete.py --- a/pypy/module/micronumpy/concrete.py +++ b/pypy/module/micronumpy/concrete.py @@ -3,7 +3,7 @@ from rpython.rlib import jit, rgc from rpython.rlib.rarithmetic import ovfcheck from rpython.rlib.listsort import make_timsort_class -from rpython.rlib.buffer import Buffer +from rpython.rlib.buffer import RawBuffer from rpython.rlib.debug import make_sure_not_resized from rpython.rlib.rstring import StringBuilder from rpython.rlib.rawstorage import alloc_raw_storage, free_raw_storage, \ @@ -702,7 +702,8 @@ def __del__(self): free_raw_storage(self.storage) -class ArrayData(Buffer): + +class ArrayData(RawBuffer): _immutable_ = True def __init__(self, impl, readonly): self.impl = impl diff --git a/pypy/module/mmap/interp_mmap.py b/pypy/module/mmap/interp_mmap.py --- a/pypy/module/mmap/interp_mmap.py +++ b/pypy/module/mmap/interp_mmap.py @@ -4,8 +4,8 @@ from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault from pypy.interpreter.buffer import SimpleView -from rpython.rlib.buffer import Buffer from rpython.rlib import rmmap, rarithmetic, objectmodel +from rpython.rlib.buffer import RawBuffer from rpython.rlib.rmmap import RValueError, RTypeError, RMMapError from rpython.rlib.rstring import StringBuilder @@ -309,7 +309,7 @@ return OperationError(space.w_SystemError, space.newtext('%s' % e)) -class MMapBuffer(Buffer): +class MMapBuffer(RawBuffer): _immutable_ = True def __init__(self, space, mmap, readonly): @@ -329,7 +329,7 @@ if step == 1: return self.mmap.getslice(start, size) else: - return Buffer.getslice(self, start, stop, step, size) + return RawBuffer.getslice(self, start, stop, step, size) def setitem(self, index, char): self.check_valid_writeable() diff --git a/pypy/module/pypyjit/test_pypy_c/test_buffers.py b/pypy/module/pypyjit/test_pypy_c/test_buffers.py --- a/pypy/module/pypyjit/test_pypy_c/test_buffers.py +++ b/pypy/module/pypyjit/test_pypy_c/test_buffers.py @@ -34,29 +34,13 @@ i = 0 while i < n: i += 1 - struct.unpack('<i', a) # ID: unpack + struct.unpack('i', a) # ID: unpack return i log = self.run(main, [1000]) assert log.result == 1000 loop, = log.loops_by_filename(self.filepath) assert loop.match_by_id('unpack', """ guard_not_invalidated(descr=...) - p90 = newstr(4) - call_n(ConstClass(copy_raw_to_string), i55, p90, 0, 4, descr=<Callv 0 irii EF=5>) - guard_no_exception(descr=...) - i91 = strgetitem(p90, 0) - i92 = strgetitem(p90, 1) - i93 = int_lshift(i92, 8) - i94 = int_or(i91, i93) - i95 = strgetitem(p90, 2) - i96 = int_lshift(i95, 16) - i97 = int_or(i94, i96) - i98 = strgetitem(p90, 3) - i99 = int_ge(i98, 128) - guard_false(i99, descr=...) - i100 = int_lshift(i98, 24) - i101 = int_or(i97, i100) - i102 = getfield_raw_i(#, descr=<FieldS pypysig_long_struct.c_value 0>) - i103 = int_lt(i102, 0) - guard_false(i103, descr=...) + i66 = raw_load_i(i53, 0, descr=<ArrayS 4>) + --TICK-- """) diff --git a/pypy/module/pypyjit/test_pypy_c/test_ffi.py b/pypy/module/pypyjit/test_pypy_c/test_ffi.py --- a/pypy/module/pypyjit/test_pypy_c/test_ffi.py +++ b/pypy/module/pypyjit/test_pypy_c/test_ffi.py @@ -110,38 +110,6 @@ loops = log.loops_by_id('sleep') assert len(loops) == 1 # make sure that we actually JITted the loop - def test_ctypes_call(self): - from rpython.rlib.test.test_clibffi import get_libm_name - def main(libm_name): - import ctypes - libm = ctypes.CDLL(libm_name) - fabs = libm.fabs - fabs.argtypes = [ctypes.c_double] - fabs.restype = ctypes.c_double - x = -4 - i = 0 - while i < 300: - x = fabs(x) - x = x - 100 - i += 1 - return fabs._ptr.getaddr(), x - - libm_name = get_libm_name(sys.platform) - log = self.run(main, [libm_name], import_site=True) - fabs_addr, res = log.result - assert res == -4.0 - loop, = log.loops_by_filename(self.filepath) - ops = loop.allops() - opnames = log.opnames(ops) - assert opnames.count('new_with_vtable') == 1 # only the virtualref - py.test.skip("XXX re-optimize _ffi for the JIT?") - assert opnames.count('call_release_gil') == 1 - idx = opnames.index('call_release_gil') - call = ops[idx] - assert (call.args[0] == 'ConstClass(fabs)' or # e.g. OS/X - int(call.args[0]) == fabs_addr) - - def test__ffi_struct(self): def main(): from _rawffi.alt import _StructDescr, Field, types diff --git a/pypy/module/pypyjit/test_pypy_c/test_struct.py b/pypy/module/pypyjit/test_pypy_c/test_struct.py --- a/pypy/module/pypyjit/test_pypy_c/test_struct.py +++ b/pypy/module/pypyjit/test_pypy_c/test_struct.py @@ -30,32 +30,33 @@ loop, = log.loops_by_filename(self.filepath) # This could, of course stand some improvement, to remove all these # arithmatic ops, but we've removed all the core overhead. - assert loop.match_by_id("pack", """ - guard_not_invalidated(descr=...) - # struct.pack - %s - i11 = int_and(i4, 255) - i13 = int_rshift(i4, 8) - i14 = int_and(i13, 255) - i16 = int_rshift(i13, 8) - i17 = int_and(i16, 255) - i19 = int_rshift(i16, 8) - i20 = int_and(i19, 255) - """ % extra) + if sys.byteorder == 'little': + # on little endian machines, we take the fast path and store the + # value using gc_store_indexed + assert loop.match_by_id("pack", """ + guard_not_invalidated(descr=...) + # struct.pack + %s + p75 = newstr(4) + gc_store_indexed(p75, 0, _, 1, _, 4, descr=...) + """ % extra) + else: + assert loop.match_by_id("pack", """ + guard_not_invalidated(descr=...) + # struct.pack + %s + i11 = int_and(i4, 255) + i13 = int_rshift(i4, 8) + i14 = int_and(i13, 255) + i16 = int_rshift(i13, 8) + i17 = int_and(i16, 255) + i19 = int_rshift(i16, 8) + i20 = int_and(i19, 255) + """ % extra) if sys.byteorder == 'little': - # the newstr and the strsetitems are because the string is forced, - # which is in turn because the optimizer doesn't know how to handle a - # gc_load_indexed_i on a virtual string. It could be improved, but it - # is also true that in real life cases struct.unpack is called on - # strings which come from the outside, so it's a minor issue. assert loop.match_by_id("unpack", """ # struct.unpack - p88 = newstr(4) - strsetitem(p88, 0, i11) - strsetitem(p88, 1, i14) - strsetitem(p88, 2, i17) - strsetitem(p88, 3, i20) i91 = gc_load_indexed_i(p88, 0, 1, _, -4) """) else: @@ -93,27 +94,106 @@ assert loop.match_by_id('pack', """ guard_not_invalidated(descr=...) # struct.pack + p85 = newstr(8) + gc_store_indexed(p85, 0, -1, 1, _, 4, descr=...) %s - i11 = int_and(i4, 255) - i13 = int_rshift(i4, 8) - i14 = int_and(i13, 255) - i16 = int_rshift(i13, 8) - i17 = int_and(i16, 255) - i19 = int_rshift(i16, 8) - i20 = int_and(i19, 255) + gc_store_indexed(p85, 4, _, 1, _, 4, descr=...) """ % extra) assert loop.match_by_id('unpack', """ # struct.unpack - p88 = newstr(8) - strsetitem(p88, 0, 255) - strsetitem(p88, 1, 255) - strsetitem(p88, 2, 255) - strsetitem(p88, 3, 255) - strsetitem(p88, 4, i11) - strsetitem(p88, 5, i14) - strsetitem(p88, 6, i17) - strsetitem(p88, 7, i20) i90 = gc_load_indexed_i(p88, 0, 1, _, -4) i91 = gc_load_indexed_i(p88, 4, 1, _, -4) """) + + def test_unpack_raw_buffer(self): + def main(n): + import array + import struct + buf = struct.pack('H', 0x1234) + buf = array.array('b', buf) + i = 1 + res = 0 + while i < n: + val = struct.unpack("h", buf)[0] # ID: unpack + res += val + i += 1 + return res + log = self.run(main, [1000]) + assert log.result == main(1000) + loop, = log.loops_by_filename(self.filepath) + assert loop.match_by_id('unpack', """ + guard_not_invalidated(descr=...) + i65 = raw_load_i(i49, 0, descr=<ArrayS 2>) + """) + + def test_unpack_bytearray(self): + def main(n): + import struct + buf = struct.pack('H', 0x1234) + buf = bytearray(buf) + i = 1 + res = 0 + while i < n: + val = struct.unpack("h", buf)[0] # ID: unpack + res += val + i += 1 + return res + log = self.run(main, [1000]) + assert log.result == main(1000) + loop, = log.loops_by_filename(self.filepath) + # the offset of gc_load_indexed_i used to be the constant 0. However, + # now it is 'i46' because we need to add 0 to + # W_BytearrayObject._offset + assert loop.match_by_id('unpack', """ + guard_not_invalidated(descr=...) + i70 = gc_load_indexed_i(p48, i46, 1, _, -2) + """) + + def test_pack_into_raw_buffer(self): + def main(n): + import array + import struct + buf = array.array('b', '\x00'*8) + i = 1 + while i < n: + struct.pack_into("h", buf, 4, i) # ID: pack_into + i += 1 + return i + log = self.run(main, [1000]) + assert log.result == main(1000) + loop, = log.loops_by_filename(self.filepath) + assert loop.match_by_id('pack_into', """\ + guard_not_invalidated(descr=...) + i65 = int_le(i58, 32767) + guard_true(i65, descr=...) + raw_store(i55, 4, i58, descr=<ArrayS 2>) + """) + + def test_pack_into_bytearray(self): + def main(n): + import struct + buf = bytearray(8) + i = 1 + while i < n: + struct.pack_into("h", buf, 4, i) # ID: pack_into + i += 1 + return i + log = self.run(main, [1000]) + assert log.result == main(1000) + loop, = log.loops_by_filename(self.filepath) + assert loop.match_by_id('pack_into', """\ + guard_not_invalidated(descr=...) + p68 = getfield_gc_r(p14, descr=<FieldP pypy.objspace.std.bytearrayobject.W_BytearrayObject.inst__data \d+>) + i69 = getfield_gc_i(p68, descr=<FieldS list.length \d+>) + i70 = getfield_gc_i(p14, descr=<FieldS pypy.objspace.std.bytearrayobject.W_BytearrayObject.inst__offset \d+>) + i71 = int_sub(i69, i70) + i73 = int_sub(i71, 4) + i75 = int_lt(i73, 2) + guard_false(i75, descr=...) + i77 = int_le(i62, 32767) + guard_true(i77, descr=...) + p78 = getfield_gc_r(p68, descr=<FieldP list.items \d+>) + i81 = int_add(4, i70) + gc_store_indexed(p78, i81, i62, 1, _, 2, descr=<ArrayS 2>) + """) 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 @@ -2,7 +2,6 @@ maxint, intmask) from rpython.rlib import jit from rpython.rlib.objectmodel import specialize -from rpython.rlib.rstring import StringBuilder from rpython.rlib.rstruct.error import StructError from rpython.rlib.rstruct.formatiterator import FormatIterator @@ -10,11 +9,15 @@ class PackFormatIterator(FormatIterator): - def __init__(self, space, args_w, size): + def __init__(self, space, wbuf, args_w): self.space = space self.args_w = args_w self.args_index = 0 - self.result = StringBuilder(size) + self.pos = 0 + self.wbuf = wbuf + + def advance(self, count): + self.pos += count # This *should* be always unroll safe, the only way to get here is by # unroll the interpret function, which means the fmt is const, and thus @@ -31,8 +34,10 @@ @jit.unroll_safe def align(self, mask): - pad = (-self.result.getlength()) & mask - self.result.append_multiple_char('\x00', pad) + pad = (-self.pos) & mask + for i in range(self.pos, self.pos+pad): + self.wbuf.setitem(i, '\x00') + self.advance(pad) def finished(self): if self.args_index != len(self.args_w): @@ -135,15 +140,22 @@ if value != self.length: raise StructError("unpack str size too long for format") - def read(self, count): + def can_advance(self, count): if self.strides: count = self.strides[0] end = self.pos + count - if end > self.length: + return end <= self.length + + def advance(self, count): + if not self.can_advance(count): raise StructError("unpack str size too short for format") - s = self.buf.getslice(self.pos, end, 1, count) - self.pos = end - return s + self.pos += count + + def read(self, count): + curpos = self.pos + end = curpos + count + self.advance(count) # raise if we are out of bound + return self.buf.getslice(curpos, end, 1, count) @specialize.argtype(1) def appendobj(self, value): @@ -162,9 +174,8 @@ def get_pos(self): return self.pos - def get_buffer_as_string_maybe(self): - string, pos = self.buf.as_str_and_offset_maybe() - return string, pos+self.pos + def get_buffer_and_pos(self): + return self.buf, self.pos def skip(self, count): # assumption: UnpackFormatIterator only iterates over diff --git a/pypy/module/struct/interp_struct.py b/pypy/module/struct/interp_struct.py --- a/pypy/module/struct/interp_struct.py +++ b/pypy/module/struct/interp_struct.py @@ -1,5 +1,6 @@ from rpython.rlib import jit from rpython.rlib.buffer import SubBuffer +from rpython.rlib.mutbuffer import MutableStringBuffer from rpython.rlib.rstruct.error import StructError, StructOverflowError from rpython.rlib.rstruct.formatiterator import CalcSizeFormatIterator @@ -48,18 +49,17 @@ def _pack(space, format, args_w): """Return string containing values v1, v2, ... packed according to fmt.""" - if jit.isconstant(format): - size = _calcsize(space, format) - else: - size = 8 - fmtiter = PackFormatIterator(space, args_w, size) + size = _calcsize(space, format) + wbuf = MutableStringBuffer(size) + fmtiter = PackFormatIterator(space, wbuf, args_w) try: fmtiter.interpret(format) except StructOverflowError as e: raise OperationError(space.w_OverflowError, space.newtext(e.msg)) except StructError as e: raise OperationError(get_error(space), space.newtext(e.msg)) - return fmtiter.result.build() + assert fmtiter.pos == wbuf.getlength(), 'missing .advance() or wrong calcsize()' + return wbuf.finish() def pack(space, w_format, args_w): @@ -79,21 +79,27 @@ format = text_or_bytes_w(space, w_format) return do_pack_into(space, format, w_buffer, offset, args_w) -# XXX inefficient def do_pack_into(space, format, w_buffer, offset, args_w): """ Pack the values v1, v2, ... according to fmt. Write the packed bytes into the writable buffer buf starting at offset """ - res = _pack(space, format, args_w) + size = _calcsize(space, format) buf = space.writebuf_w(w_buffer) if offset < 0: offset += buf.getlength() - size = len(res) if offset < 0 or (buf.getlength() - offset) < size: raise oefmt(get_error(space), "pack_into requires a buffer of at least %d bytes", size) - buf.setslice(offset, res) + # + wbuf = SubBuffer(buf, offset, size) + fmtiter = PackFormatIterator(space, wbuf, args_w) + try: + fmtiter.interpret(format) + except StructOverflowError as e: + raise OperationError(space.w_OverflowError, space.newtext(e.msg)) + except StructError as e: + raise OperationError(get_error(space), space.newtext(e.msg)) def _unpack(space, format, buf): 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 @@ -591,7 +591,7 @@ class AppTestFastPath(object): - spaceconfig = dict(usemodules=['struct', '__pypy__']) + spaceconfig = dict(usemodules=['array', 'struct', '__pypy__']) def setup_class(cls): from rpython.rlib.rstruct import standardfmttable @@ -610,7 +610,43 @@ from rpython.rlib.rstruct import standardfmttable standardfmttable.ALLOW_SLOWPATH = True + def test_unpack_simple(self): + buf = self.struct.pack("iii", 0, 42, 43) + assert self.struct.unpack("iii", buf) == (0, 42, 43) + 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) + + def test_unpack_bytearray(self): + data = self.struct.pack("iii", 0, 42, 43) + buf = bytearray(data) + assert self.struct.unpack("iii", buf) == (0, 42, 43) + + def test_unpack_array(self): + import array + data = self.struct.pack("iii", 0, 42, 43) + buf = array.array('c', data) + assert self.struct.unpack("iii", buf) == (0, 42, 43) + + def test_pack_into_bytearray(self): + expected = self.struct.pack("ii", 42, 43) + buf = bytearray(len(expected)) + self.struct.pack_into("ii", buf, 0, 42, 43) + assert buf == expected + + def test_pack_into_bytearray_padding(self): + expected = self.struct.pack("xxi", 42) + buf = bytearray(len(expected)) + self.struct.pack_into("xxi", buf, 0, 42) + assert buf == expected + + def test_pack_into_bytearray_delete(self): + expected = self.struct.pack("i", 42) + # force W_BytearrayObject._delete_from_start + buf = bytearray(64) + del buf[:8] + self.struct.pack_into("i", buf, 0, 42) + buf = buf[:len(expected)] + assert buf == expected diff --git a/pypy/objspace/std/bytearrayobject.py b/pypy/objspace/std/bytearrayobject.py --- a/pypy/objspace/std/bytearrayobject.py +++ b/pypy/objspace/std/bytearrayobject.py @@ -8,10 +8,11 @@ from rpython.rlib.debug import check_list_of_chars, check_nonneg from rpython.rtyper.lltypesystem import rffi from rpython.rlib.rgc import (resizable_list_supporting_raw_ptr, - nonmoving_raw_ptr_for_resizable_list) + nonmoving_raw_ptr_for_resizable_list) from rpython.rlib import jit -from rpython.rlib.buffer import Buffer - +from rpython.rlib.buffer import (GCBuffer, + get_gc_data_for_list_of_chars, + get_gc_data_offset_for_list_of_chars) from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.error import OperationError, oefmt from pypy.objspace.std.bytesobject import makebytesdata_w, newbytesdata_w @@ -1274,7 +1275,8 @@ start += step -class BytearrayBuffer(Buffer): +@GCBuffer.decorate +class BytearrayBuffer(GCBuffer): _immutable_ = True def __init__(self, ba, readonly=False): @@ -1304,7 +1306,7 @@ if start != 0 or stop != len(data): data = data[start:stop] return "".join(data) - return Buffer.getslice(self, start, stop, step, size) + return GCBuffer.getslice(self, start, stop, step, size) def setslice(self, start, string): # No bounds checks. @@ -1319,6 +1321,16 @@ p = rffi.ptradd(p, ba._offset) return p + @staticmethod + def _get_gc_data_offset(): + return get_gc_data_offset_for_list_of_chars() + + def _get_gc_data_extra_offset(self): + return self.ba._offset + + def _get_gc_data(self): + return get_gc_data_for_list_of_chars(self.ba._data) + @specialize.argtype(1) def _memcmp(selfvalue, buffer, length): diff --git a/pypy/objspace/std/marshal_impl.py b/pypy/objspace/std/marshal_impl.py --- a/pypy/objspace/std/marshal_impl.py +++ b/pypy/objspace/std/marshal_impl.py @@ -1,5 +1,6 @@ from rpython.rlib.rarithmetic import LONG_BIT, r_longlong, r_uint -from rpython.rlib.rstring import StringBuilder, assert_str0 +from rpython.rlib.rstring import assert_str0 +from rpython.rlib.mutbuffer import MutableStringBuffer from rpython.rlib.rstruct import ieee from rpython.rlib.unroll import unrolling_iterable from rpython.rlib import objectmodel @@ -213,9 +214,9 @@ def pack_float(f): - result = StringBuilder(8) - ieee.pack_float(result, f, 8, False) - return result.build() + buf = MutableStringBuffer(8) + ieee.pack_float(buf, 0, f, 8, False) + return buf.finish() def unpack_float(s): return ieee.unpack_float(s, False) @@ -408,7 +409,7 @@ stacksize = u.get_int() flags = u.get_int() code = space.bytes_w(u.get_w_obj()) - consts_w = _unmarshal_tuple_w(u) + consts_w = _unmarshal_tuple_w(u) names = _unmarshal_strlist(u) varnames = _unmarshal_strlist(u) freevars = _unmarshal_strlist(u) diff --git a/rpython/annotator/binaryop.py b/rpython/annotator/binaryop.py --- a/rpython/annotator/binaryop.py +++ b/rpython/annotator/binaryop.py @@ -514,13 +514,13 @@ ne = eq def lt((tup1, tup2)): - raise Exception("unsupported: (...) < (...)") + raise AnnotatorError("unsupported: (...) < (...)") def le((tup1, tup2)): - raise Exception("unsupported: (...) <= (...)") + raise AnnotatorError("unsupported: (...) <= (...)") def gt((tup1, tup2)): - raise Exception("unsupported: (...) > (...)") + raise AnnotatorError("unsupported: (...) > (...)") def ge((tup1, tup2)): - raise Exception("unsupported: (...) >= (...)") + raise AnnotatorError("unsupported: (...) >= (...)") class __extend__(pairtype(SomeDict, SomeDict)): diff --git a/rpython/annotator/bookkeeper.py b/rpython/annotator/bookkeeper.py --- a/rpython/annotator/bookkeeper.py +++ b/rpython/annotator/bookkeeper.py @@ -15,7 +15,8 @@ SomeDict, SomeBuiltin, SomePBC, SomeInteger, TLS, SomeUnicodeCodePoint, s_None, s_ImpossibleValue, SomeBool, SomeTuple, SomeException, SomeImpossibleValue, SomeUnicodeString, SomeList, HarmlesslyBlocked, - SomeWeakRef, SomeByteArray, SomeConstantType, SomeProperty) + SomeWeakRef, SomeByteArray, SomeConstantType, SomeProperty, + AnnotatorError) from rpython.annotator.classdesc import ClassDef, ClassDesc from rpython.annotator.listdef import ListDef, ListItem from rpython.annotator.dictdef import DictDef @@ -343,7 +344,7 @@ elif x is None: return s_None else: - raise Exception("Don't know how to represent %r" % (x,)) + raise AnnotatorError("Don't know how to represent %r" % (x,)) result.const = x return result @@ -363,7 +364,7 @@ result = self.newfuncdesc(pyobj) elif isinstance(pyobj, (type, types.ClassType)): if pyobj is object: - raise Exception("ClassDesc for object not supported") + raise AnnotatorError("ClassDesc for object not supported") if pyobj.__module__ == '__builtin__': # avoid making classdefs for builtin types result = self.getfrozen(pyobj) else: @@ -400,7 +401,7 @@ msg = "object with a __call__ is not RPython" else: msg = "unexpected prebuilt constant" - raise Exception("%s: %r" % (msg, pyobj)) + raise AnnotatorError("%s: %r" % (msg, pyobj)) result = self.getfrozen(pyobj) self.descs[obj_key] = result return result @@ -589,7 +590,7 @@ for name, value in dict.iteritems(): if value is func: return cls, name - raise Exception("could not match bound-method to attribute name: %r" % (boundmeth,)) + raise AnnotatorError("could not match bound-method to attribute name: %r" % (boundmeth,)) def ishashable(x): try: diff --git a/rpython/annotator/classdesc.py b/rpython/annotator/classdesc.py --- a/rpython/annotator/classdesc.py +++ b/rpython/annotator/classdesc.py @@ -550,8 +550,8 @@ "with _mixin_: %r" % (cls,)) base = b1 if mixins_before and mixins_after: - raise Exception("unsupported: class %r has mixin bases both" - " before and after the regular base" % (self,)) + raise AnnotatorError("unsupported: class %r has mixin bases both" + " before and after the regular base" % (self,)) self.add_mixins(mixins_after, check_not_in=base) self.add_mixins(mixins_before) self.add_sources_for_class(cls) @@ -569,8 +569,8 @@ attrs.update(decl) if self.basedesc is not None: if self.basedesc.all_enforced_attrs is None: - raise Exception("%r has slots or _attrs_, " - "but not its base class" % (cls,)) + raise AnnotatorError("%r has slots or _attrs_, " + "but not its base class" % (cls,)) attrs.update(self.basedesc.all_enforced_attrs) self.all_enforced_attrs = attrs @@ -714,8 +714,8 @@ try: args.fixedunpack(0) except ValueError: - raise Exception("default __init__ takes no argument" - " (class %s)" % (self.name,)) + raise AnnotatorError("default __init__ takes no argument" + " (class %s)" % (self.name,)) elif self.pyobj is Exception: # check explicitly against "raise Exception, x" where x # is a low-level exception pointer @@ -824,8 +824,8 @@ # actual copy in the rtyper). Tested in rpython.rtyper.test.test_rlist, # test_immutable_list_out_of_instance. if self._detect_invalid_attrs and attr in self._detect_invalid_attrs: - raise Exception("field %r was migrated to %r from a subclass in " - "which it was declared as _immutable_fields_" % + raise AnnotatorError("field %r was migrated to %r from a subclass in " + "which it was declared as _immutable_fields_" % (attr, self.pyobj)) search1 = '%s[*]' % (attr,) search2 = '%s?[*]' % (attr,) @@ -858,7 +858,7 @@ # call to a single class, look at the result annotation # in case it was specialized if not isinstance(s_result, SomeInstance): - raise Exception("calling a class didn't return an instance??") + raise AnnotatorError("calling a class didn't return an instance??") classdefs = [s_result.classdef] else: # call to multiple classes: specialization not supported diff --git a/rpython/annotator/description.py b/rpython/annotator/description.py --- a/rpython/annotator/description.py +++ b/rpython/annotator/description.py @@ -314,8 +314,8 @@ enforceargs = getattr(self.pyobj, '_annenforceargs_', None) signature = getattr(self.pyobj, '_signature_', None) if enforceargs and signature: - raise Exception("%r: signature and enforceargs cannot both be " - "used" % (self,)) + raise AnnotatorError("%r: signature and enforceargs cannot both be " + "used" % (self,)) if enforceargs: if not callable(enforceargs): from rpython.annotator.signature import Sig @@ -432,7 +432,7 @@ def func_args(self, args): from rpython.annotator.model import SomeInstance if self.selfclassdef is None: - raise Exception("calling %r" % (self,)) + raise AnnotatorError("calling %r" % (self,)) s_instance = SomeInstance(self.selfclassdef, flags=self.flags) return args.prepend(s_instance) diff --git a/rpython/annotator/test/test_annrpython.py b/rpython/annotator/test/test_annrpython.py --- a/rpython/annotator/test/test_annrpython.py +++ b/rpython/annotator/test/test_annrpython.py @@ -4584,7 +4584,7 @@ return Ellipsis a = self.RPythonAnnotator() e = py.test.raises(Exception, a.build_types, f, []) - assert str(e.value) == "Don't know how to represent Ellipsis" + assert "Don't know how to represent Ellipsis" in str(e.value) def test_must_be_light_finalizer(self): from rpython.rlib import rgc diff --git a/rpython/annotator/unaryop.py b/rpython/annotator/unaryop.py --- a/rpython/annotator/unaryop.py +++ b/rpython/annotator/unaryop.py @@ -185,8 +185,8 @@ return SomeString() def id(self): - raise Exception("cannot use id() in RPython; " - "see objectmodel.compute_xxx()") + raise AnnotatorError("cannot use id() in RPython; " + "see objectmodel.compute_xxx()") def int(self): return SomeInteger() @@ -421,7 +421,7 @@ def setslice(self, s_start, s_stop, s_iterable): check_negative_slice(s_start, s_stop) if not isinstance(s_iterable, SomeList): - raise Exception("list[start:stop] = x: x must be a list") + raise AnnotatorError("list[start:stop] = x: x must be a list") self.listdef.mutate() self.listdef.agree(getbookkeeper(), s_iterable.listdef) self.listdef.resize() diff --git a/rpython/jit/backend/arm/test/test_llop.py b/rpython/jit/backend/arm/test/test_llop.py new file mode 100644 --- /dev/null +++ b/rpython/jit/backend/arm/test/test_llop.py @@ -0,0 +1,9 @@ +from rpython.jit.backend.arm.test.support import JitARMMixin +from rpython.jit.metainterp.test.test_llop import TestLLOp as _TestLLOp + + +class TestLLOp(JitARMMixin, _TestLLOp): + # for the individual tests see + # ====> ../../../metainterp/test/test_llop.py + pass + diff --git a/rpython/jit/backend/llgraph/runner.py b/rpython/jit/backend/llgraph/runner.py --- a/rpython/jit/backend/llgraph/runner.py +++ b/rpython/jit/backend/llgraph/runner.py @@ -716,16 +716,28 @@ else: return self.bh_raw_load_i(struct, offset, descr) + def _get_int_type_from_size(self, size): + if size == 1: + return rffi.UCHAR + elif size == 2: + return rffi.USHORT + elif size == 4: + return rffi.UINT + elif size == 8: + return rffi.ULONGLONG + elif size == -1: + return rffi.SIGNEDCHAR + elif size == -2: + return rffi.SHORT + elif size == -4: + return rffi.INT + elif size == -8: + return rffi.LONGLONG + else: + raise NotImplementedError(size) + def bh_gc_load_indexed_i(self, struct, index, scale, base_ofs, bytes): - if bytes == 1: T = rffi.UCHAR - elif bytes == 2: T = rffi.USHORT - elif bytes == 4: T = rffi.UINT - elif bytes == 8: T = rffi.ULONGLONG - elif bytes == -1: T = rffi.SIGNEDCHAR - elif bytes == -2: T = rffi.SHORT - elif bytes == -4: T = rffi.INT - elif bytes == -8: T = rffi.LONGLONG - else: raise NotImplementedError(bytes) + T = self._get_int_type_from_size(bytes) x = llop.gc_load_indexed(T, struct, index, scale, base_ofs) return lltype.cast_primitive(lltype.Signed, x) @@ -735,6 +747,30 @@ return llop.gc_load_indexed(longlong.FLOATSTORAGE, struct, index, scale, base_ofs) + def bh_gc_store_indexed_i(self, struct, index, val, scale, base_ofs, bytes, + descr): + T = self._get_int_type_from_size(bytes) + val = lltype.cast_primitive(T, val) + if descr.A.OF == lltype.SingleFloat: + val = longlong.int2singlefloat(val) + llop.gc_store_indexed(lltype.Void, struct, index, val, scale, base_ofs) + + def bh_gc_store_indexed_f(self, struct, index, val, scale, base_ofs, bytes, + descr): + if bytes != 8: + raise Exception("gc_store_indexed_f is only for 'double'!") + val = longlong.getrealfloat(val) + llop.gc_store_indexed(lltype.Void, struct, index, val, scale, base_ofs) + + def bh_gc_store_indexed(self, struct, index, val, scale, base_ofs, bytes, + descr): + if descr.A.OF == lltype.Float: + self.bh_gc_store_indexed_f(struct, index, val, scale, base_ofs, + bytes, descr) + else: + self.bh_gc_store_indexed_i(struct, index, val, scale, base_ofs, + bytes, descr) + def bh_increment_debug_counter(self, addr): p = rffi.cast(rffi.CArrayPtr(lltype.Signed), addr) p[0] += 1 diff --git a/rpython/jit/backend/llsupport/llmodel.py b/rpython/jit/backend/llsupport/llmodel.py --- a/rpython/jit/backend/llsupport/llmodel.py +++ b/rpython/jit/backend/llsupport/llmodel.py @@ -496,6 +496,7 @@ @specialize.argtype(1) def write_float_at_mem(self, gcref, ofs, newvalue): llop.raw_store(lltype.Void, gcref, ofs, newvalue) + write_float_at_mem._annenforceargs_ = [None, None, None, longlong.r_float_storage] # ____________________________________________________________ @@ -754,6 +755,16 @@ offset = base_ofs + scale * index return self.read_float_at_mem(addr, offset) + def bh_gc_store_indexed_i(self, addr, index, val, scale, base_ofs, bytes, + descr): + offset = base_ofs + scale * index + self.write_int_at_mem(addr, offset, bytes, val) + + def bh_gc_store_indexed_f(self, addr, index, val, scale, base_ofs, bytes, + descr): + offset = base_ofs + scale * index + self.write_float_at_mem(addr, offset, val) + def bh_new(self, sizedescr): return self.gc_ll_descr.gc_malloc(sizedescr) diff --git a/rpython/jit/backend/x86/test/test_llop.py b/rpython/jit/backend/x86/test/test_llop.py new file mode 100644 --- /dev/null +++ b/rpython/jit/backend/x86/test/test_llop.py @@ -0,0 +1,14 @@ +from rpython.jit.backend.x86.test.test_basic import Jit386Mixin +from rpython.jit.metainterp.test.test_llop import TestLLOp as _TestLLOp + + +class TestLLOp(Jit386Mixin, _TestLLOp): + # for the individual tests see + # ====> ../../../metainterp/test/test_llop.py + + # do NOT test the blackhole implementation of gc_store_indexed. It cannot + # work inside tests because llmodel.py:bh_gc_store_indexed_* receive a + # symbolic as the offset. It is not a problem because it is tested anyway + # by the same test in test_metainterp.py + TEST_BLACKHOLE = False + diff --git a/rpython/jit/backend/x86/test/test_strstorage.py b/rpython/jit/backend/x86/test/test_strstorage.py deleted file mode 100644 --- a/rpython/jit/backend/x86/test/test_strstorage.py +++ /dev/null @@ -1,8 +0,0 @@ -from rpython.jit.backend.x86.test.test_basic import Jit386Mixin -from rpython.jit.metainterp.test.test_strstorage import TestStrStorage as _TestStrStorage - - -class TestStrStorage(Jit386Mixin, _TestStrStorage): - # for the individual tests see - # ====> ../../../metainterp/test/test_strstorage.py - pass diff --git a/rpython/jit/codewriter/jtransform.py b/rpython/jit/codewriter/jtransform.py --- a/rpython/jit/codewriter/jtransform.py +++ b/rpython/jit/codewriter/jtransform.py @@ -1131,6 +1131,26 @@ [op.args[0], op.args[1], op.args[2], op.args[3], c_bytes], op.result) + def rewrite_op_gc_store_indexed(self, op): + T = op.args[2].concretetype + kind = getkind(T)[0] + assert kind != 'r' + descr = self.cpu.arraydescrof(rffi.CArray(T)) + if (not isinstance(op.args[3], Constant) or + not isinstance(op.args[4], Constant)): + raise NotImplementedError("gc_store_indexed: 'scale' and 'base_ofs'" + " should be constants") + # According to the comment in resoperation.py, "itemsize is not signed + # (always > 0)", so we don't need the "bytes = -bytes" line which is + # in rewrite_op_gc_load_indexed + bytes = descr.get_item_size_in_bytes() + c_bytes = Constant(bytes, lltype.Signed) + return SpaceOperation('gc_store_indexed_%s' % kind, + [op.args[0], op.args[1], op.args[2], + op.args[3], op.args[4], c_bytes, descr], None) + + + def _rewrite_equality(self, op, opname): arg0, arg1 = op.args if isinstance(arg0, Constant) and not arg0.value: diff --git a/rpython/jit/metainterp/blackhole.py b/rpython/jit/metainterp/blackhole.py --- a/rpython/jit/metainterp/blackhole.py +++ b/rpython/jit/metainterp/blackhole.py @@ -1478,6 +1478,18 @@ def bhimpl_gc_load_indexed_f(cpu, addr, index, scale, base_ofs, bytes): return cpu.bh_gc_load_indexed_f(addr, index,scale,base_ofs, bytes) + @arguments("cpu", "r", "i", "i", "i", "i", "i", "d") + def bhimpl_gc_store_indexed_i(cpu, addr, index, val, scale, base_ofs, bytes, + arraydescr): + return cpu.bh_gc_store_indexed_i(addr, index, val, scale,base_ofs, bytes, + arraydescr) + + @arguments("cpu", "r", "i", "f", "i", "i", "i", "d") + def bhimpl_gc_store_indexed_f(cpu, addr, index, val, scale, base_ofs, bytes, + arraydescr): + return cpu.bh_gc_store_indexed_f(addr, index, val, scale,base_ofs, bytes, + arraydescr) + @arguments("r", "d", "d") def bhimpl_record_quasiimmut_field(struct, fielddescr, mutatefielddescr): pass diff --git a/rpython/jit/metainterp/compile.py b/rpython/jit/metainterp/compile.py --- a/rpython/jit/metainterp/compile.py +++ b/rpython/jit/metainterp/compile.py @@ -110,7 +110,7 @@ """ log_noopt = False - def __init__(self, trace, celltoken, state, runtime_boxes, + def __init__(self, trace, celltoken, state, call_pure_results=None, enable_opts=None, inline_short_preamble=True): self.trace = trace @@ -119,8 +119,6 @@ self.state = state self.call_pure_results = call_pure_results self.inline_short_preamble = inline_short_preamble - assert runtime_boxes is not None - self.runtime_boxes = runtime_boxes def optimize(self, metainterp_sd, jitdriver_sd, optimizations, unroll): from rpython.jit.metainterp.optimizeopt.unroll import UnrollOptimizer @@ -128,11 +126,7 @@ assert unroll # we should not be here if it's disabled opt = UnrollOptimizer(metainterp_sd, jitdriver_sd, optimizations) return opt.optimize_peeled_loop(self.trace, self.celltoken, self.state, - self.runtime_boxes, self.call_pure_results, self.inline_short_preamble) - - def forget_optimization_info(self): - self.state.forget_optimization_info() - CompileData.forget_optimization_info(self) + self.call_pure_results, self.inline_short_preamble) def show_procedures(metainterp_sd, procedure=None, error=None): # debugging @@ -298,7 +292,7 @@ start_descr = TargetToken(jitcell_token, original_jitcell_token=jitcell_token) jitcell_token.target_tokens = [start_descr] - loop_data = UnrolledLoopData(trace, jitcell_token, start_state, jumpargs, + loop_data = UnrolledLoopData(trace, jitcell_token, start_state, call_pure_results=call_pure_results, enable_opts=enable_opts) try: @@ -368,7 +362,7 @@ history.record(rop.JUMP, jumpargs[:], None, descr=loop_jitcell_token) enable_opts = jitdriver_sd.warmstate.enable_opts call_pure_results = metainterp.call_pure_results - loop_data = UnrolledLoopData(trace, loop_jitcell_token, start_state, jumpargs, + loop_data = UnrolledLoopData(trace, loop_jitcell_token, start_state, call_pure_results=call_pure_results, enable_opts=enable_opts) try: @@ -380,7 +374,6 @@ history.cut(cut) history.record(rop.JUMP, jumpargs[:], None, descr=loop_jitcell_token) loop_data = UnrolledLoopData(trace, loop_jitcell_token, start_state, - jumpargs, call_pure_results=call_pure_results, enable_opts=enable_opts, inline_short_preamble=False) @@ -525,7 +518,7 @@ for item in lst: item.set_forwarded(None) # XXX we should really do it, but we need to remember the values - # somehow for ContinueRunningNormally + # somehoe for ContinueRunningNormally if reset_values: item.reset_value() diff --git a/rpython/jit/metainterp/executor.py b/rpython/jit/metainterp/executor.py --- a/rpython/jit/metainterp/executor.py +++ b/rpython/jit/metainterp/executor.py @@ -251,6 +251,25 @@ else: return BoxInt(cpu.bh_raw_load_i(addr, offset, arraydescr)) +def do_gc_store_indexed(cpu, _, addrbox, indexbox, valuebox, scalebox, + base_ofsbox, bytesbox, arraydescr): + addr = addrbox.getref_base() + index = indexbox.getint() + scale = scalebox.getint() + base_ofs = base_ofsbox.getint() + bytes = bytesbox.getint() + if arraydescr.is_array_of_pointers(): + raise AssertionError("cannot store GC pointers in gc_store_indexed for now") + elif arraydescr.is_array_of_floats(): + floatval = valuebox.getfloatstorage() + cpu.bh_gc_store_indexed_f(addr, index, floatval, scale, base_ofs, bytes, + arraydescr) + else: + intval = valuebox.getint() + cpu.bh_gc_store_indexed_i(addr, index, intval, scale, base_ofs, bytes, + arraydescr) + + def exec_new_with_vtable(cpu, descr): return cpu.bh_new_with_vtable(descr) diff --git a/rpython/jit/metainterp/optimizeopt/heap.py b/rpython/jit/metainterp/optimizeopt/heap.py --- a/rpython/jit/metainterp/optimizeopt/heap.py +++ b/rpython/jit/metainterp/optimizeopt/heap.py @@ -12,7 +12,7 @@ from rpython.jit.metainterp.optimize import InvalidLoop from rpython.jit.metainterp.resoperation import rop, ResOperation, OpHelpers,\ AbstractResOp, GuardResOp -from rpython.rlib.objectmodel import we_are_translated, we_are_debug +from rpython.rlib.objectmodel import we_are_translated from rpython.jit.metainterp.optimizeopt import info @@ -173,7 +173,7 @@ def _getfield(self, opinfo, descr, optheap, true_force=True): res = opinfo.getfield(descr, optheap) - if we_are_debug() and res: + if not we_are_translated() and res: if isinstance(opinfo, info.AbstractStructPtrInfo): assert opinfo in self.cached_infos if isinstance(res, PreambleOp): @@ -203,7 +203,7 @@ def _getfield(self, opinfo, descr, optheap, true_force=True): res = opinfo.getitem(descr, self.index, optheap) - if we_are_debug() and res: + if not we_are_translated() and res: if isinstance(opinfo, info.ArrayPtrInfo): assert opinfo in self.cached_infos if (isinstance(res, PreambleOp) and diff --git a/rpython/jit/metainterp/optimizeopt/optimizer.py b/rpython/jit/metainterp/optimizeopt/optimizer.py --- a/rpython/jit/metainterp/optimizeopt/optimizer.py +++ b/rpython/jit/metainterp/optimizeopt/optimizer.py @@ -24,20 +24,9 @@ llhelper.CONST_NULLREF = llhelper.CONST_NULL REMOVED = AbstractResOp() -def check_no_forwarding(lsts): - for lst in lsts: - for op in lst: - assert op.get_forwarded() is None - class LoopInfo(object): label_op = None - def _check_no_forwarding(self): - pass - - def forget_optimization_info(self): - pass - class BasicLoopInfo(LoopInfo): def __init__(self, inputargs, quasi_immutable_deps, jump_op): self.inputargs = inputargs @@ -567,8 +556,7 @@ return (BasicLoopInfo(trace.inputargs, self.quasi_immutable_deps, last_op), self._newoperations) - @staticmethod - def _clean_optimization_info(lst): + def _clean_optimization_info(self, lst): for op in lst: if op.get_forwarded() is not None: op.set_forwarded(None) diff --git a/rpython/jit/metainterp/optimizeopt/shortpreamble.py b/rpython/jit/metainterp/optimizeopt/shortpreamble.py --- a/rpython/jit/metainterp/optimizeopt/shortpreamble.py +++ b/rpython/jit/metainterp/optimizeopt/shortpreamble.py @@ -5,7 +5,6 @@ rop, AbstractResOp, AbstractInputArg from rpython.jit.metainterp.history import Const, make_hashable_int,\ TreeLoop -from rpython.jit.metainterp.optimize import InvalidLoop from rpython.jit.metainterp.optimizeopt import info class PreambleOp(AbstractResOp): @@ -19,7 +18,7 @@ See force_op_from_preamble for details how the extra things are put. """ op = None - + def __init__(self, op, preamble_op, invented_name): self.op = op self.preamble_op = preamble_op @@ -52,13 +51,7 @@ class AbstractShortOp(object): """ An operation that is potentially produced by the short preamble """ - res = None - - def _check_no_forwarding(self): - assert self.res.get_forwarded() is None - - def forget_optimization_info(self): - self.res.clear_forwarded() + pass class HeapOp(AbstractShortOp): def __init__(self, res, getfield_op): @@ -108,14 +101,6 @@ descr=sop.getdescr()) return ProducedShortOp(self, preamble_op) - def _check_no_forwarding(self): - AbstractShortOp._check_no_forwarding(self) - assert self.getfield_op.get_forwarded() is None - - def forget_optimization_info(self): - AbstractShortOp.forget_optimization_info(self) - self.getfield_op.clear_forwarded() - def __repr__(self): return "HeapOp(%r)" % (self.res,) @@ -208,16 +193,6 @@ l.append(pop) return l - def _check_no_forwarding(self): - AbstractShortOp._check_no_forwarding(self) - self.one._check_no_forwarding() - self.two._check_no_forwarding() - - def forget_optimization_info(self): - AbstractShortOp.forget_optimization_info(self) - self.one.forget_optimization_info() - self.two.forget_optimization_info() - def repr(self, memo): return "CompoundOp(%s, %s, %s)" % (self.res.repr(memo), self.one.repr(memo), @@ -228,7 +203,7 @@ class ProducedShortOp(AbstractProducedShortOp): invented_name = False - + def __init__(self, short_op, preamble_op): self.short_op = short_op self.preamble_op = preamble_op @@ -240,14 +215,6 @@ def repr(self, memo): return self.short_op.repr(memo) - def _check_no_forwarding(self): - self.short_op._check_no_forwarding() - assert self.preamble_op.get_forwarded() is None - - def forget_optimization_info(self): - self.short_op.forget_optimization_info() - self.preamble_op.clear_forwarded() - def __repr__(self): return "%r -> %r" % (self.short_op, self.preamble_op) @@ -268,14 +235,6 @@ def repr(self, memo): return "INP(%s)" % (self.res.repr(memo),) - def _check_no_forwarding(self): - AbstractShortOp._check_no_forwarding(self) - assert self.preamble_op.get_forwarded() is None - - def forget_optimization_info(self): - AbstractShortOp.forget_optimization_info(self) - self.preamble_op.clear_forwarded() - def __repr__(self): return "INP(%r -> %r)" % (self.res, self.preamble_op) @@ -495,23 +454,16 @@ self.sb = sb self.extra_same_as = self.sb.extra_same_as self.target_token = target_token - self.build_inplace = False def setup(self, jump_args, short, label_args): self.jump_args = jump_args self.short = short self.label_args = label_args - self.build_inplace = True def add_preamble_op(self, preamble_op): """ Notice that we're actually using the preamble_op, add it to label and jump """ - # Could this be considered a speculative error? - # This check should only fail when trying to jump to an existing trace - # by forcing portions of the virtualstate. - if not self.build_inplace: - raise InvalidLoop("Forcing boxes would modify an existing short preamble") op = preamble_op.op.get_box_replacement() if preamble_op.invented_name: self.extra_same_as.append(op) @@ -519,8 +471,6 @@ self.jump_args.append(preamble_op.preamble_op) def use_box(self, box, preamble_op, optimizer=None): - if not self.build_inplace: - raise InvalidLoop("Forcing boxes would modify an existing short preamble") jump_op = self.short.pop() AbstractShortPreambleBuilder.use_box(self, box, preamble_op, optimizer) self.short.append(jump_op) diff --git a/rpython/jit/metainterp/optimizeopt/test/test_util.py b/rpython/jit/metainterp/optimizeopt/test/test_util.py --- a/rpython/jit/metainterp/optimizeopt/test/test_util.py +++ b/rpython/jit/metainterp/optimizeopt/test/test_util.py @@ -577,7 +577,6 @@ # compile_data.enable_opts = self.enable_opts state = optimize_trace(metainterp_sd, None, compile_data) - state[0]._check_no_forwarding() return state def _convert_call_pure_results(self, d): @@ -626,7 +625,7 @@ start_state, preamble_ops = self._do_optimize_loop(preamble_data) preamble_data.forget_optimization_info() loop_data = compile.UnrolledLoopData(preamble_data.trace, - celltoken, start_state, runtime_boxes, call_pure_results) + celltoken, start_state, call_pure_results) loop_info, ops = self._do_optimize_loop(loop_data) preamble = TreeLoop('preamble') preamble.inputargs = start_state.renamed_inputargs diff --git a/rpython/jit/metainterp/optimizeopt/unroll.py b/rpython/jit/metainterp/optimizeopt/unroll.py --- a/rpython/jit/metainterp/optimizeopt/unroll.py +++ b/rpython/jit/metainterp/optimizeopt/unroll.py @@ -6,7 +6,7 @@ from rpython.jit.metainterp.optimizeopt import info, intutils from rpython.jit.metainterp.optimize import InvalidLoop, SpeculativeError from rpython.jit.metainterp.optimizeopt.optimizer import Optimizer,\ - Optimization, LoopInfo, MININT, MAXINT, BasicLoopInfo, check_no_forwarding + Optimization, LoopInfo, MININT, MAXINT, BasicLoopInfo from rpython.jit.metainterp.optimizeopt.vstring import StrPtrInfo from rpython.jit.metainterp.optimizeopt.virtualstate import ( VirtualStateConstructor, VirtualStatesCantMatch) @@ -35,7 +35,7 @@ def setinfo_from_preamble_list(self, lst, infos): for item in lst: - if item is None or isinstance(item, Const): + if item is None: continue i = infos.get(item, None) if i is not None: @@ -99,6 +99,7 @@ elif isinstance(preamble_info, info.FloatConstInfo): op.set_forwarded(preamble_info._const) + class UnrollOptimizer(Optimization): """Unroll the loop into two iterations. The first one will become the preamble or entry bridge (don't think there is a @@ -116,22 +117,26 @@ return modifier.get_virtual_state(args) def _check_no_forwarding(self, lsts, check_newops=True): - check_no_forwarding(lsts) + for lst in lsts: + for op in lst: + assert op.get_forwarded() is None if check_newops: assert not self.optimizer._newoperations + def optimize_preamble(self, trace, runtime_boxes, call_pure_results, memo): info, newops = self.optimizer.propagate_all_forward( trace.get_iter(), call_pure_results, flush=False) exported_state = self.export_state(info.jump_op.getarglist(), - info.inputargs, memo) + info.inputargs, + runtime_boxes, memo) exported_state.quasi_immutable_deps = info.quasi_immutable_deps # we need to absolutely make sure that we've cleaned up all # the optimization info self.optimizer._clean_optimization_info(self.optimizer._newoperations) return exported_state, self.optimizer._newoperations - def optimize_peeled_loop(self, trace, celltoken, state, runtime_boxes, + def optimize_peeled_loop(self, trace, celltoken, state, call_pure_results, inline_short_preamble=True): trace = trace.get_iter() try: @@ -183,7 +188,7 @@ try: new_virtual_state = self.jump_to_existing_trace( - end_jump, label_op, runtime_boxes, force_boxes=False) + end_jump, label_op, state.runtime_boxes, force_boxes=False) except InvalidLoop: # inlining short preamble failed, jump to preamble self.jump_to_preamble(celltoken, end_jump, info) @@ -196,7 +201,7 @@ # to the preamble. try: new_virtual_state = self.jump_to_existing_trace( - end_jump, label_op, runtime_boxes, force_boxes=True) + end_jump, label_op, state.runtime_boxes, force_boxes=True) except InvalidLoop: pass @@ -279,7 +284,8 @@ debug_print("Retrace count reached, jumping to preamble") return self.jump_to_preamble(cell_token, jump_op, info) exported_state = self.export_state(info.jump_op.getarglist(), - info.inputargs, box_names_memo) + info.inputargs, runtime_boxes, + box_names_memo) exported_state.quasi_immutable_deps = self.optimizer.quasi_immutable_deps self.optimizer._clean_optimization_info(self.optimizer._newoperations) return exported_state, self.optimizer._newoperations @@ -443,7 +449,8 @@ continue self._expand_info(item, infos) - def export_state(self, original_label_args, renamed_inputargs, memo): + def export_state(self, original_label_args, renamed_inputargs, + runtime_boxes, memo): end_args = [self.optimizer.force_box_for_end_of_preamble(a) for a in original_label_args] self.optimizer.flush() @@ -464,17 +471,16 @@ op = produced_op.short_op.res if not isinstance(op, Const): self._expand_info(op, infos) + self.optimizer._clean_optimization_info(end_args) return ExportedState(label_args, end_args, virtual_state, infos, short_boxes, renamed_inputargs, - short_inputargs, memo) + short_inputargs, runtime_boxes, memo) def import_state(self, targetargs, exported_state): # the mapping between input args (from old label) and what we need # to actually emit. Update the info assert (len(exported_state.next_iteration_args) == len(targetargs)) - self._check_no_forwarding([targetargs]) - exported_state._check_no_forwarding() for i, target in enumerate(exported_state.next_iteration_args): source = targetargs[i] assert source is not target @@ -530,11 +536,13 @@ * renamed_inputargs - the start label arguments in optimized version * short_inputargs - the renamed inputargs for short preamble * quasi_immutable_deps - for tracking quasi immutables + * runtime_boxes - runtime values for boxes, necessary when generating + guards to jump to """ def __init__(self, end_args, next_iteration_args, virtual_state, exported_infos, short_boxes, renamed_inputargs, - short_inputargs, memo): + short_inputargs, runtime_boxes, memo): self.end_args = end_args self.next_iteration_args = next_iteration_args self.virtual_state = virtual_state @@ -542,8 +550,8 @@ self.short_boxes = short_boxes self.renamed_inputargs = renamed_inputargs self.short_inputargs = short_inputargs + self.runtime_boxes = runtime_boxes self.dump(memo) - self.forget_optimization_info() def dump(self, memo): if have_debug_prints(): @@ -553,35 +561,5 @@ debug_print(" " + box.repr(memo)) debug_stop("jit-log-exported-state") - def _check_no_forwarding(self): - """ Ensures that no optimization state is attached to relevant operations - before importing anything. """ - # Some of these may be redunant - check_no_forwarding([ - self.end_args, - self.next_iteration_args, - self.renamed_inputargs, - self.short_inputargs, - self.exported_infos.keys()]) - for box in self.short_boxes: - box._check_no_forwarding() - - def forget_optimization_info(self): - """ Clean up optimization info on all operations stored in the ExportedState. - - This function needs to be called when exporting the optimizer state to - prevent leaking of optimization information between invocations of the - optimizer. - - That includes cleaning up in the event that optimize_peeled_loop() fails - with an InvalidLoop exception, as optimize_peeled_loop() mutates the - contents of ExportedState. - """ - Optimizer._clean_optimization_info(self.renamed_inputargs) - for box in self.exported_infos.iterkeys(): - box.clear_forwarded() - for box in self.short_boxes: - box.forget_optimization_info() - def final(self): return False diff --git a/rpython/jit/metainterp/pyjitpl.py b/rpython/jit/metainterp/pyjitpl.py --- a/rpython/jit/metainterp/pyjitpl.py +++ b/rpython/jit/metainterp/pyjitpl.py @@ -827,6 +827,21 @@ self._remove_symbolics(scalebox), self._remove_symbolics(baseofsbox), bytesbox) + @arguments("box", "box", "box", "box", "box", "box", "descr") + def _opimpl_gc_store_indexed(self, addrbox, indexbox, valuebox, + scalebox, baseofsbox, bytesbox, + arraydescr): + return self.execute_with_descr(rop.GC_STORE_INDEXED, + arraydescr, + addrbox, + indexbox, + valuebox, + self._remove_symbolics(scalebox), + self._remove_symbolics(baseofsbox), + bytesbox) + opimpl_gc_store_indexed_i = _opimpl_gc_store_indexed + opimpl_gc_store_indexed_f = _opimpl_gc_store_indexed + @arguments("box") def opimpl_hint_force_virtualizable(self, box): self.metainterp.gen_store_back_in_vable(box) @@ -2024,8 +2039,6 @@ self.aborted_tracing_greenkey = None def retrace_needed(self, trace, exported_state): - if not we_are_translated(): - exported_state._check_no_forwarding() self.partial_trace = trace self.retracing_from = self.potential_retrace_position self.exported_state = exported_state diff --git a/rpython/jit/metainterp/resoperation.py b/rpython/jit/metainterp/resoperation.py --- a/rpython/jit/metainterp/resoperation.py +++ b/rpython/jit/metainterp/resoperation.py @@ -52,10 +52,6 @@ llop.debug_print(lltype.Void, "setting forwarded on:", self.__class__.__name__) raise SettingForwardedOnAbstractValue() - def clear_forwarded(self): - if self.get_forwarded() is not None: - self.set_forwarded(None) - @specialize.arg(1) def get_box_replacement(op, not_const=False): # Read the chain "op, op._forwarded, op._forwarded._forwarded..." diff --git a/rpython/jit/metainterp/test/support.py b/rpython/jit/metainterp/test/support.py --- a/rpython/jit/metainterp/test/support.py +++ b/rpython/jit/metainterp/test/support.py @@ -19,7 +19,8 @@ supports_floats=True, supports_longlong=False, supports_singlefloats=False, - translationoptions={}, **kwds): + translationoptions={}, + backendopt_inline_threshold=0, **kwds): from rpython.jit.codewriter import support class FakeJitCell(object): @@ -59,7 +60,7 @@ FakeWarmRunnerState.enable_opts = {} func._jit_unroll_safe_ = True - rtyper = support.annotate(func, values, + rtyper = support.annotate(func, values, inline=backendopt_inline_threshold, translationoptions=translationoptions) graphs = rtyper.annotator.translator.graphs testself.all_graphs = graphs diff --git a/rpython/jit/metainterp/test/test_llop.py b/rpython/jit/metainterp/test/test_llop.py new file mode 100644 --- /dev/null +++ b/rpython/jit/metainterp/test/test_llop.py @@ -0,0 +1,72 @@ +import py +import sys +import struct +from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.rtyper.test.test_llop import (BaseLLOpTest, str_gc_load, + newlist_and_gc_store) +from rpython.jit.codewriter import longlong +from rpython.jit.metainterp.history import getkind +from rpython.jit.metainterp.test.support import LLJitMixin + + +class TestLLOp(BaseLLOpTest, LLJitMixin): + + # for the individual tests see + # ====> ../../../rtyper/test/test_llop.py + TEST_BLACKHOLE = True + + def gc_load_from_string(self, TYPE, buf, offset): + def f(offset): + return str_gc_load(TYPE, buf, offset) + res = self.interp_operations(f, [offset], supports_singlefloats=True) + # + kind = getkind(TYPE)[0] # 'i' or 'f' + self.check_operations_history({'gc_load_indexed_%s' % kind: 1, + 'finish': 1}) + # + if TYPE == lltype.SingleFloat: + # interp_operations returns the int version of r_singlefloat, but + # our tests expects to receive an r_singlefloat: let's convert it + # back! + return longlong.int2singlefloat(res) + return res + + def newlist_and_gc_store(self, TYPE, value, expected): + def f(value): + lst = newlist_and_gc_store(TYPE, value) + got = ''.join(lst) + if got != expected: + # I'm not sure why, but if I use an assert, the test doesn't fail + raise ValueError('got != expected') + return len(got) + # + if self.TEST_BLACKHOLE: + # we pass a big inline_threshold to ensure that + # newlist_and_gc_store is inlined, else the blackhole does not see + # (and thus we do not test!) the llop.gc_store_indexed + threshold = 33 + else: + threshold = 0 + return self.interp_operations(f, [value], supports_singlefloats=True, + backendopt_inline_threshold=threshold) + + + def test_force_virtual_str_storage(self): + byteorder = sys.byteorder + size = rffi.sizeof(lltype.Signed) + def f(val): + if byteorder == 'little': + x = chr(val) + '\x00'*(size-1) + else: + x = '\x00'*(size-1) + chr(val) + return str_gc_load(lltype.Signed, x, 0) + res = self.interp_operations(f, [42], supports_singlefloats=True) + assert res == 42 + self.check_operations_history({ + 'newstr': 1, # str forcing + 'strsetitem': 1, # str forcing + 'call_pure_r': 1, # str forcing (copystrcontent) + 'guard_no_exception': 1, # str forcing + 'gc_load_indexed_i': 1, # str_storage_getitem + 'finish': 1 + }) diff --git a/rpython/jit/metainterp/test/test_strstorage.py b/rpython/jit/metainterp/test/test_strstorage.py deleted file mode 100644 --- a/rpython/jit/metainterp/test/test_strstorage.py +++ /dev/null @@ -1,53 +0,0 @@ -import py -import sys -import struct -from rpython.rtyper.lltypesystem import lltype, rffi -from rpython.rlib.strstorage import str_storage_getitem -from rpython.rlib.test.test_strstorage import BaseStrStorageTest -from rpython.jit.codewriter import longlong -from rpython.jit.metainterp.history import getkind -from rpython.jit.metainterp.test.support import LLJitMixin - -class TestStrStorage(BaseStrStorageTest, LLJitMixin): - - # for the individual tests see - # ====> ../../../rlib/test/test_strstorage.py - - def str_storage_getitem(self, TYPE, buf, offset): - def f(): - return str_storage_getitem(TYPE, buf, offset) - res = self.interp_operations(f, [], supports_singlefloats=True) - # - kind = getkind(TYPE)[0] # 'i' or 'f' - self.check_operations_history({'gc_load_indexed_%s' % kind: 1, - 'finish': 1}) - # - if TYPE == lltype.SingleFloat: - # interp_operations returns the int version of r_singlefloat, but - # our tests expects to receive an r_singlefloat: let's convert it - # back! - return longlong.int2singlefloat(res) - return res - - #def str_storage_supported(self, TYPE): - # py.test.skip('this is not a JIT test') - - def test_force_virtual_str_storage(self): - byteorder = sys.byteorder - size = rffi.sizeof(lltype.Signed) - def f(val): - if byteorder == 'little': - x = chr(val) + '\x00'*(size-1) - else: - x = '\x00'*(size-1) + chr(val) - return str_storage_getitem(lltype.Signed, x, 0) - res = self.interp_operations(f, [42], supports_singlefloats=True) - assert res == 42 - self.check_operations_history({ - 'newstr': 1, # str forcing - 'strsetitem': 1, # str forcing - 'call_pure_r': 1, # str forcing (copystrcontent) - 'guard_no_exception': 1, # str forcing - 'gc_load_indexed_i': 1, # str_storage_getitem - 'finish': 1 - }) diff --git a/rpython/rlib/buffer.py b/rpython/rlib/buffer.py --- a/rpython/rlib/buffer.py +++ b/rpython/rlib/buffer.py @@ -1,13 +1,55 @@ """ Buffer protocol support. """ -from rpython.rlib.rgc import ( - nonmoving_raw_ptr_for_resizable_list, resizable_list_supporting_raw_ptr) +from rpython.rtyper.lltypesystem import lltype, llmemory +from rpython.rtyper.lltypesystem.lloperation import llop +from rpython.rtyper.lltypesystem.rstr import STR +from rpython.rtyper.lltypesystem.rlist import LIST_OF +from rpython.rtyper.annlowlevel import llstr +from rpython.rlib.objectmodel import specialize +from rpython.rlib import jit +from rpython.rlib.rgc import (resizable_list_supporting_raw_ptr, + nonmoving_raw_ptr_for_resizable_list, + ll_for_resizable_list) from rpython.rlib.signature import signature from rpython.rlib import types +from rpython.rlib import rawstorage + +ALLOW_UNALIGNED_ACCESS = rawstorage.misaligned_is_fine + +@specialize.ll() +def is_alignment_correct(TYPE, index): + if ALLOW_UNALIGNED_ACCESS: + return True + try: + rawstorage._check_alignment(TYPE, index) + except rawstorage.AlignmentError: + return False + else: + return True + + +class CannotRead(Exception): + """ + Exception raised by Buffer.typed_read in case it is not possible to + accomplish the request. This might be because it is not supported by the + specific type of buffer, or because of alignment issues. + """ + +class CannotWrite(Exception): + """ + Raised by Buffer.typed_write in case it is not possible to accomplish the + request + """ class Buffer(object): - """Base class for buffers of bytes""" + """ + Base class for buffers of bytes. + + Most probably, you do NOT want to use this as a lone base class, but + either inherit from RawBuffer or GCBuffer, so that you automatically get + the proper implementation of typed_read and typed_write. + """ _attrs_ = ['readonly'] _immutable_ = True @@ -25,14 +67,6 @@ # 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. @@ -60,7 +94,141 @@ for i in range(len(string)): self.setitem(start + i, string[i]) -class ByteBuffer(Buffer): + @jit.look_inside_iff(lambda self, index, count: + jit.isconstant(count) and count <= 8) + def setzeros(self, index, count): + for i in range(index, index+count): _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit