From noreply at buildbot.pypy.org Mon Aug 1 13:32:40 2011 From: noreply at buildbot.pypy.org (fijal) Date: Mon, 1 Aug 2011 13:32:40 +0200 (CEST) Subject: [pypy-commit] pypy default: rework naming of loops/bridges. jitviewer needs updating Message-ID: <20110801113240.5F27B822AC@wyvern.cs.uni-duesseldorf.de> Author: Maciej Fijalkowski Branch: Changeset: r46137:d41794f10532 Date: 2011-08-01 13:31 +0200 http://bitbucket.org/pypy/pypy/changeset/d41794f10532/ Log: rework naming of loops/bridges. jitviewer needs updating diff --git a/pypy/jit/backend/x86/assembler.py b/pypy/jit/backend/x86/assembler.py --- a/pypy/jit/backend/x86/assembler.py +++ b/pypy/jit/backend/x86/assembler.py @@ -56,7 +56,9 @@ self.exc = exc self.is_guard_not_invalidated = is_guard_not_invalidated -DEBUG_COUNTER = lltype.Struct('DEBUG_COUNTER', ('i', lltype.Signed)) +DEBUG_COUNTER = lltype.Struct('DEBUG_COUNTER', ('i', lltype.Signed), + ('bridge', lltype.Signed), # 0 or 1 + ('number', lltype.Signed)) class Assembler386(object): _regalloc = None @@ -155,9 +157,12 @@ def finish_once(self): if self._debug: debug_start('jit-backend-counts') - for i in range(len(self.loop_run_counters)): - struct = self.loop_run_counters[i] - debug_print(str(i) + ':' + str(struct.i)) + for struct in self.loop_run_counters: + if struct.bridge: + prefix = 'bridge ' + else: + prefix = 'loop ' + debug_print(prefix + str(struct.number) + ':' + str(struct.i)) debug_stop('jit-backend-counts') def _build_float_constants(self): @@ -404,7 +409,7 @@ self.setup(looptoken) self.currently_compiling_loop = looptoken if log: - self._register_counter() + self._register_counter(False, looptoken.number) operations = self._inject_debugging_code(looptoken, operations) regalloc = RegAlloc(self, self.cpu.translate_support_code) @@ -473,7 +478,7 @@ self.setup(original_loop_token) if log: - self._register_counter() + self._register_counter(True, descr_number) operations = self._inject_debugging_code(faildescr, operations) arglocs = self.rebuild_faillocs_from_descr(failure_recovery) @@ -570,7 +575,7 @@ return self.mc.materialize(self.cpu.asmmemmgr, allblocks, self.cpu.gc_ll_descr.gcrootmap) - def _register_counter(self): + def _register_counter(self, bridge, number): if self._debug: # YYY very minor leak -- we need the counters to stay alive # forever, just because we want to report them at the end @@ -578,6 +583,8 @@ struct = lltype.malloc(DEBUG_COUNTER, flavor='raw', track_allocation=False) struct.i = 0 + struct.bridge = int(bridge) + struct.number = number self.loop_run_counters.append(struct) def _find_failure_recovery_bytecode(self, faildescr): From noreply at buildbot.pypy.org Mon Aug 1 13:32:39 2011 From: noreply at buildbot.pypy.org (fijal) Date: Mon, 1 Aug 2011 13:32:39 +0200 (CEST) Subject: [pypy-commit] pypy inline-dict-ops: missing import Message-ID: <20110801113239.1174F822AB@wyvern.cs.uni-duesseldorf.de> Author: Maciej Fijalkowski Branch: inline-dict-ops Changeset: r46136:902125543062 Date: 2011-07-31 23:28 +0200 http://bitbucket.org/pypy/pypy/changeset/902125543062/ Log: missing import diff --git a/pypy/rpython/lltypesystem/rdict.py b/pypy/rpython/lltypesystem/rdict.py --- a/pypy/rpython/lltypesystem/rdict.py +++ b/pypy/rpython/lltypesystem/rdict.py @@ -6,6 +6,7 @@ from pypy.rlib.rarithmetic import r_uint, intmask, LONG_BIT from pypy.rlib.objectmodel import hlinvoke from pypy.rlib import objectmodel +from pypy.rlib import jit from pypy.rpython import rmodel from pypy.rpython.error import TyperError From noreply at buildbot.pypy.org Mon Aug 1 13:32:41 2011 From: noreply at buildbot.pypy.org (fijal) Date: Mon, 1 Aug 2011 13:32:41 +0200 (CEST) Subject: [pypy-commit] pypy default: merge Message-ID: <20110801113241.9725C822AB@wyvern.cs.uni-duesseldorf.de> Author: Maciej Fijalkowski Branch: Changeset: r46138:650824ea845a Date: 2011-08-01 13:32 +0200 http://bitbucket.org/pypy/pypy/changeset/650824ea845a/ Log: merge 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 @@ -239,7 +239,7 @@ new_buffer = lltype.nullptr(mytype.arraytype) if self.buffer: - lltype.free(self.buffer, flavor='raw') + lltype.free(self.buffer, flavor='raw', track_allocation=False) self.buffer = new_buffer self.len = size diff --git a/pypy/rpython/memory/gc/test/test_minimark.py b/pypy/rpython/memory/gc/test/test_minimark.py --- a/pypy/rpython/memory/gc/test/test_minimark.py +++ b/pypy/rpython/memory/gc/test/test_minimark.py @@ -34,6 +34,7 @@ growth_rate_max=1.5) gc.min_heap_size = 100.0 gc.max_heap_size = 300.0 + gc.next_major_collection_initial = 0.0 gc.next_major_collection_threshold = 0.0 # first, we don't grow past min_heap_size for i in range(5): From noreply at buildbot.pypy.org Mon Aug 1 14:24:45 2011 From: noreply at buildbot.pypy.org (arigo) Date: Mon, 1 Aug 2011 14:24:45 +0200 (CEST) Subject: [pypy-commit] pypy jit-singlefloat: Close branch about to be merged. Message-ID: <20110801122445.47479822AB@wyvern.cs.uni-duesseldorf.de> Author: Armin Rigo Branch: jit-singlefloat Changeset: r46139:d1844205ae46 Date: 2011-08-01 14:21 +0200 http://bitbucket.org/pypy/pypy/changeset/d1844205ae46/ Log: Close branch about to be merged. From noreply at buildbot.pypy.org Mon Aug 1 14:24:47 2011 From: noreply at buildbot.pypy.org (arigo) Date: Mon, 1 Aug 2011 14:24:47 +0200 (CEST) Subject: [pypy-commit] pypy default: hg merge jit-singlefloat: Message-ID: <20110801122447.4E9B3822AB@wyvern.cs.uni-duesseldorf.de> Author: Armin Rigo Branch: Changeset: r46140:21a24e41f441 Date: 2011-08-01 14:25 +0200 http://bitbucket.org/pypy/pypy/changeset/21a24e41f441/ Log: hg merge jit-singlefloat: - add optional singlefloats support in the JIT front-end (passed around as BoxInt) - add support in the x86 backend (a singlefloat is stored in 32 bits in a regular cpu register, not in an xmm register at all) - wrap() and specialize_value() now accept almost any type - simplify rlib/libffi.py to not use the casting between longlongs and floats any more, and instead write it in a style that will correctly hide or show parts depending on whether the current jit backend supports floats, singlefloats, and longlongs. diff --git a/pypy/jit/backend/llgraph/llimpl.py b/pypy/jit/backend/llgraph/llimpl.py --- a/pypy/jit/backend/llgraph/llimpl.py +++ b/pypy/jit/backend/llgraph/llimpl.py @@ -1071,6 +1071,8 @@ return heaptracker.adr2int(llmemory.cast_ptr_to_adr(x)) if TP == llmemory.Address: return heaptracker.adr2int(x) + if TP is lltype.SingleFloat: + return longlong.singlefloat2int(x) return lltype.cast_primitive(lltype.Signed, x) def cast_from_int(TYPE, x): @@ -1086,6 +1088,9 @@ x = llmemory.cast_int_to_adr(x) assert lltype.typeOf(x) == llmemory.Address return x + elif TYPE is lltype.SingleFloat: + assert lltype.typeOf(x) is lltype.Signed + return longlong.int2singlefloat(x) else: if lltype.typeOf(x) == llmemory.Address: x = heaptracker.adr2int(x) @@ -1140,6 +1145,7 @@ del _future_values[:] def set_future_value_int(index, value): + assert lltype.typeOf(value) is lltype.Signed set_future_value_ref(index, value) def set_future_value_float(index, value): @@ -1488,6 +1494,7 @@ 'i': lltype.Signed, 'f': lltype.Float, 'L': lltype.SignedLongLong, + 'S': lltype.SingleFloat, 'v': lltype.Void, } diff --git a/pypy/jit/backend/llgraph/runner.py b/pypy/jit/backend/llgraph/runner.py --- a/pypy/jit/backend/llgraph/runner.py +++ b/pypy/jit/backend/llgraph/runner.py @@ -91,6 +91,7 @@ class BaseCPU(model.AbstractCPU): supports_floats = True supports_longlong = llimpl.IS_32_BIT + supports_singlefloats = True def __init__(self, rtyper, stats=None, opts=None, translate_support_code=False, @@ -327,12 +328,16 @@ def calldescrof_dynamic(self, ffi_args, ffi_result, extrainfo=None): from pypy.jit.backend.llsupport.ffisupport import get_ffi_type_kind + from pypy.jit.backend.llsupport.ffisupport import UnsupportedKind arg_types = [] - for arg in ffi_args: - kind = get_ffi_type_kind(arg) - if kind != history.VOID: - arg_types.append(kind) - reskind = get_ffi_type_kind(ffi_result) + try: + for arg in ffi_args: + kind = get_ffi_type_kind(self, arg) + if kind != history.VOID: + arg_types.append(kind) + reskind = get_ffi_type_kind(self, ffi_result) + except UnsupportedKind: + return None return self.getdescr(0, reskind, extrainfo=extrainfo, arg_types=''.join(arg_types)) diff --git a/pypy/jit/backend/llsupport/descr.py b/pypy/jit/backend/llsupport/descr.py --- a/pypy/jit/backend/llsupport/descr.py +++ b/pypy/jit/backend/llsupport/descr.py @@ -303,6 +303,8 @@ c = 'f' elif c == 'f' and longlong.supports_longlong: return 'longlong.getrealfloat(%s)' % (process('L'),) + elif c == 'S': + return 'longlong.int2singlefloat(%s)' % (process('i'),) arg = 'args_%s[%d]' % (c, seen[c]) seen[c] += 1 return arg @@ -318,6 +320,8 @@ return lltype.Void elif arg == 'L': return lltype.SignedLongLong + elif arg == 'S': + return lltype.SingleFloat else: raise AssertionError(arg) @@ -334,6 +338,8 @@ result = 'rffi.cast(lltype.SignedLongLong, res)' elif self.get_return_type() == history.VOID: result = 'None' + elif self.get_return_type() == 'S': + result = 'longlong.singlefloat2int(res)' else: assert 0 source = py.code.Source(""" @@ -344,14 +350,15 @@ """ % locals()) ARGS = [TYPE(arg) for arg in self.arg_classes] FUNC = lltype.FuncType(ARGS, RESULT) - d = locals().copy() - d.update(globals()) + d = globals().copy() + d.update(locals()) exec source.compile() in d self.call_stub = d['call_stub'] def verify_types(self, args_i, args_r, args_f, return_type): assert self._return_type in return_type - assert self.arg_classes.count('i') == len(args_i or ()) + assert (self.arg_classes.count('i') + + self.arg_classes.count('S')) == len(args_i or ()) assert self.arg_classes.count('r') == len(args_r or ()) assert (self.arg_classes.count('f') + self.arg_classes.count('L')) == len(args_f or ()) @@ -428,23 +435,39 @@ def get_result_size(self, translate_support_code): return 0 +_SingleFloatCallDescr = None # built lazily + def getCallDescrClass(RESULT): if RESULT is lltype.Void: return VoidCallDescr if RESULT is lltype.Float: return FloatCallDescr + if RESULT is lltype.SingleFloat: + global _SingleFloatCallDescr + if _SingleFloatCallDescr is None: + assert rffi.sizeof(rffi.UINT) == rffi.sizeof(RESULT) + class SingleFloatCallDescr(getCallDescrClass(rffi.UINT)): + _clsname = 'SingleFloatCallDescr' + _return_type = 'S' + _SingleFloatCallDescr = SingleFloatCallDescr + return _SingleFloatCallDescr if is_longlong(RESULT): return LongLongCallDescr return getDescrClass(RESULT, BaseIntCallDescr, GcPtrCallDescr, NonGcPtrCallDescr, 'Call', 'get_result_size', Ellipsis, # <= floatattrname should not be used here '_is_result_signed') +getCallDescrClass._annspecialcase_ = 'specialize:memo' def get_call_descr(gccache, ARGS, RESULT, extrainfo=None): arg_classes = [] for ARG in ARGS: kind = getkind(ARG) - if kind == 'int': arg_classes.append('i') + if kind == 'int': + if ARG is lltype.SingleFloat: + arg_classes.append('S') + else: + arg_classes.append('i') elif kind == 'ref': arg_classes.append('r') elif kind == 'float': if is_longlong(ARG): @@ -476,6 +499,9 @@ return GcPtrDescr else: return NonGcPtrDescr + if TYPE is lltype.SingleFloat: + assert rffi.sizeof(rffi.UINT) == rffi.sizeof(TYPE) + TYPE = rffi.UINT try: return _cache[nameprefix, TYPE] except KeyError: diff --git a/pypy/jit/backend/llsupport/ffisupport.py b/pypy/jit/backend/llsupport/ffisupport.py --- a/pypy/jit/backend/llsupport/ffisupport.py +++ b/pypy/jit/backend/llsupport/ffisupport.py @@ -1,19 +1,21 @@ from pypy.rlib.rarithmetic import intmask from pypy.jit.metainterp import history -from pypy.jit.backend.llsupport.descr import DynamicIntCallDescr, NonGcPtrCallDescr,\ - FloatCallDescr, VoidCallDescr +from pypy.rpython.lltypesystem import rffi +from pypy.jit.backend.llsupport.descr import ( + DynamicIntCallDescr, NonGcPtrCallDescr, FloatCallDescr, VoidCallDescr, + LongLongCallDescr, getCallDescrClass) class UnsupportedKind(Exception): pass -def get_call_descr_dynamic(ffi_args, ffi_result, extrainfo=None): +def get_call_descr_dynamic(cpu, ffi_args, ffi_result, extrainfo=None): """Get a call descr: the types of result and args are represented by rlib.libffi.types.*""" try: - reskind = get_ffi_type_kind(ffi_result) - argkinds = [get_ffi_type_kind(arg) for arg in ffi_args] + reskind = get_ffi_type_kind(cpu, ffi_result) + argkinds = [get_ffi_type_kind(cpu, arg) for arg in ffi_args] except UnsupportedKind: - return None # ?? + return None arg_classes = ''.join(argkinds) if reskind == history.INT: size = intmask(ffi_result.c_size) @@ -25,17 +27,26 @@ return FloatCallDescr(arg_classes, extrainfo) elif reskind == history.VOID: return VoidCallDescr(arg_classes, extrainfo) + elif reskind == 'L': + return LongLongCallDescr(arg_classes, extrainfo) + elif reskind == 'S': + SingleFloatCallDescr = getCallDescrClass(rffi.FLOAT) + return SingleFloatCallDescr(arg_classes, extrainfo) assert False -def get_ffi_type_kind(ffi_type): +def get_ffi_type_kind(cpu, ffi_type): from pypy.rlib.libffi import types kind = types.getkind(ffi_type) if kind == 'i' or kind == 'u': return history.INT - elif kind == 'f': + elif cpu.supports_floats and kind == 'f': return history.FLOAT elif kind == 'v': return history.VOID + elif cpu.supports_longlong and (kind == 'I' or kind == 'U'): # longlong + return 'L' + elif cpu.supports_singlefloats and kind == 's': # singlefloat + return 'S' raise UnsupportedKind("Unsupported kind '%s'" % kind) def is_ffi_type_signed(ffi_type): diff --git a/pypy/jit/backend/llsupport/llmodel.py b/pypy/jit/backend/llsupport/llmodel.py --- a/pypy/jit/backend/llsupport/llmodel.py +++ b/pypy/jit/backend/llsupport/llmodel.py @@ -259,7 +259,7 @@ def calldescrof_dynamic(self, ffi_args, ffi_result, extrainfo=None): from pypy.jit.backend.llsupport import ffisupport - return ffisupport.get_call_descr_dynamic(ffi_args, ffi_result, + return ffisupport.get_call_descr_dynamic(self, ffi_args, ffi_result, extrainfo) def get_overflow_error(self): @@ -499,7 +499,7 @@ def bh_call_i(self, func, calldescr, args_i, args_r, args_f): assert isinstance(calldescr, BaseIntCallDescr) if not we_are_translated(): - calldescr.verify_types(args_i, args_r, args_f, history.INT) + calldescr.verify_types(args_i, args_r, args_f, history.INT + 'S') return calldescr.call_stub(func, args_i, args_r, args_f) def bh_call_r(self, func, calldescr, args_i, args_r, args_f): diff --git a/pypy/jit/backend/llsupport/test/test_descr.py b/pypy/jit/backend/llsupport/test/test_descr.py --- a/pypy/jit/backend/llsupport/test/test_descr.py +++ b/pypy/jit/backend/llsupport/test/test_descr.py @@ -52,7 +52,8 @@ S = lltype.GcStruct('S', ('x', lltype.Char), ('y', lltype.Ptr(T)), ('z', lltype.Ptr(U)), - ('f', lltype.Float)) + ('f', lltype.Float), + ('s', lltype.SingleFloat)) assert getFieldDescrClass(lltype.Ptr(T)) is GcPtrFieldDescr assert getFieldDescrClass(lltype.Ptr(U)) is NonGcPtrFieldDescr cls = getFieldDescrClass(lltype.Char) @@ -61,6 +62,10 @@ clsf = getFieldDescrClass(lltype.Float) assert clsf != cls assert clsf == getFieldDescrClass(lltype.Float) + clss = getFieldDescrClass(lltype.SingleFloat) + assert clss not in (cls, clsf) + assert clss == getFieldDescrClass(lltype.SingleFloat) + assert clss == getFieldDescrClass(rffi.UINT) # for now # c0 = GcCache(False) c1 = GcCache(True) @@ -72,14 +77,17 @@ descr_y = get_field_descr(c2, S, 'y') descr_z = get_field_descr(c2, S, 'z') descr_f = get_field_descr(c2, S, 'f') + descr_s = get_field_descr(c2, S, 's') assert descr_x.__class__ is cls assert descr_y.__class__ is GcPtrFieldDescr assert descr_z.__class__ is NonGcPtrFieldDescr assert descr_f.__class__ is clsf + assert descr_s.__class__ is clss assert descr_x.name == 'S.x' assert descr_y.name == 'S.y' assert descr_z.name == 'S.z' assert descr_f.name == 'S.f' + assert descr_s.name == 'S.s' if not tsc: assert descr_x.offset < descr_y.offset < descr_z.offset assert descr_x.sort_key() < descr_y.sort_key() < descr_z.sort_key() @@ -87,23 +95,29 @@ assert descr_y.get_field_size(False) == rffi.sizeof(lltype.Ptr(T)) assert descr_z.get_field_size(False) == rffi.sizeof(lltype.Ptr(U)) assert descr_f.get_field_size(False) == rffi.sizeof(lltype.Float) + assert descr_s.get_field_size(False) == rffi.sizeof( + lltype.SingleFloat) else: assert isinstance(descr_x.offset, Symbolic) assert isinstance(descr_y.offset, Symbolic) assert isinstance(descr_z.offset, Symbolic) assert isinstance(descr_f.offset, Symbolic) + assert isinstance(descr_s.offset, Symbolic) assert isinstance(descr_x.get_field_size(True), Symbolic) assert isinstance(descr_y.get_field_size(True), Symbolic) assert isinstance(descr_z.get_field_size(True), Symbolic) assert isinstance(descr_f.get_field_size(True), Symbolic) + assert isinstance(descr_s.get_field_size(True), Symbolic) assert not descr_x.is_pointer_field() assert descr_y.is_pointer_field() assert not descr_z.is_pointer_field() assert not descr_f.is_pointer_field() + assert not descr_s.is_pointer_field() assert not descr_x.is_float_field() assert not descr_y.is_float_field() assert not descr_z.is_float_field() assert descr_f.is_float_field() + assert not descr_s.is_float_field() def test_get_field_descr_sign(): @@ -135,6 +149,7 @@ A2 = lltype.GcArray(lltype.Ptr(T)) A3 = lltype.GcArray(lltype.Ptr(U)) A4 = lltype.GcArray(lltype.Float) + A5 = lltype.GcArray(lltype.SingleFloat) assert getArrayDescrClass(A2) is GcPtrArrayDescr assert getArrayDescrClass(A3) is NonGcPtrArrayDescr cls = getArrayDescrClass(A1) @@ -143,25 +158,32 @@ clsf = getArrayDescrClass(A4) assert clsf != cls assert clsf == getArrayDescrClass(lltype.GcArray(lltype.Float)) + clss = getArrayDescrClass(A5) + assert clss not in (clsf, cls) + assert clss == getArrayDescrClass(lltype.GcArray(rffi.UINT)) # c0 = GcCache(False) descr1 = get_array_descr(c0, A1) descr2 = get_array_descr(c0, A2) descr3 = get_array_descr(c0, A3) descr4 = get_array_descr(c0, A4) + descr5 = get_array_descr(c0, A5) assert descr1.__class__ is cls assert descr2.__class__ is GcPtrArrayDescr assert descr3.__class__ is NonGcPtrArrayDescr assert descr4.__class__ is clsf + assert descr5.__class__ is clss assert descr1 == get_array_descr(c0, lltype.GcArray(lltype.Char)) assert not descr1.is_array_of_pointers() assert descr2.is_array_of_pointers() assert not descr3.is_array_of_pointers() assert not descr4.is_array_of_pointers() + assert not descr5.is_array_of_pointers() assert not descr1.is_array_of_floats() assert not descr2.is_array_of_floats() assert not descr3.is_array_of_floats() assert descr4.is_array_of_floats() + assert not descr5.is_array_of_floats() # def get_alignment(code): # Retrieve default alignment for the compiler/platform @@ -170,27 +192,33 @@ assert descr2.get_base_size(False) == get_alignment('p') assert descr3.get_base_size(False) == get_alignment('p') assert descr4.get_base_size(False) == get_alignment('d') + assert descr5.get_base_size(False) == get_alignment('f') assert descr1.get_ofs_length(False) == 0 assert descr2.get_ofs_length(False) == 0 assert descr3.get_ofs_length(False) == 0 assert descr4.get_ofs_length(False) == 0 + assert descr5.get_ofs_length(False) == 0 assert descr1.get_item_size(False) == rffi.sizeof(lltype.Char) assert descr2.get_item_size(False) == rffi.sizeof(lltype.Ptr(T)) assert descr3.get_item_size(False) == rffi.sizeof(lltype.Ptr(U)) assert descr4.get_item_size(False) == rffi.sizeof(lltype.Float) + assert descr5.get_item_size(False) == rffi.sizeof(lltype.SingleFloat) # assert isinstance(descr1.get_base_size(True), Symbolic) assert isinstance(descr2.get_base_size(True), Symbolic) assert isinstance(descr3.get_base_size(True), Symbolic) assert isinstance(descr4.get_base_size(True), Symbolic) + assert isinstance(descr5.get_base_size(True), Symbolic) assert isinstance(descr1.get_ofs_length(True), Symbolic) assert isinstance(descr2.get_ofs_length(True), Symbolic) assert isinstance(descr3.get_ofs_length(True), Symbolic) assert isinstance(descr4.get_ofs_length(True), Symbolic) + assert isinstance(descr5.get_ofs_length(True), Symbolic) assert isinstance(descr1.get_item_size(True), Symbolic) assert isinstance(descr2.get_item_size(True), Symbolic) assert isinstance(descr3.get_item_size(True), Symbolic) assert isinstance(descr4.get_item_size(True), Symbolic) + assert isinstance(descr5.get_item_size(True), Symbolic) CA = rffi.CArray(lltype.Signed) descr = get_array_descr(c0, CA) assert not descr.is_array_of_floats() @@ -210,6 +238,11 @@ assert descr.is_array_of_floats() assert descr.get_base_size(False) == 0 assert descr.get_ofs_length(False) == -1 + CA = rffi.CArray(rffi.FLOAT) + descr = get_array_descr(c0, CA) + assert not descr.is_array_of_floats() + assert descr.get_base_size(False) == 0 + assert descr.get_ofs_length(False) == -1 def test_get_array_descr_sign(): @@ -257,6 +290,11 @@ assert descr4.get_result_size(False) == rffi.sizeof(lltype.Float) assert descr4.get_return_type() == history.FLOAT assert descr4.arg_classes == "ff" + # + descr5 = get_call_descr(c0, [lltype.SingleFloat], lltype.SingleFloat) + assert descr5.get_result_size(False) == rffi.sizeof(lltype.SingleFloat) + assert descr5.get_return_type() == "S" + assert descr5.arg_classes == "S" def test_get_call_descr_not_translated_longlong(): if sys.maxint > 2147483647: @@ -286,6 +324,11 @@ assert isinstance(descr4.get_result_size(True), Symbolic) assert descr4.get_return_type() == history.FLOAT assert descr4.arg_classes == "ff" + # + descr5 = get_call_descr(c1, [lltype.SingleFloat], lltype.SingleFloat) + assert isinstance(descr5.get_result_size(True), Symbolic) + assert descr5.get_return_type() == "S" + assert descr5.arg_classes == "S" def test_call_descr_extra_info(): c1 = GcCache(True) @@ -345,8 +388,11 @@ # descr4f = get_call_descr(c0, [lltype.Char, lltype.Ptr(S)], lltype.Float) assert 'FloatCallDescr' in descr4f.repr_of_descr() + # + descr5f = get_call_descr(c0, [lltype.Char], lltype.SingleFloat) + assert 'SingleFloatCallDescr' in descr5f.repr_of_descr() -def test_call_stubs(): +def test_call_stubs_1(): c0 = GcCache(False) ARGS = [lltype.Char, lltype.Signed] RES = lltype.Char @@ -360,6 +406,8 @@ res = call_stub(rffi.cast(lltype.Signed, fnptr), [1, 2], None, None) assert res == ord('c') +def test_call_stubs_2(): + c0 = GcCache(False) ARRAY = lltype.GcArray(lltype.Signed) ARGS = [lltype.Float, lltype.Ptr(ARRAY)] RES = lltype.Float @@ -375,3 +423,27 @@ res = descr2.call_stub(rffi.cast(lltype.Signed, fnptr), [], [opaquea], [longlong.getfloatstorage(3.5)]) assert longlong.getrealfloat(res) == 4.5 + +def test_call_stubs_single_float(): + from pypy.rlib.longlong2float import uint2singlefloat, singlefloat2uint + from pypy.rlib.rarithmetic import r_singlefloat, intmask + # + c0 = GcCache(False) + ARGS = [lltype.SingleFloat, lltype.SingleFloat, lltype.SingleFloat] + RES = lltype.SingleFloat + + def f(a, b, c): + a = float(a) + b = float(b) + c = float(c) + x = a - (b / c) + return r_singlefloat(x) + + fnptr = llhelper(lltype.Ptr(lltype.FuncType(ARGS, RES)), f) + descr2 = get_call_descr(c0, ARGS, RES) + a = intmask(singlefloat2uint(r_singlefloat(-10.0))) + b = intmask(singlefloat2uint(r_singlefloat(3.0))) + c = intmask(singlefloat2uint(r_singlefloat(2.0))) + res = descr2.call_stub(rffi.cast(lltype.Signed, fnptr), + [a, b, c], [], []) + assert float(uint2singlefloat(rffi.r_uint(res))) == -11.5 diff --git a/pypy/jit/backend/llsupport/test/test_ffisupport.py b/pypy/jit/backend/llsupport/test/test_ffisupport.py --- a/pypy/jit/backend/llsupport/test/test_ffisupport.py +++ b/pypy/jit/backend/llsupport/test/test_ffisupport.py @@ -1,24 +1,48 @@ from pypy.rlib.libffi import types -from pypy.jit.backend.llsupport.ffisupport import get_call_descr_dynamic, \ - VoidCallDescr, DynamicIntCallDescr - +from pypy.jit.backend.llsupport.ffisupport import * + + +class FakeCPU: + def __init__(self, supports_floats=False, supports_longlong=False, + supports_singlefloats=False): + self.supports_floats = supports_floats + self.supports_longlong = supports_longlong + self.supports_singlefloats = supports_singlefloats + + def test_call_descr_dynamic(): + args = [types.sint, types.pointer] + descr = get_call_descr_dynamic(FakeCPU(), args, types.sint) + assert isinstance(descr, DynamicIntCallDescr) + assert descr.arg_classes == 'ii' args = [types.sint, types.double, types.pointer] - descr = get_call_descr_dynamic(args, types.void) + descr = get_call_descr_dynamic(FakeCPU(), args, types.void) + assert descr is None # missing floats + descr = get_call_descr_dynamic(FakeCPU(supports_floats=True), + args, types.void) assert isinstance(descr, VoidCallDescr) assert descr.arg_classes == 'ifi' - descr = get_call_descr_dynamic([], types.sint8) + descr = get_call_descr_dynamic(FakeCPU(), [], types.sint8) assert isinstance(descr, DynamicIntCallDescr) assert descr.get_result_size(False) == 1 assert descr.is_result_signed() == True - descr = get_call_descr_dynamic([], types.uint8) + descr = get_call_descr_dynamic(FakeCPU(), [], types.uint8) assert isinstance(descr, DynamicIntCallDescr) assert descr.get_result_size(False) == 1 assert descr.is_result_signed() == False - descr = get_call_descr_dynamic([], types.float) - assert descr is None # single floats are not supported so far - + descr = get_call_descr_dynamic(FakeCPU(), [], types.slonglong) + assert descr is None # missing longlongs + descr = get_call_descr_dynamic(FakeCPU(supports_longlong=True), + [], types.slonglong) + assert isinstance(descr, LongLongCallDescr) + + descr = get_call_descr_dynamic(FakeCPU(), [], types.float) + assert descr is None # missing singlefloats + descr = get_call_descr_dynamic(FakeCPU(supports_singlefloats=True), + [], types.float) + SingleFloatCallDescr = getCallDescrClass(rffi.FLOAT) + assert isinstance(descr, SingleFloatCallDescr) diff --git a/pypy/jit/backend/model.py b/pypy/jit/backend/model.py --- a/pypy/jit/backend/model.py +++ b/pypy/jit/backend/model.py @@ -8,6 +8,7 @@ # ^^^ This is only useful on 32-bit platforms. If True, # longlongs are supported by the JIT, but stored as doubles. # Boxes and Consts are BoxFloats and ConstFloats. + supports_singlefloats = False done_with_this_frame_void_v = -1 done_with_this_frame_int_v = -1 diff --git a/pypy/jit/backend/test/calling_convention_test.py b/pypy/jit/backend/test/calling_convention_test.py --- a/pypy/jit/backend/test/calling_convention_test.py +++ b/pypy/jit/backend/test/calling_convention_test.py @@ -290,3 +290,58 @@ assert abs(x - expected_result) < 0.0001 finally: del self.cpu.done_with_this_frame_float_v + + def test_call_with_singlefloats(self): + cpu = self.cpu + if not cpu.supports_floats or not cpu.supports_singlefloats: + py.test.skip('requires floats and singlefloats') + + import random + from pypy.rlib.libffi import types + from pypy.rlib.rarithmetic import r_singlefloat + + def func(*args): + res = 0.0 + for i, x in enumerate(args): + res += (i + 1.1) * float(x) + return res + + F = lltype.Float + S = lltype.SingleFloat + I = lltype.Signed + floats = [random.random() - 0.5 for i in range(8)] + singlefloats = [r_singlefloat(random.random() - 0.5) for i in range(8)] + ints = [random.randrange(-99, 99) for i in range(8)] + for repeat in range(100): + args = [] + argvalues = [] + argslist = [] + local_floats = list(floats) + local_singlefloats = list(singlefloats) + local_ints = list(ints) + for i in range(8): + case = random.randrange(0, 3) + if case == 0: + args.append(F) + arg = local_floats.pop() + argslist.append(boxfloat(arg)) + elif case == 1: + args.append(S) + arg = local_singlefloats.pop() + argslist.append(BoxInt(longlong.singlefloat2int(arg))) + else: + args.append(I) + arg = local_ints.pop() + argslist.append(BoxInt(arg)) + argvalues.append(arg) + FUNC = self.FuncType(args, F) + FPTR = self.Ptr(FUNC) + func_ptr = llhelper(FPTR, func) + calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT) + funcbox = self.get_funcbox(cpu, func_ptr) + + res = self.execute_operation(rop.CALL, + [funcbox] + argslist, + 'float', descr=calldescr) + expected = func(*argvalues) + assert abs(res.getfloat() - expected) < 0.0001 diff --git a/pypy/jit/backend/test/runner_test.py b/pypy/jit/backend/test/runner_test.py --- a/pypy/jit/backend/test/runner_test.py +++ b/pypy/jit/backend/test/runner_test.py @@ -2734,6 +2734,65 @@ 'float', descr=calldescr) assert res.getfloatstorage() == expected + def test_singlefloat_result_of_call_direct(self): + if not self.cpu.supports_singlefloats: + py.test.skip("singlefloat test") + from pypy.translator.tool.cbuild import ExternalCompilationInfo + from pypy.rlib.rarithmetic import r_singlefloat + eci = ExternalCompilationInfo( + separate_module_sources=[""" + float fn_test_result_of_call(float x) + { + return x / 2.0f; + } + """], + export_symbols=['fn_test_result_of_call']) + f = rffi.llexternal('fn_test_result_of_call', [lltype.SingleFloat], + lltype.SingleFloat, + compilation_info=eci, _nowrapper=True) + value = r_singlefloat(-42.5) + expected = r_singlefloat(-21.25) + assert f(value) == expected + # + FUNC = self.FuncType([lltype.SingleFloat], lltype.SingleFloat) + FPTR = self.Ptr(FUNC) + calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT) + ivalue = longlong.singlefloat2int(value) + iexpected = longlong.singlefloat2int(expected) + x = self.cpu.bh_call_i(self.get_funcbox(self.cpu, f).value, + calldescr, [ivalue], None, None) + assert x == iexpected + + def test_singlefloat_result_of_call_compiled(self): + if not self.cpu.supports_singlefloats: + py.test.skip("test of singlefloat result") + from pypy.translator.tool.cbuild import ExternalCompilationInfo + from pypy.rlib.rarithmetic import r_singlefloat + eci = ExternalCompilationInfo( + separate_module_sources=[""" + float fn_test_result_of_call(float x) + { + return x / 2.0f; + } + """], + export_symbols=['fn_test_result_of_call']) + f = rffi.llexternal('fn_test_result_of_call', [lltype.SingleFloat], + lltype.SingleFloat, + compilation_info=eci, _nowrapper=True) + value = r_singlefloat(-42.5) + expected = r_singlefloat(-21.25) + assert f(value) == expected + # + FUNC = self.FuncType([lltype.SingleFloat], lltype.SingleFloat) + FPTR = self.Ptr(FUNC) + calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT) + funcbox = self.get_funcbox(self.cpu, f) + ivalue = longlong.singlefloat2int(value) + iexpected = longlong.singlefloat2int(expected) + res = self.execute_operation(rop.CALL, [funcbox, BoxInt(ivalue)], + 'int', descr=calldescr) + assert res.value == iexpected + def test_free_loop_and_bridges(self): from pypy.jit.backend.llsupport.llmodel import AbstractLLCPU if not isinstance(self.cpu, AbstractLLCPU): diff --git a/pypy/jit/backend/x86/assembler.py b/pypy/jit/backend/x86/assembler.py --- a/pypy/jit/backend/x86/assembler.py +++ b/pypy/jit/backend/x86/assembler.py @@ -1075,9 +1075,10 @@ self.implement_guard(guard_token, checkfalsecond) return genop_cmp_guard_float - def _emit_call(self, force_index, x, arglocs, start=0, tmp=eax): + def _emit_call(self, force_index, x, arglocs, start=0, tmp=eax, + argtypes=None): if IS_X86_64: - return self._emit_call_64(force_index, x, arglocs, start) + return self._emit_call_64(force_index, x, arglocs, start, argtypes) p = 0 n = len(arglocs) @@ -1105,12 +1106,13 @@ self.mc.CALL(x) self.mark_gc_roots(force_index) - def _emit_call_64(self, force_index, x, arglocs, start): + def _emit_call_64(self, force_index, x, arglocs, start, argtypes): src_locs = [] dst_locs = [] xmm_src_locs = [] xmm_dst_locs = [] pass_on_stack = [] + singlefloats = None # In reverse order for use with pop() unused_gpr = [r9, r8, ecx, edx, esi, edi] @@ -1130,6 +1132,11 @@ xmm_dst_locs.append(unused_xmm.pop()) else: pass_on_stack.append(loc) + elif (argtypes is not None and argtypes[i-start] == 'S' and + len(unused_xmm) > 0): + # Singlefloat argument + if singlefloats is None: singlefloats = [] + singlefloats.append((loc, unused_xmm.pop())) else: if len(unused_gpr) > 0: src_locs.append(loc) @@ -1157,9 +1164,15 @@ else: self.mc.MOV_sr(i*WORD, loc.value) - # Handle register arguments + # Handle register arguments: first remap the xmm arguments + remap_frame_layout(self, xmm_src_locs, xmm_dst_locs, + X86_64_XMM_SCRATCH_REG) + # Load the singlefloat arguments from main regs or stack to xmm regs + if singlefloats is not None: + for src, dst in singlefloats: + self.mc.MOVD(dst, src) + # Finally remap the arguments in the main regs remap_frame_layout(self, src_locs, dst_locs, X86_64_SCRATCH_REG) - remap_frame_layout(self, xmm_src_locs, xmm_dst_locs, X86_64_XMM_SCRATCH_REG) self._regalloc.reserve_param(len(pass_on_stack)) self.mc.CALL(x) @@ -1274,6 +1287,20 @@ def genop_cast_int_to_float(self, op, arglocs, resloc): self.mc.CVTSI2SD(resloc, arglocs[0]) + def genop_cast_float_to_singlefloat(self, op, arglocs, resloc): + loc0, loctmp = arglocs + self.mc.CVTSD2SS(loctmp, loc0) + assert isinstance(resloc, RegLoc) + assert isinstance(loctmp, RegLoc) + self.mc.MOVD_rx(resloc.value, loctmp.value) + + def genop_cast_singlefloat_to_float(self, op, arglocs, resloc): + loc0, = arglocs + assert isinstance(resloc, RegLoc) + assert isinstance(loc0, RegLoc) + self.mc.MOVD_xr(resloc.value, loc0.value) + self.mc.CVTSS2SD_xx(resloc.value, resloc.value) + def genop_guard_int_is_true(self, op, guard_op, guard_token, arglocs, resloc): guard_opnum = guard_op.getopnum() self.mc.CMP(arglocs[0], imm0) @@ -2032,7 +2059,8 @@ else: tmp = eax - self._emit_call(force_index, x, arglocs, 3, tmp=tmp) + self._emit_call(force_index, x, arglocs, 3, tmp=tmp, + argtypes=op.getdescr().get_arg_types()) if IS_X86_32 and isinstance(resloc, StackLoc) and resloc.width == 8: # a float or a long long return @@ -2044,7 +2072,19 @@ # and this way is simpler also because the result loc # can just be always a stack location else: - self.mc.FSTP_b(resloc.value) # float return + self.mc.FSTPL_b(resloc.value) # float return + elif op.getdescr().get_return_type() == 'S': + # singlefloat return + assert resloc is eax + if IS_X86_32: + # must convert ST(0) to a 32-bit singlefloat and load it into EAX + # mess mess mess + self.mc.SUB_ri(esp.value, 4) + self.mc.FSTPS_s(0) + self.mc.POP_r(eax.value) + elif IS_X86_64: + # must copy from the lower 32 bits of XMM0 into eax + self.mc.MOVD_rx(eax.value, xmm0.value) elif size == WORD: assert resloc is eax or resloc is xmm0 # a full word elif size == 0: @@ -2202,7 +2242,7 @@ self._emit_call(fail_index, imm(asm_helper_adr), [eax, arglocs[1]], 0, tmp=ecx) if IS_X86_32 and isinstance(result_loc, StackLoc) and result_loc.type == FLOAT: - self.mc.FSTP_b(result_loc.value) + self.mc.FSTPL_b(result_loc.value) #else: result_loc is already either eax or None, checked below self.mc.JMP_l8(0) # jump to done, patched later jmp_location = self.mc.get_relative_pos() diff --git a/pypy/jit/backend/x86/regalloc.py b/pypy/jit/backend/x86/regalloc.py --- a/pypy/jit/backend/x86/regalloc.py +++ b/pypy/jit/backend/x86/regalloc.py @@ -705,6 +705,17 @@ self.Perform(op, [loc0], loc1) self.rm.possibly_free_var(op.getarg(0)) + def consider_cast_float_to_singlefloat(self, op): + loc0 = self.xrm.make_sure_var_in_reg(op.getarg(0)) + loc1 = self.rm.force_allocate_reg(op.result) + self.xrm.possibly_free_var(op.getarg(0)) + tmpxvar = TempBox() + loctmp = self.xrm.force_allocate_reg(tmpxvar) # may be equal to loc0 + self.xrm.possibly_free_var(tmpxvar) + self.Perform(op, [loc0, loctmp], loc1) + + consider_cast_singlefloat_to_float = consider_cast_int_to_float + def _consider_llong_binop_xx(self, op): # must force both arguments into xmm registers, because we don't # know if they will be suitably aligned. Exception: if the second diff --git a/pypy/jit/backend/x86/regloc.py b/pypy/jit/backend/x86/regloc.py --- a/pypy/jit/backend/x86/regloc.py +++ b/pypy/jit/backend/x86/regloc.py @@ -521,6 +521,8 @@ UCOMISD = _binaryop('UCOMISD') CVTSI2SD = _binaryop('CVTSI2SD') CVTTSD2SI = _binaryop('CVTTSD2SI') + CVTSD2SS = _binaryop('CVTSD2SS') + CVTSS2SD = _binaryop('CVTSS2SD') SQRTSD = _binaryop('SQRTSD') @@ -534,6 +536,8 @@ PXOR = _binaryop('PXOR') PCMPEQD = _binaryop('PCMPEQD') + MOVD = _binaryop('MOVD') + CALL = _relative_unaryop('CALL') JMP = _relative_unaryop('JMP') diff --git a/pypy/jit/backend/x86/runner.py b/pypy/jit/backend/x86/runner.py --- a/pypy/jit/backend/x86/runner.py +++ b/pypy/jit/backend/x86/runner.py @@ -19,6 +19,7 @@ class AbstractX86CPU(AbstractLLCPU): debug = True supports_floats = True + supports_singlefloats = True BOOTSTRAP_TP = lltype.FuncType([], lltype.Signed) dont_keepalive_stuff = False # for tests diff --git a/pypy/jit/backend/x86/rx86.py b/pypy/jit/backend/x86/rx86.py --- a/pypy/jit/backend/x86/rx86.py +++ b/pypy/jit/backend/x86/rx86.py @@ -573,7 +573,8 @@ BTS_jr = insn(rex_w, '\x0F\xAB', register(2,8), abs_, immediate(1)) # x87 instructions - FSTP_b = insn('\xDD', orbyte(3<<3), stack_bp(1)) + FSTPL_b = insn('\xDD', orbyte(3<<3), stack_bp(1)) # rffi.DOUBLE ('as' wants L??) + FSTPS_s = insn('\xD9', orbyte(3<<3), stack_sp(1)) # lltype.SingleFloat # ------------------------------ Random mess ----------------------- RDTSC = insn('\x0F\x31') @@ -590,8 +591,18 @@ CVTTSD2SI_rx = xmminsn('\xF2', rex_w, '\x0F\x2C', register(1, 8), register(2), '\xC0') CVTTSD2SI_rb = xmminsn('\xF2', rex_w, '\x0F\x2C', register(1, 8), stack_bp(2)) - MOVD_rx = xmminsn('\x66', rex_w, '\x0F\x7E', register(2, 8), register(1), '\xC0') - MOVD_xr = xmminsn('\x66', rex_w, '\x0F\x6E', register(1, 8), register(2), '\xC0') + CVTSD2SS_xx = xmminsn('\xF2', rex_nw, '\x0F\x5A', + register(1, 8), register(2), '\xC0') + CVTSD2SS_xb = xmminsn('\xF2', rex_nw, '\x0F\x5A', + register(1, 8), stack_bp(2)) + CVTSS2SD_xx = xmminsn('\xF3', rex_nw, '\x0F\x5A', + register(1, 8), register(2), '\xC0') + CVTSS2SD_xb = xmminsn('\xF3', rex_nw, '\x0F\x5A', + register(1, 8), stack_bp(2)) + + MOVD_rx = xmminsn('\x66', rex_nw, '\x0F\x7E', register(2, 8), register(1), '\xC0') + MOVD_xr = xmminsn('\x66', rex_nw, '\x0F\x6E', register(1, 8), register(2), '\xC0') + MOVD_xb = xmminsn('\x66', rex_nw, '\x0F\x6E', register(1, 8), stack_bp(2)) PSRAD_xi = xmminsn('\x66', rex_nw, '\x0F\x72', register(1), '\xE0', immediate(2, 'b')) diff --git a/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py b/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py --- a/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py +++ b/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py @@ -36,6 +36,14 @@ def hexdump(s): return ' '.join(["%02X" % ord(c) for c in s]) +def reduce_to_32bit(s): + if s[:2] != '%r': + return s + if s[2:].isdigit(): + return s + 'd' + else: + return '%e' + s[2:] + # ____________________________________________________________ COUNT1 = 15 @@ -180,12 +188,14 @@ ## for m, extra in args: ## if m in (i386.MODRM, i386.MODRM8) or all: ## suffix = suffixes[sizes[m]] + suffix - if argmodes and not self.is_xmm_insn: + if (argmodes and not self.is_xmm_insn + and not instrname.startswith('FSTP')): suffix = suffixes[self.WORD] # Special case: On 64-bit CPUs, rx86 assumes 64-bit integer # operands when converting to/from floating point, so we need to # indicate that with a suffix - if (self.WORD == 8) and instrname.startswith('CVT'): + if (self.WORD == 8) and (instrname.startswith('CVT') and + 'SI' in instrname): suffix = suffixes[self.WORD] if instr_suffix is not None: @@ -218,10 +228,10 @@ and ops[1].startswith('%r')): # movq $xxx, %rax => movl $xxx, %eax suffix = 'l' - if ops[1][2:].isdigit(): - ops[1] += 'd' - else: - ops[1] = '%e' + ops[1][2:] + ops[1] = reduce_to_32bit(ops[1]) + if instrname.lower() == 'movd': + ops[0] = reduce_to_32bit(ops[0]) + ops[1] = reduce_to_32bit(ops[1]) # op = '\t%s%s %s%s' % (instrname.lower(), suffix, ', '.join(ops), following) diff --git a/pypy/jit/codewriter/assembler.py b/pypy/jit/codewriter/assembler.py --- a/pypy/jit/codewriter/assembler.py +++ b/pypy/jit/codewriter/assembler.py @@ -76,6 +76,8 @@ TYPE = llmemory.Address if TYPE == llmemory.Address: value = heaptracker.adr2int(value) + if TYPE is lltype.SingleFloat: + value = longlong.singlefloat2int(value) if not isinstance(value, (llmemory.AddressAsInt, ComputedIntSymbolic)): value = lltype.cast_primitive(lltype.Signed, value) diff --git a/pypy/jit/codewriter/jtransform.py b/pypy/jit/codewriter/jtransform.py --- a/pypy/jit/codewriter/jtransform.py +++ b/pypy/jit/codewriter/jtransform.py @@ -9,7 +9,7 @@ from pypy.objspace.flow.model import SpaceOperation, Variable, Constant, c_last_exception from pypy.rlib import objectmodel from pypy.rlib.jit import _we_are_jitted -from pypy.rpython.lltypesystem import lltype, llmemory, rstr, rclass +from pypy.rpython.lltypesystem import lltype, llmemory, rstr, rclass, rffi from pypy.rpython.rclass import IR_QUASIIMMUTABLE, IR_QUASIIMMUTABLE_ARRAY from pypy.translator.simplify import get_funcobj from pypy.translator.unsimplify import varoftype @@ -785,7 +785,6 @@ op2.result = op.result return op2 elif toll: - from pypy.rpython.lltypesystem import rffi size, unsigned = rffi.size_and_sign(op.args[0].concretetype) if unsigned: INTERMEDIATE = lltype.Unsigned @@ -807,20 +806,27 @@ return self.force_cast_without_longlong(op.args[0], op.result) def force_cast_without_longlong(self, v_arg, v_result): - from pypy.rpython.lltypesystem.rffi import size_and_sign, sizeof, FLOAT - # - if (v_result.concretetype in (FLOAT, lltype.Float) or - v_arg.concretetype in (FLOAT, lltype.Float)): - assert (v_result.concretetype == lltype.Float and - v_arg.concretetype == lltype.Float), "xxx unsupported cast" + if v_result.concretetype == v_arg.concretetype: return - # - size2, unsigned2 = size_and_sign(v_result.concretetype) - assert size2 <= sizeof(lltype.Signed) - if size2 == sizeof(lltype.Signed): + if v_arg.concretetype == rffi.FLOAT: + assert v_result.concretetype == lltype.Float, "cast %s -> %s" % ( + v_arg.concretetype, v_result.concretetype) + return SpaceOperation('cast_singlefloat_to_float', [v_arg], + v_result) + if v_result.concretetype == rffi.FLOAT: + assert v_arg.concretetype == lltype.Float, "cast %s -> %s" % ( + v_arg.concretetype, v_result.concretetype) + return SpaceOperation('cast_float_to_singlefloat', [v_arg], + v_result) + return self.force_cast_without_singlefloat(v_arg, v_result) + + def force_cast_without_singlefloat(self, v_arg, v_result): + size2, unsigned2 = rffi.size_and_sign(v_result.concretetype) + assert size2 <= rffi.sizeof(lltype.Signed) + if size2 == rffi.sizeof(lltype.Signed): return # the target type is LONG or ULONG - size1, unsigned1 = size_and_sign(v_arg.concretetype) - assert size1 <= sizeof(lltype.Signed) + size1, unsigned1 = rffi.size_and_sign(v_arg.concretetype) + assert size1 <= rffi.sizeof(lltype.Signed) # def bounds(size, unsigned): if unsigned: @@ -849,7 +855,6 @@ return result def rewrite_op_direct_ptradd(self, op): - from pypy.rpython.lltypesystem import rffi # xxx otherwise, not implemented: assert op.args[0].concretetype == rffi.CCHARP # diff --git a/pypy/jit/codewriter/longlong.py b/pypy/jit/codewriter/longlong.py --- a/pypy/jit/codewriter/longlong.py +++ b/pypy/jit/codewriter/longlong.py @@ -7,7 +7,8 @@ """ import sys -from pypy.rpython.lltypesystem import lltype +from pypy.rpython.lltypesystem import lltype, rffi +from pypy.rlib import rarithmetic, longlong2float if sys.maxint > 2147483647: @@ -31,8 +32,6 @@ # ---------- 32-bit platform ---------- # the type FloatStorage is r_longlong, and conversion is needed - from pypy.rlib import rarithmetic, longlong2float - is_64_bit = False supports_longlong = True r_float_storage = rarithmetic.r_longlong @@ -41,9 +40,19 @@ getfloatstorage = longlong2float.float2longlong getrealfloat = longlong2float.longlong2float gethash = lambda xll: rarithmetic.intmask(xll - (xll >> 32)) - is_longlong = lambda TYPE: (TYPE == lltype.SignedLongLong or - TYPE == lltype.UnsignedLongLong) + is_longlong = lambda TYPE: (TYPE is lltype.SignedLongLong or + TYPE is lltype.UnsignedLongLong) # ------------------------------------- ZEROF = getfloatstorage(0.0) + +# ____________________________________________________________ + +def int2singlefloat(x): + x = rffi.r_uint(x) + return longlong2float.uint2singlefloat(x) + +def singlefloat2int(x): + x = longlong2float.singlefloat2uint(x) + return rffi.cast(lltype.Signed, x) diff --git a/pypy/jit/codewriter/policy.py b/pypy/jit/codewriter/policy.py --- a/pypy/jit/codewriter/policy.py +++ b/pypy/jit/codewriter/policy.py @@ -12,6 +12,7 @@ self.unsafe_loopy_graphs = set() self.supports_floats = False self.supports_longlong = False + self.supports_singlefloats = False def set_supports_floats(self, flag): self.supports_floats = flag @@ -19,6 +20,9 @@ def set_supports_longlong(self, flag): self.supports_longlong = flag + def set_supports_singlefloats(self, flag): + self.supports_singlefloats = flag + def dump_unsafe_loops(self): f = udir.join("unsafe-loops.txt").open('w') strs = [str(graph) for graph in self.unsafe_loopy_graphs] @@ -58,8 +62,9 @@ func, '_jit_unroll_safe_', False) unsupported = contains_unsupported_variable_type(graph, - self.supports_floats, - self.supports_longlong) + self.supports_floats, + self.supports_longlong, + self.supports_singlefloats) res = see_function and not unsupported if res and contains_loop: self.unsafe_loopy_graphs.add(graph) @@ -80,17 +85,24 @@ return res def contains_unsupported_variable_type(graph, supports_floats, - supports_longlong): + supports_longlong, + supports_singlefloats): getkind = history.getkind try: for block in graph.iterblocks(): for v in block.inputargs: - getkind(v.concretetype, supports_floats, supports_longlong) + getkind(v.concretetype, supports_floats, + supports_longlong, + supports_singlefloats) for op in block.operations: for v in op.args: - getkind(v.concretetype, supports_floats, supports_longlong) + getkind(v.concretetype, supports_floats, + supports_longlong, + supports_singlefloats) v = op.result - getkind(v.concretetype, supports_floats, supports_longlong) + getkind(v.concretetype, supports_floats, + supports_longlong, + supports_singlefloats) except NotImplementedError, e: log.WARNING('%s, ignoring graph' % (e,)) log.WARNING(' %s' % (graph,)) diff --git a/pypy/jit/codewriter/test/test_longlong.py b/pypy/jit/codewriter/test/test_longlong.py --- a/pypy/jit/codewriter/test/test_longlong.py +++ b/pypy/jit/codewriter/test/test_longlong.py @@ -230,3 +230,18 @@ assert list(op1.args[3]) == [] assert list(op1.args[4]) == vlist assert op1.result == v_result + + +##def test_singlefloat_constants(): +## v_x = varoftype(TYPE) +## vlist = [v_x, const(rffi.cast(TYPE, 7))] +## v_result = varoftype(TYPE) +## op = SpaceOperation('llong_add', vlist, v_result) +## tr = Transformer(FakeCPU(), FakeBuiltinCallControl()) +## op1 = tr.rewrite_operation(op) +## # +## assert op1.opname == 'residual_call_irf_f' +## assert list(op1.args[2]) == [] +## assert list(op1.args[3]) == [] +## assert list(op1.args[4]) == vlist +## assert op1.result == v_result diff --git a/pypy/jit/codewriter/test/test_policy.py b/pypy/jit/codewriter/test/test_policy.py --- a/pypy/jit/codewriter/test/test_policy.py +++ b/pypy/jit/codewriter/test/test_policy.py @@ -12,24 +12,30 @@ graph = support.getgraph(f, [5]) for sf in [False, True]: for sll in [False, True]: - assert not contains_unsupported_variable_type(graph, sf, sll) + for ssf in [False, True]: + assert not contains_unsupported_variable_type(graph, sf, + sll, ssf) # graph = support.getgraph(f, [5.5]) for sf in [False, True]: for sll in [False, True]: - res = contains_unsupported_variable_type(graph, sf, sll) - assert res is not sf + for ssf in [False, True]: + res = contains_unsupported_variable_type(graph, sf, sll, ssf) + assert res is not sf # graph = support.getgraph(f, [r_singlefloat(5.5)]) for sf in [False, True]: for sll in [False, True]: - assert contains_unsupported_variable_type(graph, sf, sll) + for ssf in [False, True]: + res = contains_unsupported_variable_type(graph, sf, sll, ssf) + assert res == (not ssf) # graph = support.getgraph(f, [r_longlong(5)]) for sf in [False, True]: for sll in [False, True]: - res = contains_unsupported_variable_type(graph, sf, sll) - assert res == (sys.maxint == 2147483647 and not sll) + for ssf in [False, True]: + res = contains_unsupported_variable_type(graph, sf, sll, ssf) + assert res == (sys.maxint == 2147483647 and not sll) def test_regular_function(): diff --git a/pypy/jit/metainterp/blackhole.py b/pypy/jit/metainterp/blackhole.py --- a/pypy/jit/metainterp/blackhole.py +++ b/pypy/jit/metainterp/blackhole.py @@ -623,6 +623,19 @@ x = float(a) return longlong.getfloatstorage(x) + @arguments("f", returns="i") + def bhimpl_cast_float_to_singlefloat(a): + from pypy.rlib.rarithmetic import r_singlefloat + a = longlong.getrealfloat(a) + a = r_singlefloat(a) + return longlong.singlefloat2int(a) + + @arguments("i", returns="f") + def bhimpl_cast_singlefloat_to_float(a): + a = longlong.int2singlefloat(a) + a = float(a) + return longlong.getfloatstorage(a) + # ---------- # control flow operations diff --git a/pypy/jit/metainterp/executor.py b/pypy/jit/metainterp/executor.py --- a/pypy/jit/metainterp/executor.py +++ b/pypy/jit/metainterp/executor.py @@ -50,7 +50,7 @@ func = argboxes[0].getint() # do the call using the correct function from the cpu rettype = descr.get_return_type() - if rettype == INT: + if rettype == INT or rettype == 'S': # *S*ingle float try: result = cpu.bh_call_i(func, descr, args_i, args_r, args_f) except Exception, e: @@ -64,7 +64,7 @@ metainterp.execute_raised(e) result = NULL return BoxPtr(result) - if rettype == FLOAT or rettype == 'L': + if rettype == FLOAT or rettype == 'L': # *L*ong long try: result = cpu.bh_call_f(func, descr, args_i, args_r, args_f) except Exception, e: diff --git a/pypy/jit/metainterp/history.py b/pypy/jit/metainterp/history.py --- a/pypy/jit/metainterp/history.py +++ b/pypy/jit/metainterp/history.py @@ -20,12 +20,16 @@ FAILARGS_LIMIT = 1000 -def getkind(TYPE, supports_floats=True, supports_longlong=True): +def getkind(TYPE, supports_floats=True, + supports_longlong=True, + supports_singlefloats=True): if TYPE is lltype.Void: return "void" elif isinstance(TYPE, lltype.Primitive): if TYPE is lltype.Float and supports_floats: return 'float' + if TYPE is lltype.SingleFloat and supports_singlefloats: + return 'int' # singlefloats are stored in an int if TYPE in (lltype.Float, lltype.SingleFloat): raise NotImplementedError("type %s not supported" % TYPE) # XXX fix this for oo... @@ -145,6 +149,7 @@ """ Implement in call descr. Must return INT, REF, FLOAT, or 'v' for void. On 32-bit (hack) it can also be 'L' for longlongs. + Additionally it can be 'S' for singlefloats. """ raise NotImplementedError diff --git a/pypy/jit/metainterp/optimizeopt/fficall.py b/pypy/jit/metainterp/optimizeopt/fficall.py --- a/pypy/jit/metainterp/optimizeopt/fficall.py +++ b/pypy/jit/metainterp/optimizeopt/fficall.py @@ -6,7 +6,6 @@ from pypy.jit.metainterp.resoperation import rop, ResOperation from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method from pypy.jit.metainterp.optimizeopt.optimizer import Optimization -from pypy.jit.backend.llsupport.ffisupport import UnsupportedKind class FuncInfo(object): @@ -20,11 +19,8 @@ self.funcval = funcval self.opargs = [] argtypes, restype = self._get_signature(funcval) - try: - self.descr = cpu.calldescrof_dynamic(argtypes, restype) - except UnsupportedKind: - # e.g., I or U for long longs - self.descr = None + self.descr = cpu.calldescrof_dynamic(argtypes, restype) + # ^^^ may be None if unsupported self.prepare_op = prepare_op self.delayed_ops = [] @@ -184,7 +180,8 @@ def do_call(self, op): funcval = self._get_funcval(op) funcinfo = self.funcinfo - if not funcinfo or funcinfo.funcval is not funcval: + if (not funcinfo or funcinfo.funcval is not funcval or + funcinfo.descr is None): return [op] # cannot optimize funcsymval = self.getvalue(op.getarg(2)) arglist = [funcsymval.force_box()] diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py --- a/pypy/jit/metainterp/pyjitpl.py +++ b/pypy/jit/metainterp/pyjitpl.py @@ -215,6 +215,7 @@ for _opimpl in ['int_is_true', 'int_is_zero', 'int_neg', 'int_invert', 'cast_float_to_int', 'cast_int_to_float', + 'cast_float_to_singlefloat', 'cast_singlefloat_to_float', 'float_neg', 'float_abs', ]: exec py.code.Source(''' @@ -1227,7 +1228,7 @@ src_i = src_r = src_f = 0 i = 1 for kind in descr.get_arg_types(): - if kind == history.INT: + if kind == history.INT or kind == 'S': # single float while True: box = argboxes[src_i] src_i += 1 diff --git a/pypy/jit/metainterp/resoperation.py b/pypy/jit/metainterp/resoperation.py --- a/pypy/jit/metainterp/resoperation.py +++ b/pypy/jit/metainterp/resoperation.py @@ -408,6 +408,8 @@ 'FLOAT_ABS/1', 'CAST_FLOAT_TO_INT/1', 'CAST_INT_TO_FLOAT/1', + 'CAST_FLOAT_TO_SINGLEFLOAT/1', + 'CAST_SINGLEFLOAT_TO_FLOAT/1', # 'INT_LT/2b', 'INT_LE/2b', diff --git a/pypy/jit/metainterp/test/test_fficall.py b/pypy/jit/metainterp/test/test_fficall.py --- a/pypy/jit/metainterp/test/test_fficall.py +++ b/pypy/jit/metainterp/test/test_fficall.py @@ -3,7 +3,7 @@ from pypy.rlib.rarithmetic import r_singlefloat, r_longlong, r_ulonglong from pypy.rlib.jit import JitDriver, promote, dont_look_inside from pypy.rlib.unroll import unrolling_iterable -from pypy.rlib.libffi import ArgChain, longlong2float, float2longlong +from pypy.rlib.libffi import ArgChain from pypy.rlib.libffi import IS_32_BIT from pypy.rlib.test.test_libffi import TestLibffiCall as _TestLibffiCall from pypy.rpython.lltypesystem import lltype, rffi @@ -12,10 +12,11 @@ from pypy.jit.metainterp.test.support import LLJitMixin class TestFfiCall(LLJitMixin, _TestLibffiCall): + supports_all = False # supports_{floats,longlong,singlefloats} # ===> ../../../rlib/test/test_libffi.py - def call(self, funcspec, args, RESULT, init_result=0, is_struct=False): + def call(self, funcspec, args, RESULT, is_struct=False, jitif=[]): """ Call the function specified by funcspec in a loop, and let the jit to see and optimize it. @@ -24,14 +25,7 @@ lib, name, argtypes, restype = funcspec method_and_args = [] for argval in args: - if type(argval) is r_singlefloat: - method_name = 'arg_singlefloat' - argval = float(argval) - elif IS_32_BIT and type(argval) in [r_longlong, r_ulonglong]: - method_name = 'arg_longlong' - argval = rffi.cast(rffi.LONGLONG, argval) - argval = longlong2float(argval) - elif isinstance(argval, tuple): + if isinstance(argval, tuple): method_name, argval = argval else: method_name = 'arg' @@ -39,10 +33,20 @@ method_and_args = unrolling_iterable(method_and_args) # reds = ['n', 'res', 'func'] - if (RESULT in [rffi.FLOAT, rffi.DOUBLE] or + if (RESULT is rffi.DOUBLE or IS_32_BIT and RESULT in [rffi.LONGLONG, rffi.ULONGLONG]): - reds = ['n', 'func', 'res'] # floats must be *after* refs + reds = ['n', 'func', 'res'] # 'double' floats must be *after* refs driver = JitDriver(reds=reds, greens=[]) + init_result = rffi.cast(RESULT, 0) + # + def g(func): + # a different function, which is marked as "dont_look_inside" + # in case it uses an unsupported argument + argchain = ArgChain() + # this loop is unrolled + for method_name, argval in method_and_args: + getattr(argchain, method_name)(argval) + return func.call(argchain, RESULT, is_struct=is_struct) # def f(n): func = lib.getpointer(name, argtypes, restype) @@ -50,18 +54,44 @@ while n < 10: driver.jit_merge_point(n=n, res=res, func=func) promote(func) - argchain = ArgChain() - # this loop is unrolled - for method_name, argval in method_and_args: - getattr(argchain, method_name)(argval) - res = func.call(argchain, RESULT, is_struct=is_struct) + res = g(func) n += 1 return res # - res = self.meta_interp(f, [0], backendopt=True) + res = self.meta_interp(f, [0], backendopt=True, + supports_floats = self.supports_all, + supports_longlong = self.supports_all, + supports_singlefloats = self.supports_all) + d = {'floats': self.supports_all, + 'longlong': self.supports_all or not IS_32_BIT, + 'singlefloats': self.supports_all, + 'byval': False} + supported = all(d[check] for check in jitif) + if supported: + self.check_loops( + call_release_gil=1, # a CALL_RELEASE_GIL, and no other CALLs + call=0, + call_may_force=0, + guard_no_exception=1, + guard_not_forced=1, + int_add=1, + int_lt=1, + guard_true=1, + jump=1) + else: + self.check_loops( + call_release_gil=0, # no CALL_RELEASE_GIL + int_add=1, + int_lt=1, + guard_true=1, + jump=1) return res def test_byval_result(self): _TestLibffiCall.test_byval_result(self) test_byval_result.__doc__ = _TestLibffiCall.test_byval_result.__doc__ test_byval_result.dont_track_allocations = True + + +class TestFfiCallSupportAll(TestFfiCall): + supports_all = True # supports_{floats,longlong,singlefloats} diff --git a/pypy/jit/metainterp/test/test_float.py b/pypy/jit/metainterp/test/test_float.py --- a/pypy/jit/metainterp/test/test_float.py +++ b/pypy/jit/metainterp/test/test_float.py @@ -36,6 +36,15 @@ res = self.interp_operations(f, [x]) assert res == -x + def test_singlefloat(self): + from pypy.rlib.rarithmetic import r_singlefloat + def f(a): + a = float(r_singlefloat(a)) + a *= 4.25 + return float(r_singlefloat(a)) + res = self.interp_operations(f, [-2.0]) + assert res == -8.5 + class TestOOtype(FloatTests, OOJitMixin): pass diff --git a/pypy/jit/metainterp/test/test_warmspot.py b/pypy/jit/metainterp/test/test_warmspot.py --- a/pypy/jit/metainterp/test/test_warmspot.py +++ b/pypy/jit/metainterp/test/test_warmspot.py @@ -303,6 +303,7 @@ class FakeCPU(object): supports_floats = False supports_longlong = False + supports_singlefloats = False ts = llhelper translate_support_code = False stats = "stats" diff --git a/pypy/jit/metainterp/test/test_warmstate.py b/pypy/jit/metainterp/test/test_warmstate.py --- a/pypy/jit/metainterp/test/test_warmstate.py +++ b/pypy/jit/metainterp/test/test_warmstate.py @@ -1,13 +1,14 @@ from pypy.rpython.test.test_llinterp import interpret -from pypy.rpython.lltypesystem import lltype, llmemory, rstr +from pypy.rpython.lltypesystem import lltype, llmemory, rstr, rffi from pypy.rpython.ootypesystem import ootype from pypy.rpython.annlowlevel import llhelper -from pypy.jit.metainterp.warmstate import wrap, unwrap +from pypy.jit.metainterp.warmstate import wrap, unwrap, specialize_value from pypy.jit.metainterp.warmstate import equal_whatever, hash_whatever from pypy.jit.metainterp.warmstate import WarmEnterState, JitCell from pypy.jit.metainterp.history import BoxInt, BoxFloat, BoxPtr from pypy.jit.metainterp.history import ConstInt, ConstFloat, ConstPtr from pypy.jit.codewriter import longlong +from pypy.rlib.rarithmetic import r_singlefloat def boxfloat(x): return BoxFloat(longlong.getfloatstorage(x)) @@ -40,6 +41,28 @@ assert _is(wrap(None, 42, in_const_box=True), ConstInt(42)) assert _is(wrap(None, 42.5, in_const_box=True), constfloat(42.5)) assert _is(wrap(None, p, in_const_box=True), ConstPtr(po)) + if longlong.supports_longlong: + import sys + from pypy.rlib.rarithmetic import r_longlong, r_ulonglong + value = r_longlong(-sys.maxint*17) + assert _is(wrap(None, value), BoxFloat(value)) + assert _is(wrap(None, value, in_const_box=True), ConstFloat(value)) + value_unsigned = r_ulonglong(-sys.maxint*17) + assert _is(wrap(None, value_unsigned), BoxFloat(value)) + sfval = r_singlefloat(42.5) + ival = longlong.singlefloat2int(sfval) + assert _is(wrap(None, sfval), BoxInt(ival)) + assert _is(wrap(None, sfval, in_const_box=True), ConstInt(ival)) + +def test_specialize_value(): + assert specialize_value(lltype.Char, 0x41) == '\x41' + if longlong.supports_longlong: + import sys + value = longlong.r_float_storage(sys.maxint*17) + assert specialize_value(lltype.SignedLongLong, value) == sys.maxint*17 + sfval = r_singlefloat(42.5) + ival = longlong.singlefloat2int(sfval) + assert specialize_value(rffi.FLOAT, ival) == sfval def test_hash_equal_whatever_lltype(): s1 = rstr.mallocstr(2) diff --git a/pypy/jit/metainterp/warmspot.py b/pypy/jit/metainterp/warmspot.py --- a/pypy/jit/metainterp/warmspot.py +++ b/pypy/jit/metainterp/warmspot.py @@ -173,6 +173,7 @@ policy = JitPolicy() policy.set_supports_floats(self.cpu.supports_floats) policy.set_supports_longlong(self.cpu.supports_longlong) + policy.set_supports_singlefloats(self.cpu.supports_singlefloats) graphs = self.codewriter.find_all_graphs(policy) policy.dump_unsafe_loops() self.check_access_directly_sanity(graphs) @@ -283,7 +284,9 @@ auto_inline_graphs(self.translator, graphs, 0.01) def build_cpu(self, CPUClass, translate_support_code=False, - no_stats=False, **kwds): + no_stats=False, supports_floats=True, + supports_longlong=True, supports_singlefloats=True, + **kwds): assert CPUClass is not None self.opt = history.Options(**kwds) if no_stats: @@ -295,6 +298,9 @@ self.annhelper = MixLevelHelperAnnotator(self.translator.rtyper) cpu = CPUClass(self.translator.rtyper, self.stats, self.opt, translate_support_code, gcdescr=self.gcdescr) + if not supports_floats: cpu.supports_floats = False + if not supports_longlong: cpu.supports_longlong = False + if not supports_singlefloats: cpu.supports_singlefloats = False self.cpu = cpu def build_meta_interp(self, ProfilerClass): diff --git a/pypy/jit/metainterp/warmstate.py b/pypy/jit/metainterp/warmstate.py --- a/pypy/jit/metainterp/warmstate.py +++ b/pypy/jit/metainterp/warmstate.py @@ -25,9 +25,13 @@ if isinstance(TYPE, lltype.Ptr) and TYPE.TO._gckind == 'raw': # non-gc pointer return rffi.cast(TYPE, x) + elif TYPE is lltype.SingleFloat: + return longlong.int2singlefloat(x) else: return lltype.cast_primitive(TYPE, x) elif INPUT is longlong.FLOATSTORAGE: + if longlong.is_longlong(TYPE): + return rffi.cast(TYPE, x) assert TYPE is lltype.Float return longlong.getrealfloat(x) else: @@ -84,8 +88,12 @@ return history.ConstObj(value) else: return history.BoxObj(value) - elif isinstance(value, float): - value = longlong.getfloatstorage(value) + elif (isinstance(value, float) or + longlong.is_longlong(lltype.typeOf(value))): + if isinstance(value, float): + value = longlong.getfloatstorage(value) + else: + value = rffi.cast(lltype.SignedLongLong, value) if in_const_box: return history.ConstFloat(value) else: @@ -93,6 +101,8 @@ elif isinstance(value, str) or isinstance(value, unicode): assert len(value) == 1 # must be a character value = ord(value) + elif lltype.typeOf(value) is lltype.SingleFloat: + value = longlong.singlefloat2int(value) else: value = intmask(value) if in_const_box: diff --git a/pypy/module/_ffi/interp_ffi.py b/pypy/module/_ffi/interp_ffi.py --- a/pypy/module/_ffi/interp_ffi.py +++ b/pypy/module/_ffi/interp_ffi.py @@ -222,9 +222,9 @@ w_arg = space.ord(w_arg) argchain.arg(space.int_w(w_arg)) elif w_argtype.is_double(): - argchain.arg(space.float_w(w_arg)) + self.arg_float(space, argchain, w_arg) elif w_argtype.is_singlefloat(): - argchain.arg_singlefloat(space.float_w(w_arg)) + self.arg_singlefloat(space, argchain, w_arg) elif w_argtype.is_struct(): # arg_raw directly takes value to put inside ll_args w_arg = space.interp_w(W_StructureInstance, w_arg) @@ -267,15 +267,26 @@ else: return w_arg - @jit.dont_look_inside + def arg_float(self, space, argchain, w_arg): + # a separate function, which can be seen by the jit or not, + # depending on whether floats are supported + argchain.arg(space.float_w(w_arg)) + def arg_longlong(self, space, argchain, w_arg): + # a separate function, which can be seen by the jit or not, + # depending on whether longlongs are supported bigarg = space.bigint_w(w_arg) ullval = bigarg.ulonglongmask() llval = rffi.cast(rffi.LONGLONG, ullval) - # this is a hack: we store the 64 bits of the long long into the - # 64 bits of a float (i.e., a C double) - floatval = libffi.longlong2float(llval) - argchain.arg_longlong(floatval) + argchain.arg(llval) + + def arg_singlefloat(self, space, argchain, w_arg): + # a separate function, which can be seen by the jit or not, + # depending on whether singlefloats are supported + from pypy.rlib.rarithmetic import r_singlefloat + fval = space.float_w(w_arg) + sfval = r_singlefloat(fval) + argchain.arg(sfval) def call(self, space, args_w): self = jit.promote(self) @@ -296,8 +307,7 @@ # note that we must check for longlong first, because either # is_signed or is_unsigned returns true anyway assert libffi.IS_32_BIT - reskind = libffi.types.getkind(self.func.restype) # XXX: remove the kind - return self._call_longlong(space, argchain, reskind) + return self._call_longlong(space, argchain) elif w_restype.is_signed(): return self._call_int(space, argchain) elif w_restype.is_unsigned() or w_restype.is_pointer(): @@ -309,12 +319,9 @@ intres = self.func.call(argchain, rffi.WCHAR_T) return space.wrap(unichr(intres)) elif w_restype.is_double(): - floatres = self.func.call(argchain, rffi.DOUBLE) - return space.wrap(floatres) + return self._call_float(space, argchain) elif w_restype.is_singlefloat(): - # the result is a float, but widened to be inside a double - floatres = self.func.call(argchain, rffi.FLOAT) - return space.wrap(floatres) + return self._call_singlefloat(space, argchain) elif w_restype.is_struct(): w_datashape = w_restype.w_datashape assert isinstance(w_datashape, W_Structure) @@ -383,19 +390,32 @@ space.wrap('Unsupported restype')) return space.wrap(intres) - @jit.dont_look_inside - def _call_longlong(self, space, argchain, reskind): - # this is a hack: we store the 64 bits of the long long into the 64 - # bits of a float (i.e., a C double) - floatres = self.func.call(argchain, rffi.LONGLONG) - llres = libffi.float2longlong(floatres) - if reskind == 'I': + def _call_float(self, space, argchain): + # a separate function, which can be seen by the jit or not, + # depending on whether floats are supported + floatres = self.func.call(argchain, rffi.DOUBLE) + return space.wrap(floatres) + + def _call_longlong(self, space, argchain): + # a separate function, which can be seen by the jit or not, + # depending on whether longlongs are supported + restype = self.func.restype + call = self.func.call + if restype is libffi.types.slonglong: + llres = call(argchain, rffi.LONGLONG) return space.wrap(llres) - elif reskind == 'U': - ullres = rffi.cast(rffi.ULONGLONG, llres) + elif restype is libffi.types.ulonglong: + ullres = call(argchain, rffi.ULONGLONG) return space.wrap(ullres) else: - assert False + raise OperationError(space.w_ValueError, + space.wrap('Unsupported longlong restype')) + + def _call_singlefloat(self, space, argchain): + # a separate function, which can be seen by the jit or not, + # depending on whether singlefloats are supported + sfres = self.func.call(argchain, rffi.FLOAT) + return space.wrap(float(sfres)) def getaddr(self, space): """ diff --git a/pypy/module/pypyjit/test_pypy_c/test_array.py b/pypy/module/pypyjit/test_pypy_c/test_array.py --- a/pypy/module/pypyjit/test_pypy_c/test_array.py +++ b/pypy/module/pypyjit/test_pypy_c/test_array.py @@ -1,4 +1,4 @@ -import py +import py, sys from pypy.module.pypyjit.test_pypy_c.test_00_model import BaseTestPyPyC class TestArray(BaseTestPyPyC): @@ -88,6 +88,73 @@ jump(p0, p1, p2, p3, p4, p5, p6, i28, i15, p9, i10, i11, descr=) """) + def test_array_of_doubles(self): + def main(): + from array import array + img = array('d', [21.5]*1000) + i = 0 + while i < 1000: + img[i] += 20.5 + assert img[i] == 42.0 + i += 1 + return 123 + # + log = self.run(main, []) + assert log.result == 123 + loop, = log.loops_by_filename(self.filepath) + assert loop.match(""" + i10 = int_lt(i6, 1000) + guard_true(i10, descr=...) + i11 = int_lt(i6, i7) + guard_true(i11, descr=...) + f13 = getarrayitem_raw(i8, i6, descr=) + f15 = float_add(f13, 20.500000) + setarrayitem_raw(i8, i6, f15, descr=) + f16 = getarrayitem_raw(i8, i6, descr=) + i18 = float_eq(f16, 42.000000) + guard_true(i18, descr=...) + i20 = int_add(i6, 1) + --TICK-- + jump(..., descr=) + """) + + def test_array_of_floats(self): + def main(): + from array import array + img = array('f', [21.5]*1000) + i = 0 + while i < 1000: + img[i] += 20.5 + assert img[i] == 42.0 + i += 1 + return 321 + # + log = self.run(main, []) + assert log.result == 321 + loop, = log.loops_by_filename(self.filepath) + if sys.maxint == 2147483647: + arraydescr = 'UnsignedArrayNoLengthDescr' + else: + arraydescr = 'UINTArrayNoLengthDescr' + assert loop.match(""" + i10 = int_lt(i6, 1000) + guard_true(i10, descr=...) + i11 = int_lt(i6, i7) + guard_true(i11, descr=...) + i13 = getarrayitem_raw(i8, i6, descr=<%s>) + f14 = cast_singlefloat_to_float(i13) + f16 = float_add(f14, 20.500000) + i17 = cast_float_to_singlefloat(f16) + setarrayitem_raw(i8, i6,i17, descr=<%s>) + i18 = getarrayitem_raw(i8, i6, descr=<%s>) + f19 = cast_singlefloat_to_float(i18) + i21 = float_eq(f19, 42.000000) + guard_true(i21, descr=...) + i23 = int_add(i6, 1) + --TICK-- + jump(..., descr=) + """ % (arraydescr, arraydescr, arraydescr)) + def test_zeropadded(self): def main(): diff --git a/pypy/rlib/libffi.py b/pypy/rlib/libffi.py --- a/pypy/rlib/libffi.py +++ b/pypy/rlib/libffi.py @@ -2,14 +2,13 @@ from pypy.rpython.lltypesystem import rffi, lltype from pypy.rlib.objectmodel import specialize, enforceargs, we_are_translated -from pypy.rlib.rarithmetic import intmask, r_uint, r_singlefloat +from pypy.rlib.rarithmetic import intmask, r_uint, r_singlefloat, r_longlong from pypy.rlib import jit from pypy.rlib import clibffi from pypy.rlib.clibffi import get_libc_name, FUNCFLAG_CDECL, AbstractFuncPtr, \ push_arg_as_ffiptr, c_ffi_call, FFI_TYPE_STRUCT from pypy.rlib.rdynload import dlopen, dlclose, dlsym, dlsym_byordinal from pypy.rlib.rdynload import DLLHANDLE -from pypy.rlib.longlong2float import longlong2float, float2longlong class types(object): """ @@ -122,9 +121,10 @@ elif TYPE is rffi.DOUBLE: cls = FloatArg elif TYPE is rffi.LONGLONG or TYPE is rffi.ULONGLONG: - raise TypeError, 'r_(u)longlong not supported by arg(), use arg_(u)longlong()' + cls = LongLongArg + val = rffi.cast(rffi.LONGLONG, val) elif TYPE is rffi.FLOAT: - raise TypeError, 'r_singlefloat not supported by arg(), use arg_singlefloat()' + cls = SingleFloatArg else: raise TypeError, 'Unsupported argument type: %s' % TYPE self._append(cls(val)) @@ -133,25 +133,6 @@ def arg_raw(self, val): self._append(RawArg(val)) - def arg_longlong(self, val): - """ - Note: this is a hack. So far, the JIT does not support long longs, so - you must pass it as if it were a python Float (rffi.DOUBLE). You can - use the convenience functions longlong2float and float2longlong to do - the conversions. Note that if you use long longs, the call won't - be jitted at all. - """ - assert IS_32_BIT # use a normal integer on 64-bit platforms - self._append(LongLongArg(val)) - - def arg_singlefloat(self, val): - """ - Note: you must pass a python Float (rffi.DOUBLE), not a r_singlefloat - (else the jit complains). Note that if you use single floats, the - call won't be jitted at all. - """ - self._append(SingleFloatArg(val)) - def _append(self, arg): if self.first is None: self.first = self.last = arg @@ -196,25 +177,25 @@ func._push_raw(self.ptrval, ll_args, i) class SingleFloatArg(AbstractArg): - """ An argument representing a C float (but holding a C double) + """ An argument representing a C float """ - def __init__(self, floatval): - self.floatval = floatval + def __init__(self, singlefloatval): + self.singlefloatval = singlefloatval def push(self, func, ll_args, i): - func._push_single_float(self.floatval, ll_args, i) + func._push_singlefloat(self.singlefloatval, ll_args, i) class LongLongArg(AbstractArg): - """ An argument representing a C long long (but holding a C double) + """ An argument representing a C long long """ - def __init__(self, floatval): - self.floatval = floatval + def __init__(self, longlongval): + self.longlongval = longlongval def push(self, func, ll_args, i): - func._push_longlong(self.floatval, ll_args, i) + func._push_longlong(self.longlongval, ll_args, i) # ====================================================================== @@ -274,15 +255,10 @@ elif RESULT is rffi.DOUBLE: return self._do_call_float(self.funcsym, ll_args) elif RESULT is rffi.FLOAT: - # XXX: even if RESULT is FLOAT, we still return a DOUBLE, else the - # jit complains. Note that the jit is disabled in this case - return self._do_call_single_float(self.funcsym, ll_args) + return self._do_call_singlefloat(self.funcsym, ll_args) elif RESULT is rffi.LONGLONG or RESULT is rffi.ULONGLONG: - # XXX: even if RESULT is LONGLONG, we still return a DOUBLE, else the - # jit complains. Note that the jit is disabled in this case - # (it's not a typo, we really return a DOUBLE) assert IS_32_BIT - return self._do_call_longlong(self.funcsym, ll_args) + res = self._do_call_longlong(self.funcsym, ll_args) elif RESULT is lltype.Void: return self._do_call_void(self.funcsym, ll_args) else: @@ -320,16 +296,15 @@ def _push_float(self, value, ll_args, i): self._push_arg(value, ll_args, i) - @jit.dont_look_inside - def _push_single_float(self, value, ll_args, i): - self._push_arg(r_singlefloat(value), ll_args, i) + @jit.oopspec('libffi_push_singlefloat(self, value, ll_args, i)') + @enforceargs(None, r_singlefloat, None, int) # fix the annotation for tests + def _push_singlefloat(self, value, ll_args, i): + self._push_arg(value, ll_args, i) - @jit.dont_look_inside - def _push_longlong(self, floatval, ll_args, i): - """ - Takes a longlong represented as a python Float. It's a hack for the - jit, else we could not see the whole libffi module at all""" - self._push_arg(float2longlong(floatval), ll_args, i) + @jit.oopspec('libffi_push_longlong(self, value, ll_args, i)') + @enforceargs(None, r_longlong, None, int) # fix the annotation for tests + def _push_longlong(self, value, ll_args, i): + self._push_arg(value, ll_args, i) @jit.oopspec('libffi_call_int(self, funcsym, ll_args)') def _do_call_int(self, funcsym, ll_args): @@ -339,20 +314,18 @@ def _do_call_float(self, funcsym, ll_args): return self._do_call(funcsym, ll_args, rffi.DOUBLE) - @jit.dont_look_inside - def _do_call_single_float(self, funcsym, ll_args): - single_res = self._do_call(funcsym, ll_args, rffi.FLOAT) - return float(single_res) + @jit.oopspec('libffi_call_singlefloat(self, funcsym, ll_args)') + def _do_call_singlefloat(self, funcsym, ll_args): + return self._do_call(funcsym, ll_args, rffi.FLOAT) @jit.dont_look_inside def _do_call_raw(self, funcsym, ll_args): # same as _do_call_int, but marked as jit.dont_look_inside return self._do_call(funcsym, ll_args, rffi.LONG) - @jit.dont_look_inside + @jit.oopspec('libffi_call_longlong(self, funcsym, ll_args)') def _do_call_longlong(self, funcsym, ll_args): - llres = self._do_call(funcsym, ll_args, rffi.LONGLONG) - return longlong2float(llres) + return self._do_call(funcsym, ll_args, rffi.LONGLONG) @jit.oopspec('libffi_call_void(self, funcsym, ll_args)') def _do_call_void(self, funcsym, ll_args): diff --git a/pypy/rlib/longlong2float.py b/pypy/rlib/longlong2float.py --- a/pypy/rlib/longlong2float.py +++ b/pypy/rlib/longlong2float.py @@ -11,6 +11,8 @@ # -------- implement longlong2float and float2longlong -------- DOUBLE_ARRAY_PTR = lltype.Ptr(lltype.Array(rffi.DOUBLE)) LONGLONG_ARRAY_PTR = lltype.Ptr(lltype.Array(rffi.LONGLONG)) +UINT_ARRAY_PTR = lltype.Ptr(lltype.Array(rffi.UINT)) +FLOAT_ARRAY_PTR = lltype.Ptr(lltype.Array(rffi.FLOAT)) # these definitions are used only in tests, when not translated def longlong2float_emulator(llval): @@ -29,6 +31,22 @@ lltype.free(d_array, flavor='raw') return llval +def uint2singlefloat_emulator(ival): + f_array = lltype.malloc(FLOAT_ARRAY_PTR.TO, 1, flavor='raw') + i_array = rffi.cast(UINT_ARRAY_PTR, f_array) + i_array[0] = ival + singlefloatval = f_array[0] + lltype.free(f_array, flavor='raw') + return singlefloatval + +def singlefloat2uint_emulator(singlefloatval): + f_array = lltype.malloc(FLOAT_ARRAY_PTR.TO, 1, flavor='raw') + i_array = rffi.cast(UINT_ARRAY_PTR, f_array) + f_array[0] = singlefloatval + ival = i_array[0] + lltype.free(f_array, flavor='raw') + return ival + from pypy.translator.tool.cbuild import ExternalCompilationInfo eci = ExternalCompilationInfo(includes=['string.h', 'assert.h'], post_include_bits=[""" @@ -44,6 +62,18 @@ memcpy(&ll, &x, 8); return ll; } +static float pypy__uint2singlefloat(unsigned int x) { + float ff; + assert(sizeof(float) == 4 && sizeof(unsigned int) == 4); + memcpy(&ff, &x, 4); + return ff; +} +static unsigned int pypy__singlefloat2uint(float x) { + unsigned int ii; + assert(sizeof(float) == 4 && sizeof(unsigned int) == 4); + memcpy(&ii, &x, 4); + return ii; +} """]) longlong2float = rffi.llexternal( @@ -55,3 +85,13 @@ "pypy__float2longlong", [rffi.DOUBLE], rffi.LONGLONG, _callable=float2longlong_emulator, compilation_info=eci, _nowrapper=True, elidable_function=True) + +uint2singlefloat = rffi.llexternal( + "pypy__uint2singlefloat", [rffi.UINT], rffi.FLOAT, + _callable=uint2singlefloat_emulator, compilation_info=eci, + _nowrapper=True, elidable_function=True) + +singlefloat2uint = rffi.llexternal( + "pypy__singlefloat2uint", [rffi.FLOAT], rffi.UINT, + _callable=singlefloat2uint_emulator, compilation_info=eci, + _nowrapper=True, elidable_function=True) diff --git a/pypy/rlib/rarithmetic.py b/pypy/rlib/rarithmetic.py --- a/pypy/rlib/rarithmetic.py +++ b/pypy/rlib/rarithmetic.py @@ -71,9 +71,8 @@ return int(n) def longlongmask(n): - if isinstance(n, int): - n = long(n) - assert isinstance(n, long) + assert isinstance(n, (int, long)) + n = long(n) n &= LONGLONG_MASK if n >= LONGLONG_TEST: n -= 2*LONGLONG_TEST diff --git a/pypy/rlib/test/test_libffi.py b/pypy/rlib/test/test_libffi.py --- a/pypy/rlib/test/test_libffi.py +++ b/pypy/rlib/test/test_libffi.py @@ -5,7 +5,7 @@ from pypy.rlib.rarithmetic import r_singlefloat, r_longlong, r_ulonglong from pypy.rlib.test.test_clibffi import BaseFfiTest, get_libm_name, make_struct_ffitype_e from pypy.rlib.libffi import CDLL, Func, get_libc_name, ArgChain, types -from pypy.rlib.libffi import longlong2float, float2longlong, IS_32_BIT +from pypy.rlib.libffi import IS_32_BIT class TestLibffiMisc(BaseFfiTest): @@ -52,19 +52,6 @@ del lib assert not ALLOCATED - def test_longlong_as_float(self): - from pypy.translator.c.test.test_genc import compile - maxint64 = r_longlong(9223372036854775807) - def fn(x): - d = longlong2float(x) - ll = float2longlong(d) - return ll - assert fn(maxint64) == maxint64 - # - fn2 = compile(fn, [r_longlong]) - res = fn2(maxint64) - assert res == maxint64 - class TestLibffiCall(BaseFfiTest): """ Test various kind of calls through libffi. @@ -111,7 +98,7 @@ def get_libfoo(self): return self.CDLL(self.libfoo_name) - def call(self, funcspec, args, RESULT, init_result=0, is_struct=False): + def call(self, funcspec, args, RESULT, is_struct=False, jitif=[]): """ Call the specified function after constructing and ArgChain with the arguments in ``args``. @@ -128,14 +115,7 @@ func = lib.getpointer(name, argtypes, restype) chain = ArgChain() for arg in args: - if isinstance(arg, r_singlefloat): - chain.arg_singlefloat(float(arg)) - elif IS_32_BIT and isinstance(arg, r_longlong): - chain.arg_longlong(longlong2float(arg)) - elif IS_32_BIT and isinstance(arg, r_ulonglong): - arg = rffi.cast(rffi.LONGLONG, arg) - chain.arg_longlong(longlong2float(arg)) - elif isinstance(arg, tuple): + if isinstance(arg, tuple): methname, arg = arg meth = getattr(chain, methname) meth(arg) @@ -143,13 +123,19 @@ chain.arg(arg) return func.call(chain, RESULT, is_struct=is_struct) - def check_loops(self, *args, **kwds): + # ------------------------------------------------------------------------ + + def test_very_simple(self): """ - Ignored here, but does something in the JIT tests + int diff_xy(int x, long y) + { + return x - y; + } """ - pass - - # ------------------------------------------------------------------------ + libfoo = self.get_libfoo() + func = (libfoo, 'diff_xy', [types.sint, types.slong], types.sint) + res = self.call(func, [50, 8], lltype.Signed) + assert res == 42 def test_simple(self): """ @@ -160,23 +146,14 @@ """ libfoo = self.get_libfoo() func = (libfoo, 'sum_xy', [types.sint, types.double], types.sint) - res = self.call(func, [38, 4.2], rffi.LONG) + res = self.call(func, [38, 4.2], lltype.Signed, jitif=["floats"]) assert res == 42 - self.check_loops({ - 'call_release_gil': 1, - 'guard_no_exception': 1, - 'guard_not_forced': 1, - 'int_add': 1, - 'int_lt': 1, - 'guard_true': 1, - 'jump': 1}) def test_float_result(self): libm = self.get_libm() func = (libm, 'pow', [types.double, types.double], types.double) - res = self.call(func, [2.0, 3.0], rffi.DOUBLE, init_result=0.0) + res = self.call(func, [2.0, 3.0], rffi.DOUBLE, jitif=["floats"]) assert res == 8.0 - self.check_loops(call_release_gil=1, guard_no_exception=1, guard_not_forced=1) def test_cast_result(self): """ @@ -189,7 +166,6 @@ func = (libfoo, 'cast_to_uchar_and_ovf', [types.sint], types.uchar) res = self.call(func, [0], rffi.UCHAR) assert res == 200 - self.check_loops(call_release_gil=1, guard_no_exception=1, guard_not_forced=1) def test_cast_argument(self): """ @@ -271,8 +247,7 @@ libfoo = self.get_libfoo() func = (libfoo, 'get_pointer_to_b', [], types.pointer) LONGP = lltype.Ptr(rffi.CArray(rffi.LONG)) - null = lltype.nullptr(LONGP.TO) - res = self.call(func, [], LONGP, init_result=null) + res = self.call(func, [], LONGP) assert res[0] == 20 def test_void_result(self): @@ -287,7 +262,7 @@ # initval = self.call(get_dummy, [], rffi.LONG) # - res = self.call(set_dummy, [initval+1], lltype.Void, init_result=None) + res = self.call(set_dummy, [initval+1], lltype.Void) assert res is None # res = self.call(get_dummy, [], rffi.LONG) @@ -305,9 +280,9 @@ func = (libfoo, 'sum_xy_float', [types.float, types.float], types.float) x = r_singlefloat(12.34) y = r_singlefloat(56.78) - res = self.call(func, [x, y], rffi.FLOAT, init_result=0.0) + res = self.call(func, [x, y], rffi.FLOAT, jitif=["singlefloats"]) expected = c_float(c_float(12.34).value + c_float(56.78).value).value - assert res == expected + assert float(res) == expected def test_slonglong_args(self): """ @@ -325,16 +300,10 @@ if IS_32_BIT: x = r_longlong(maxint32+1) y = r_longlong(maxint32+2) - zero = longlong2float(r_longlong(0)) else: x = maxint32+1 y = maxint32+2 - zero = 0 - res = self.call(func, [x, y], rffi.LONGLONG, init_result=zero) - if IS_32_BIT: - # obscure, on 32bit it's really a long long, so it returns a - # DOUBLE because of the JIT hack - res = float2longlong(res) + res = self.call(func, [x, y], rffi.LONGLONG, jitif=["longlong"]) expected = maxint32*2 + 3 assert res == expected @@ -354,12 +323,7 @@ types.ulonglong) x = r_ulonglong(maxint64+1) y = r_ulonglong(2) - res = self.call(func, [x, y], rffi.ULONGLONG, init_result=0) - if IS_32_BIT: - # obscure, on 32bit it's really a long long, so it returns a - # DOUBLE because of the JIT hack - res = float2longlong(res) - res = rffi.cast(rffi.ULONGLONG, res) + res = self.call(func, [x, y], rffi.ULONGLONG, jitif=["longlong"]) expected = maxint64 + 3 assert res == expected @@ -406,7 +370,8 @@ buf[0] = 30 buf[1] = 12 adr = rffi.cast(rffi.VOIDP, buf) - res = self.call(sum_point, [('arg_raw', adr)], rffi.LONG, init_result=0) + res = self.call(sum_point, [('arg_raw', adr)], rffi.LONG, + jitif=["byval"]) assert res == 42 # check that we still have the ownership on the buffer assert buf[0] == 30 @@ -431,8 +396,8 @@ make_point = (libfoo, 'make_point', [types.slong, types.slong], ffi_point) # PTR = lltype.Ptr(rffi.CArray(rffi.LONG)) - p = self.call(make_point, [12, 34], PTR, init_result=lltype.nullptr(PTR.TO), - is_struct=True) + p = self.call(make_point, [12, 34], PTR, is_struct=True, + jitif=["byval"]) assert p[0] == 12 assert p[1] == 34 lltype.free(p, flavor='raw') diff --git a/pypy/rlib/test/test_longlong2float.py b/pypy/rlib/test/test_longlong2float.py --- a/pypy/rlib/test/test_longlong2float.py +++ b/pypy/rlib/test/test_longlong2float.py @@ -1,5 +1,7 @@ from pypy.translator.c.test.test_genc import compile from pypy.rlib.longlong2float import longlong2float, float2longlong +from pypy.rlib.longlong2float import uint2singlefloat, singlefloat2uint +from pypy.rlib.rarithmetic import r_singlefloat def fn(f1): @@ -28,3 +30,23 @@ for x in enum_floats(): res = fn2(x) assert repr(res) == repr(x) + +# ____________________________________________________________ + +def fnsingle(f1): + sf1 = r_singlefloat(f1) + ii = singlefloat2uint(sf1) + sf2 = uint2singlefloat(ii) + f2 = float(sf2) + return f2 + +def test_int_as_singlefloat(): + for x in enum_floats(): + res = fnsingle(x) + assert repr(res) == repr(float(r_singlefloat(x))) + +def test_compiled_single(): + fn2 = compile(fnsingle, [float]) + for x in enum_floats(): + res = fn2(x) + assert repr(res) == repr(float(r_singlefloat(x))) diff --git a/pypy/rpython/lltypesystem/ll2ctypes.py b/pypy/rpython/lltypesystem/ll2ctypes.py --- a/pypy/rpython/lltypesystem/ll2ctypes.py +++ b/pypy/rpython/lltypesystem/ll2ctypes.py @@ -27,7 +27,11 @@ from pypy.rpython import raddress from pypy.translator.platform import platform from array import array -from thread import _local as tlsobject +try: + from thread import _local as tlsobject +except ImportError: + class tlsobject(object): + pass # ____________________________________________________________ @@ -688,6 +692,8 @@ res = ctypes.cast(res, ctypes.c_void_p).value if res is None: return 0 + if T.TO.RESULT == lltype.SingleFloat: + res = res.value # baaaah, cannot return a c_float() return res def callback(*cargs): diff --git a/pypy/rpython/lltypesystem/lltype.py b/pypy/rpython/lltypesystem/lltype.py --- a/pypy/rpython/lltypesystem/lltype.py +++ b/pypy/rpython/lltypesystem/lltype.py @@ -1,7 +1,7 @@ import py from pypy.rlib.rarithmetic import (r_int, r_uint, intmask, r_singlefloat, r_ulonglong, r_longlong, r_longfloat, - base_int, normalizedinttype) + base_int, normalizedinttype, longlongmask) from pypy.rlib.objectmodel import Symbolic from pypy.tool.uid import Hashable from pypy.tool.identity_dict import identity_dict @@ -654,6 +654,9 @@ _numbertypes = {int: Number("Signed", int, intmask)} _numbertypes[r_int] = _numbertypes[int] +if r_longlong is not r_int: + _numbertypes[r_longlong] = Number("SignedLongLong", r_longlong, + longlongmask) def build_number(name, type): try: From noreply at buildbot.pypy.org Mon Aug 1 14:37:09 2011 From: noreply at buildbot.pypy.org (RonnyPfannschmidt) Date: Mon, 1 Aug 2011 14:37:09 +0200 (CEST) Subject: [pypy-commit] buildbot default: bbhook: warning comment and parameter fix for the host param of app.run Message-ID: <20110801123709.D29E0822AB@wyvern.cs.uni-duesseldorf.de> Author: Ronny Pfannschmidt Branch: Changeset: r544:8f7057902356 Date: 2011-08-01 12:28 +0200 http://bitbucket.org/pypy/buildbot/changeset/8f7057902356/ Log: bbhook: warning comment and parameter fix for the host param of app.run diff --git a/bbhook/run.py b/bbhook/run.py --- a/bbhook/run.py +++ b/bbhook/run.py @@ -16,8 +16,11 @@ #HOST_NAME = 'wyvern.cs.uni-duesseldorf.de' HOST_NAME = '' PORT_NUMBER = 9237 + # WARNING: + # deply is meant as a argument for running public facing, + # its not supposed to be used when running a proxy setup main.app.run( - host = HOST_NAME if 'deploy' in sys.argv else 'localhost', + host = HOST_NAME if 'deploy' in sys.argv else '127.0.0.1', debug = 'debug' in sys.argv, port=PORT_NUMBER) From noreply at buildbot.pypy.org Mon Aug 1 15:28:02 2011 From: noreply at buildbot.pypy.org (arigo) Date: Mon, 1 Aug 2011 15:28:02 +0200 (CEST) Subject: [pypy-commit] pypy default: Backout changesets 1c74e688ab5b, 73cd9edd4a03, ae9804777ad3, Message-ID: <20110801132802.591E7822AB@wyvern.cs.uni-duesseldorf.de> Author: Armin Rigo Branch: Changeset: r46141:5e69647bc0c2 Date: 2011-08-01 14:44 +0200 http://bitbucket.org/pypy/pypy/changeset/5e69647bc0c2/ Log: Backout changesets 1c74e688ab5b, 73cd9edd4a03, ae9804777ad3, a5246fac14ee. diff --git a/pypy/module/_rawffi/interp_rawffi.py b/pypy/module/_rawffi/interp_rawffi.py --- a/pypy/module/_rawffi/interp_rawffi.py +++ b/pypy/module/_rawffi/interp_rawffi.py @@ -268,7 +268,7 @@ self.ll_buffer = rffi.cast(rffi.VOIDP, address) else: self.ll_buffer = lltype.malloc(rffi.VOIDP.TO, size, flavor='raw', - zero=True, track_allocation=False) + zero=True) if tracker.DO_TRACING: ll_buf = rffi.cast(lltype.Signed, self.ll_buffer) tracker.trace_allocation(ll_buf, self) @@ -291,7 +291,7 @@ if tracker.DO_TRACING: ll_buf = rffi.cast(lltype.Signed, self.ll_buffer) tracker.trace_free(ll_buf) - lltype.free(self.ll_buffer, flavor='raw', track_allocation=False) + lltype.free(self.ll_buffer, flavor='raw') self.ll_buffer = lltype.nullptr(rffi.VOIDP.TO) def descr_buffer(self, space): 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 @@ -226,8 +226,7 @@ some += size >> 3 self.allocated = size + some new_buffer = lltype.malloc(mytype.arraytype, - self.allocated, flavor='raw', - track_allocation=False) + self.allocated, flavor='raw') for i in range(min(size, self.len)): new_buffer[i] = self.buffer[i] else: @@ -239,7 +238,7 @@ new_buffer = lltype.nullptr(mytype.arraytype) if self.buffer: - lltype.free(self.buffer, flavor='raw', track_allocation=False) + lltype.free(self.buffer, flavor='raw') self.buffer = new_buffer self.len = size diff --git a/pypy/module/micronumpy/interp_numarray.py b/pypy/module/micronumpy/interp_numarray.py --- a/pypy/module/micronumpy/interp_numarray.py +++ b/pypy/module/micronumpy/interp_numarray.py @@ -506,7 +506,7 @@ self._sliceloop2(start, stop, step, arr, self) def __del__(self): - lltype.free(self.storage, flavor='raw', track_allocation=False) + lltype.free(self.storage, flavor='raw') def new_numarray(space, w_size_or_iterable): l = space.listview(w_size_or_iterable) From noreply at buildbot.pypy.org Mon Aug 1 15:28:03 2011 From: noreply at buildbot.pypy.org (arigo) Date: Mon, 1 Aug 2011 15:28:03 +0200 (CEST) Subject: [pypy-commit] pypy default: Add an explicit flag 'add_memory_pressure=True' to the Message-ID: <20110801132803.9FD00822AB@wyvern.cs.uni-duesseldorf.de> Author: Armin Rigo Branch: Changeset: r46142:5282016589ed Date: 2011-08-01 15:18 +0200 http://bitbucket.org/pypy/pypy/changeset/5282016589ed/ Log: Add an explicit flag 'add_memory_pressure=True' to the raw-flavored malloc(). When we set it, the minimark GC will count the allocated memory as part of the next major collection's threshold. diff --git a/pypy/annotation/builtin.py b/pypy/annotation/builtin.py --- a/pypy/annotation/builtin.py +++ b/pypy/annotation/builtin.py @@ -416,7 +416,8 @@ from pypy.annotation.model import SomePtr from pypy.rpython.lltypesystem import lltype -def malloc(s_T, s_n=None, s_flavor=None, s_zero=None, s_track_allocation=None): +def malloc(s_T, s_n=None, s_flavor=None, s_zero=None, s_track_allocation=None, + s_add_memory_pressure=None): assert (s_n is None or s_n.knowntype == int or issubclass(s_n.knowntype, pypy.rlib.rarithmetic.base_int)) assert s_T.is_constant() @@ -432,6 +433,8 @@ else: assert s_flavor.is_constant() assert s_track_allocation is None or s_track_allocation.is_constant() + assert (s_add_memory_pressure is None or + s_add_memory_pressure.is_constant()) # not sure how to call malloc() for the example 'p' in the # presence of s_extraargs r = SomePtr(lltype.Ptr(s_T.const)) diff --git a/pypy/module/_rawffi/interp_rawffi.py b/pypy/module/_rawffi/interp_rawffi.py --- a/pypy/module/_rawffi/interp_rawffi.py +++ b/pypy/module/_rawffi/interp_rawffi.py @@ -268,7 +268,7 @@ self.ll_buffer = rffi.cast(rffi.VOIDP, address) else: self.ll_buffer = lltype.malloc(rffi.VOIDP.TO, size, flavor='raw', - zero=True) + zero=True, add_memory_pressure=True) if tracker.DO_TRACING: ll_buf = rffi.cast(lltype.Signed, self.ll_buffer) tracker.trace_allocation(ll_buf, self) 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 @@ -226,7 +226,8 @@ some += size >> 3 self.allocated = size + some new_buffer = lltype.malloc(mytype.arraytype, - self.allocated, flavor='raw') + self.allocated, flavor='raw', + add_memory_pressure=True) for i in range(min(size, self.len)): new_buffer[i] = self.buffer[i] else: diff --git a/pypy/module/micronumpy/interp_numarray.py b/pypy/module/micronumpy/interp_numarray.py --- a/pypy/module/micronumpy/interp_numarray.py +++ b/pypy/module/micronumpy/interp_numarray.py @@ -477,7 +477,8 @@ BaseArray.__init__(self) self.size = size self.storage = lltype.malloc(TP, size, zero=True, - flavor='raw', track_allocation=False) + flavor='raw', track_allocation=False, + add_memory_pressure=True) # XXX find out why test_zjit explodes with trackign of allocations def get_concrete(self): @@ -506,7 +507,7 @@ self._sliceloop2(start, stop, step, arr, self) def __del__(self): - lltype.free(self.storage, flavor='raw') + lltype.free(self.storage, flavor='raw', track_allocation=False) def new_numarray(space, w_size_or_iterable): l = space.listview(w_size_or_iterable) diff --git a/pypy/rpython/lltypesystem/lltype.py b/pypy/rpython/lltypesystem/lltype.py --- a/pypy/rpython/lltypesystem/lltype.py +++ b/pypy/rpython/lltypesystem/lltype.py @@ -1939,7 +1939,7 @@ def malloc(T, n=None, flavor='gc', immortal=False, zero=False, - track_allocation=True): + track_allocation=True, add_memory_pressure=False): assert flavor in ('gc', 'raw') if zero or immortal: initialization = 'example' diff --git a/pypy/rpython/memory/gc/minimark.py b/pypy/rpython/memory/gc/minimark.py --- a/pypy/rpython/memory/gc/minimark.py +++ b/pypy/rpython/memory/gc/minimark.py @@ -729,7 +729,7 @@ if self.max_heap_size < self.next_major_collection_threshold: self.next_major_collection_threshold = self.max_heap_size - def raw_malloc_varsize_hint(self, sizehint): + def raw_malloc_memory_pressure(self, sizehint): self.next_major_collection_threshold -= sizehint if self.next_major_collection_threshold < 0: # cannot trigger a full collection now, but we can ensure diff --git a/pypy/rpython/memory/gctransform/framework.py b/pypy/rpython/memory/gctransform/framework.py --- a/pypy/rpython/memory/gctransform/framework.py +++ b/pypy/rpython/memory/gctransform/framework.py @@ -386,15 +386,15 @@ else: self.malloc_varsize_nonmovable_ptr = None - if getattr(GCClass, 'raw_malloc_varsize_hint', False): - def raw_malloc_varsize_hint(length, itemsize): + if getattr(GCClass, 'raw_malloc_memory_pressure', False): + def raw_malloc_memory_pressure(length, itemsize): totalmem = length * itemsize if totalmem > 0: - gcdata.gc.raw_malloc_varsize_hint(totalmem) + gcdata.gc.raw_malloc_memory_pressure(totalmem) #else: probably an overflow -- the following rawmalloc # will fail then - self.raw_malloc_varsize_hint_ptr = getfn( - raw_malloc_varsize_hint, + self.raw_malloc_memory_pressure_ptr = getfn( + raw_malloc_memory_pressure, [annmodel.SomeInteger(), annmodel.SomeInteger()], annmodel.s_None, minimal_transform = False) diff --git a/pypy/rpython/memory/gctransform/transform.py b/pypy/rpython/memory/gctransform/transform.py --- a/pypy/rpython/memory/gctransform/transform.py +++ b/pypy/rpython/memory/gctransform/transform.py @@ -590,15 +590,10 @@ def gct_fv_raw_malloc_varsize(self, hop, flags, TYPE, v_length, c_const_size, c_item_size, c_offset_to_length): - track_allocation = flags.get('track_allocation', True) - if not track_allocation: - # idea: raw mallocs with track_allocation=False correspond - # generally to raw mallocs of stuff that we store in GC objects. - # So we tell the GC about such raw mallocs, so that it can - # adjust its total size estimate. - if hasattr(self, 'raw_malloc_varsize_hint_ptr'): + if flags.get('add_memory_pressure', False): + if hasattr(self, 'raw_malloc_memory_pressure_ptr'): hop.genop("direct_call", - [self.raw_malloc_varsize_hint_ptr, + [self.raw_malloc_memory_pressure_ptr, v_length, c_item_size]) if c_offset_to_length is None: if flags.get('zero'): @@ -615,7 +610,7 @@ [self.raw_malloc_varsize_ptr, v_length, c_const_size, c_item_size, c_offset_to_length], resulttype=llmemory.Address) - if track_allocation: + if flags.get('track_allocation', True): hop.genop("track_alloc_start", [v_raw]) return v_raw diff --git a/pypy/rpython/rbuiltin.py b/pypy/rpython/rbuiltin.py --- a/pypy/rpython/rbuiltin.py +++ b/pypy/rpython/rbuiltin.py @@ -345,14 +345,17 @@ BUILTIN_TYPER[object.__init__] = rtype_object__init__ # annotation of low-level types -def rtype_malloc(hop, i_flavor=None, i_zero=None, i_track_allocation=None): +def rtype_malloc(hop, i_flavor=None, i_zero=None, i_track_allocation=None, + i_add_memory_pressure=None): assert hop.args_s[0].is_constant() vlist = [hop.inputarg(lltype.Void, arg=0)] opname = 'malloc' - v_flavor, v_zero, v_track_allocation = parse_kwds(hop, + v_flavor, v_zero, v_track_allocation, v_add_memory_pressure = parse_kwds( + hop, (i_flavor, lltype.Void), (i_zero, None), - (i_track_allocation, None)) + (i_track_allocation, None), + (i_add_memory_pressure, None)) flags = {'flavor': 'gc'} if v_flavor is not None: @@ -361,8 +364,11 @@ flags['zero'] = v_zero.value if i_track_allocation is not None: flags['track_allocation'] = v_track_allocation.value + if i_add_memory_pressure is not None: + flags['add_memory_pressure'] = v_add_memory_pressure.value vlist.append(hop.inputconst(lltype.Void, flags)) - + + assert 1 <= hop.nb_args <= 2 if hop.nb_args == 2: vlist.append(hop.inputarg(lltype.Signed, arg=1)) opname += '_varsize' diff --git a/pypy/translator/c/test/test_newgc.py b/pypy/translator/c/test/test_newgc.py --- a/pypy/translator/c/test/test_newgc.py +++ b/pypy/translator/c/test/test_newgc.py @@ -1396,9 +1396,10 @@ class A: def __init__(self, n): self.buf = lltype.malloc(ARRAY, n, flavor='raw', - track_allocation=False) + add_memory_pressure=True) def __del__(self): - lltype.free(self.buf, flavor='raw', track_allocation=False) + lltype.free(self.buf, flavor='raw') + A(6) def f(): # allocate a total of ~77GB, but if the automatic gc'ing works, # it should never need more than a few MBs at once From noreply at buildbot.pypy.org Mon Aug 1 16:18:34 2011 From: noreply at buildbot.pypy.org (arigo) Date: Mon, 1 Aug 2011 16:18:34 +0200 (CEST) Subject: [pypy-commit] pypy default: Minor simplification of the interface. Message-ID: <20110801141834.9E6BA822AC@wyvern.cs.uni-duesseldorf.de> Author: Armin Rigo Branch: Changeset: r46144:801f2ddd52be Date: 2011-07-31 18:19 +0200 http://bitbucket.org/pypy/pypy/changeset/801f2ddd52be/ Log: Minor simplification of the interface. diff --git a/pypy/module/_multibytecodec/c_codecs.py b/pypy/module/_multibytecodec/c_codecs.py --- a/pypy/module/_multibytecodec/c_codecs.py +++ b/pypy/module/_multibytecodec/c_codecs.py @@ -126,7 +126,7 @@ pypy_cjk_dec_free(decodebuf) def decodeex(decodebuf, stringdata, errors="strict", errorcb=None, namecb=None, - incompletepos=None): + ignore_error=0): inleft = len(stringdata) inbuf = rffi.get_nonmovingbuffer(stringdata) try: @@ -134,10 +134,7 @@ raise MemoryError while True: r = pypy_cjk_dec_chunk(decodebuf) - if r == 0: - break - if incompletepos is not None and r == MBERR_TOOFEW: - incompletepos[0] = pypy_cjk_dec_inbuf_consumed(decodebuf) + if r == 0 or r == ignore_error: break multibytecodec_decerror(decodebuf, r, errors, errorcb, namecb, stringdata) diff --git a/pypy/module/_multibytecodec/test/test_c_codecs.py b/pypy/module/_multibytecodec/test/test_c_codecs.py --- a/pypy/module/_multibytecodec/test/test_c_codecs.py +++ b/pypy/module/_multibytecodec/test/test_c_codecs.py @@ -62,10 +62,11 @@ u'\u73b7', # h ]): buf += c - incompletepos = [len(buf)] - u = c_codecs.decodeex(decodebuf, buf, incompletepos=incompletepos) + u = c_codecs.decodeex(decodebuf, buf, + ignore_error = c_codecs.MBERR_TOOFEW) assert u == output - buf = buf[incompletepos[0]:] + incompletepos = c_codecs.pypy_cjk_dec_inbuf_consumed(decodebuf) + buf = buf[incompletepos:] assert buf == '' c_codecs.pypy_cjk_dec_free(decodebuf) From noreply at buildbot.pypy.org Mon Aug 1 16:18:33 2011 From: noreply at buildbot.pypy.org (arigo) Date: Mon, 1 Aug 2011 16:18:33 +0200 (CEST) Subject: [pypy-commit] pypy default: Incremental support: keep the decodebuf around several calls to Message-ID: <20110801141833.6AEF2822AB@wyvern.cs.uni-duesseldorf.de> Author: Armin Rigo Branch: Changeset: r46143:f040a9a3f4fb Date: 2011-07-31 18:16 +0200 http://bitbucket.org/pypy/pypy/changeset/f040a9a3f4fb/ Log: Incremental support: keep the decodebuf around several calls to decodeex(), and don't complain when getting MBERR_TOOFEW. diff --git a/pypy/module/_multibytecodec/c_codecs.py b/pypy/module/_multibytecodec/c_codecs.py --- a/pypy/module/_multibytecodec/c_codecs.py +++ b/pypy/module/_multibytecodec/c_codecs.py @@ -52,11 +52,13 @@ includes = ['src/cjkcodecs/multibytecodec.h'], include_dirs = [str(srcdir)], export_symbols = [ + "pypy_cjk_dec_new", "pypy_cjk_dec_init", "pypy_cjk_dec_free", "pypy_cjk_dec_chunk", "pypy_cjk_dec_outbuf", "pypy_cjk_dec_outlen", "pypy_cjk_dec_inbuf_remaining", "pypy_cjk_dec_inbuf_consumed", "pypy_cjk_dec_replace_on_error", + "pypy_cjk_enc_new", "pypy_cjk_enc_init", "pypy_cjk_enc_free", "pypy_cjk_enc_chunk", "pypy_cjk_enc_reset", "pypy_cjk_enc_outbuf", "pypy_cjk_enc_outlen", "pypy_cjk_enc_inbuf_remaining", "pypy_cjk_enc_inbuf_consumed", @@ -92,9 +94,11 @@ # Decoding DECODEBUF_P = rffi.COpaquePtr('struct pypy_cjk_dec_s', compilation_info=eci) +pypy_cjk_dec_new = llexternal('pypy_cjk_dec_new', + [MULTIBYTECODEC_P], DECODEBUF_P) pypy_cjk_dec_init = llexternal('pypy_cjk_dec_init', - [MULTIBYTECODEC_P, rffi.CCHARP, rffi.SSIZE_T], - DECODEBUF_P) + [DECODEBUF_P, rffi.CCHARP, rffi.SSIZE_T], + rffi.SSIZE_T) pypy_cjk_dec_free = llexternal('pypy_cjk_dec_free', [DECODEBUF_P], lltype.Void) pypy_cjk_dec_chunk = llexternal('pypy_cjk_dec_chunk', [DECODEBUF_P], @@ -113,25 +117,33 @@ rffi.SSIZE_T) def decode(codec, stringdata, errors="strict", errorcb=None, namecb=None): + decodebuf = pypy_cjk_dec_new(codec) + if not decodebuf: + raise MemoryError + try: + return decodeex(decodebuf, stringdata, errors, errorcb, namecb) + finally: + pypy_cjk_dec_free(decodebuf) + +def decodeex(decodebuf, stringdata, errors="strict", errorcb=None, namecb=None, + incompletepos=None): inleft = len(stringdata) inbuf = rffi.get_nonmovingbuffer(stringdata) try: - decodebuf = pypy_cjk_dec_init(codec, inbuf, inleft) - if not decodebuf: + if pypy_cjk_dec_init(decodebuf, inbuf, inleft) < 0: raise MemoryError - try: - while True: - r = pypy_cjk_dec_chunk(decodebuf) - if r == 0: - break - multibytecodec_decerror(decodebuf, r, errors, - errorcb, namecb, stringdata) - src = pypy_cjk_dec_outbuf(decodebuf) - length = pypy_cjk_dec_outlen(decodebuf) - return rffi.wcharpsize2unicode(src, length) - # - finally: - pypy_cjk_dec_free(decodebuf) + while True: + r = pypy_cjk_dec_chunk(decodebuf) + if r == 0: + break + if incompletepos is not None and r == MBERR_TOOFEW: + incompletepos[0] = pypy_cjk_dec_inbuf_consumed(decodebuf) + break + multibytecodec_decerror(decodebuf, r, errors, + errorcb, namecb, stringdata) + src = pypy_cjk_dec_outbuf(decodebuf) + length = pypy_cjk_dec_outlen(decodebuf) + return rffi.wcharpsize2unicode(src, length) # finally: rffi.free_nonmovingbuffer(stringdata, inbuf) diff --git a/pypy/module/_multibytecodec/test/test_c_codecs.py b/pypy/module/_multibytecodec/test/test_c_codecs.py --- a/pypy/module/_multibytecodec/test/test_c_codecs.py +++ b/pypy/module/_multibytecodec/test/test_c_codecs.py @@ -2,6 +2,7 @@ from pypy.module._multibytecodec.c_codecs import getcodec, codecs from pypy.module._multibytecodec.c_codecs import decode, encode from pypy.module._multibytecodec.c_codecs import EncodeDecodeError +from pypy.module._multibytecodec import c_codecs def test_codecs_existence(): @@ -22,6 +23,51 @@ c = getcodec("hz") u = decode(c, "~{abc}") assert u == u'\u5f95\u6cef' + u = decode(c, "~{") + assert u == u'' + +def test_decodeex_hz(): + c = getcodec("hz") + decodebuf = c_codecs.pypy_cjk_dec_new(c) + u = c_codecs.decodeex(decodebuf, "~{abcd~}") + assert u == u'\u5f95\u6c85' + u = c_codecs.decodeex(decodebuf, "~{efgh~}") + assert u == u'\u5f50\u73b7' + u = c_codecs.decodeex(decodebuf, "!~{abcd~}xyz~{efgh") + assert u == u'!\u5f95\u6c85xyz\u5f50\u73b7' + c_codecs.pypy_cjk_dec_free(decodebuf) + +def test_decodeex_hz_incomplete(): + c = getcodec("hz") + decodebuf = c_codecs.pypy_cjk_dec_new(c) + buf = '' + for c, output in zip("!~{abcd~}xyz~{efgh", + [u'!', # ! + u'', # ~ + u'', # { + u'', # a + u'\u5f95', # b + u'', # c + u'\u6c85', # d + u'', # ~ + u'', # } + u'x', # x + u'y', # y + u'z', # z + u'', # ~ + u'', # { + u'', # e + u'\u5f50', # f + u'', # g + u'\u73b7', # h + ]): + buf += c + incompletepos = [len(buf)] + u = c_codecs.decodeex(decodebuf, buf, incompletepos=incompletepos) + assert u == output + buf = buf[incompletepos[0]:] + assert buf == '' + c_codecs.pypy_cjk_dec_free(decodebuf) def test_decode_hz_error(): # error diff --git a/pypy/translator/c/src/cjkcodecs/multibytecodec.c b/pypy/translator/c/src/cjkcodecs/multibytecodec.c --- a/pypy/translator/c/src/cjkcodecs/multibytecodec.c +++ b/pypy/translator/c/src/cjkcodecs/multibytecodec.c @@ -3,31 +3,38 @@ #include "src/cjkcodecs/multibytecodec.h" -struct pypy_cjk_dec_s *pypy_cjk_dec_init(const MultibyteCodec *codec, - char *inbuf, Py_ssize_t inlen) +struct pypy_cjk_dec_s *pypy_cjk_dec_new(const MultibyteCodec *codec) { struct pypy_cjk_dec_s *d = malloc(sizeof(struct pypy_cjk_dec_s)); if (!d) return NULL; if (codec->decinit != NULL && codec->decinit(&d->state, codec->config) != 0) - goto errorexit; + { + free(d); + return NULL; + } + d->codec = codec; + d->outbuf_start = NULL; + return d; +} - d->codec = codec; +Py_ssize_t pypy_cjk_dec_init(struct pypy_cjk_dec_s *d, + char *inbuf, Py_ssize_t inlen) +{ d->inbuf_start = inbuf; d->inbuf = inbuf; d->inbuf_end = inbuf + inlen; - d->outbuf_start = (inlen <= (PY_SSIZE_T_MAX / sizeof(Py_UNICODE)) ? - malloc(inlen * sizeof(Py_UNICODE)) : - NULL); - if (!d->outbuf_start) - goto errorexit; + if (d->outbuf_start == NULL) + { + d->outbuf_start = (inlen <= (PY_SSIZE_T_MAX / sizeof(Py_UNICODE)) ? + malloc(inlen * sizeof(Py_UNICODE)) : + NULL); + if (d->outbuf_start == NULL) + return -1; + d->outbuf_end = d->outbuf_start + inlen; + } d->outbuf = d->outbuf_start; - d->outbuf_end = d->outbuf_start + inlen; - return d; - - errorexit: - free(d); - return NULL; + return 0; } void pypy_cjk_dec_free(struct pypy_cjk_dec_s *d) diff --git a/pypy/translator/c/src/cjkcodecs/multibytecodec.h b/pypy/translator/c/src/cjkcodecs/multibytecodec.h --- a/pypy/translator/c/src/cjkcodecs/multibytecodec.h +++ b/pypy/translator/c/src/cjkcodecs/multibytecodec.h @@ -94,8 +94,9 @@ Py_UNICODE *outbuf_start, *outbuf, *outbuf_end; }; -struct pypy_cjk_dec_s *pypy_cjk_dec_init(const MultibyteCodec *codec, - char *inbuf, Py_ssize_t inlen); +struct pypy_cjk_dec_s *pypy_cjk_dec_new(const MultibyteCodec *codec); +Py_ssize_t pypy_cjk_dec_init(struct pypy_cjk_dec_s *d, + char *inbuf, Py_ssize_t inlen); void pypy_cjk_dec_free(struct pypy_cjk_dec_s *); Py_ssize_t pypy_cjk_dec_chunk(struct pypy_cjk_dec_s *); Py_UNICODE *pypy_cjk_dec_outbuf(struct pypy_cjk_dec_s *); From noreply at buildbot.pypy.org Mon Aug 1 16:18:35 2011 From: noreply at buildbot.pypy.org (arigo) Date: Mon, 1 Aug 2011 16:18:35 +0200 (CEST) Subject: [pypy-commit] pypy default: MultibyteIncrementalDecoder. Message-ID: <20110801141835.D5A33822AB@wyvern.cs.uni-duesseldorf.de> Author: Armin Rigo Branch: Changeset: r46145:8949a315da3f Date: 2011-07-31 18:34 +0200 http://bitbucket.org/pypy/pypy/changeset/8949a315da3f/ Log: MultibyteIncrementalDecoder. diff --git a/pypy/module/_multibytecodec/__init__.py b/pypy/module/_multibytecodec/__init__.py --- a/pypy/module/_multibytecodec/__init__.py +++ b/pypy/module/_multibytecodec/__init__.py @@ -7,13 +7,14 @@ # for compatibility this name is obscured, and should be called # via the _codecs_*.py modules written in lib_pypy. '__getcodec': 'interp_multibytecodec.getcodec', + + 'MultibyteIncrementalDecoder': + 'interp_incremental.MultibyteIncrementalDecoder', } appleveldefs = { 'MultibyteIncrementalEncoder': 'app_multibytecodec.MultibyteIncrementalEncoder', - 'MultibyteIncrementalDecoder': - 'app_multibytecodec.MultibyteIncrementalDecoder', 'MultibyteStreamReader': 'app_multibytecodec.MultibyteStreamReader', 'MultibyteStreamWriter': diff --git a/pypy/module/_multibytecodec/interp_incremental.py b/pypy/module/_multibytecodec/interp_incremental.py new file mode 100644 --- /dev/null +++ b/pypy/module/_multibytecodec/interp_incremental.py @@ -0,0 +1,80 @@ +from pypy.rpython.lltypesystem import lltype +from pypy.module._multibytecodec import c_codecs +from pypy.module._multibytecodec.interp_multibytecodec import ( + MultibyteCodec, wrap_unicodedecodeerror, wrap_runtimeerror) +from pypy.interpreter.baseobjspace import Wrappable +from pypy.interpreter.gateway import interp2app, unwrap_spec +from pypy.interpreter.typedef import TypeDef +from pypy.module._codecs.interp_codecs import CodecState + + +class MultibyteIncrementalDecoder(Wrappable): + + def __init__(self, space, errors): + if errors is None: + errors = 'strict' + self.space = space + self.errors = errors + w_codec = space.getattr(space.wrap(self), space.wrap("codec")) + codec = space.interp_w(MultibyteCodec, w_codec) + self.codec = codec.codec + self.name = codec.name + self._initialize() + + def _initialize(self): + self.decodebuf = c_codecs.pypy_cjk_dec_new(self.codec) + self.pending = "" + + def _free(self): + self.pending = None + if self.decodebuf: + pypy_cjk_dec_free(self.decodebuf) + self.decodebuf = lltype.nullptr(DECODEBUF_P.TO) + + def __del__(self): + self._free() + + def reset_w(self): + self._free() + self._initialize() + + @unwrap_spec(object=str, final=bool) + def decode_w(self, object, final=False): + space = self.space + state = space.fromcache(CodecState) + if len(self.pending) > 0: + object = self.pending + object + try: + output = c_codecs.decodeex(self.decodebuf, object, self.errors, + state.decode_error_handler, self.name, + get_ignore_error(final)) + except c_codecs.EncodeDecodeError, e: + raise wrap_unicodedecodeerror(space, e, object, self.name) + except RuntimeError: + raise wrap_runtimeerror(space) + pos = c_codecs.pypy_cjk_dec_inbuf_consumed(self.decodebuf) + assert 0 <= pos <= len(object) + self.pending = object[pos:] + return space.wrap(output) + + + at unwrap_spec(errors="str_or_None") +def mbidecoder_new(space, w_subtype, errors=None): + r = space.allocate_instance(MultibyteIncrementalDecoder, w_subtype) + r.__init__(space, errors) + return space.wrap(r) + +MultibyteIncrementalDecoder.typedef = TypeDef( + 'MultibyteIncrementalDecoder', + __module__ = '_multibytecodec', + __new__ = interp2app(mbidecoder_new), + decode = interp2app(MultibyteIncrementalDecoder.decode_w), + reset = interp2app(MultibyteIncrementalDecoder.reset_w), + ) + + +def get_ignore_error(final): + if final: + return 0 # don't ignore any error + else: + return c_codecs.MBERR_TOOFEW diff --git a/pypy/module/_multibytecodec/interp_multibytecodec.py b/pypy/module/_multibytecodec/interp_multibytecodec.py --- a/pypy/module/_multibytecodec/interp_multibytecodec.py +++ b/pypy/module/_multibytecodec/interp_multibytecodec.py @@ -22,17 +22,9 @@ output = c_codecs.decode(self.codec, input, errors, state.decode_error_handler, self.name) except c_codecs.EncodeDecodeError, e: - raise OperationError( - space.w_UnicodeDecodeError, - space.newtuple([ - space.wrap(self.name), - space.wrap(input), - space.wrap(e.start), - space.wrap(e.end), - space.wrap(e.reason)])) + raise wrap_unicodedecodeerror(space, e, input, self.name) except RuntimeError: - raise OperationError(space.w_RuntimeError, - space.wrap("internal codec error")) + raise wrap_runtimeerror(space) return space.newtuple([space.wrap(output), space.wrap(len(input))]) @@ -46,17 +38,9 @@ output = c_codecs.encode(self.codec, input, errors, state.encode_error_handler, self.name) except c_codecs.EncodeDecodeError, e: - raise OperationError( - space.w_UnicodeEncodeError, - space.newtuple([ - space.wrap(self.name), - space.wrap(input), - space.wrap(e.start), - space.wrap(e.end), - space.wrap(e.reason)])) + raise wrap_unicodeencodeerror(space, e, input, self.name) except RuntimeError: - raise OperationError(space.w_RuntimeError, - space.wrap("internal codec error")) + raise wrap_runtimeerror(space) return space.newtuple([space.wrap(output), space.wrap(len(input))]) @@ -78,3 +62,28 @@ raise OperationError(space.w_LookupError, space.wrap("no such codec is supported.")) return space.wrap(MultibyteCodec(name, codec)) + + +def wrap_unicodedecodeerror(space, e, input, name): + return OperationError( + space.w_UnicodeDecodeError, + space.newtuple([ + space.wrap(name), + space.wrap(input), + space.wrap(e.start), + space.wrap(e.end), + space.wrap(e.reason)])) + +def wrap_unicodeencodeerror(space, e, input, name): + raise OperationError( + space.w_UnicodeEncodeError, + space.newtuple([ + space.wrap(name), + space.wrap(input), + space.wrap(e.start), + space.wrap(e.end), + space.wrap(e.reason)])) + +def wrap_runtimeerror(space): + raise OperationError(space.w_RuntimeError, + space.wrap("internal codec error")) diff --git a/pypy/module/_multibytecodec/test/test_app_incremental.py b/pypy/module/_multibytecodec/test/test_app_incremental.py new file mode 100644 --- /dev/null +++ b/pypy/module/_multibytecodec/test/test_app_incremental.py @@ -0,0 +1,51 @@ +from pypy.conftest import gettestobjspace + + +class AppTestClasses: + def setup_class(cls): + cls.space = gettestobjspace(usemodules=['_multibytecodec']) + cls.w_IncrementalHzDecoder = cls.space.appexec([], """(): + import _codecs_cn + from _multibytecodec import MultibyteIncrementalDecoder + + class IncrementalHzDecoder(MultibyteIncrementalDecoder): + codec = _codecs_cn.getcodec('hz') + + return IncrementalHzDecoder + """) + + def test_decode_hz(self): + d = self.IncrementalHzDecoder() + r = d.decode("~{abcd~}") + assert r == u'\u5f95\u6c85' + r = d.decode("~{efgh~}") + assert r == u'\u5f50\u73b7' + for c, output in zip("!~{abcd~}xyz~{efgh", + [u'!', # ! + u'', # ~ + u'', # { + u'', # a + u'\u5f95', # b + u'', # c + u'\u6c85', # d + u'', # ~ + u'', # } + u'x', # x + u'y', # y + u'z', # z + u'', # ~ + u'', # { + u'', # e + u'\u5f50', # f + u'', # g + u'\u73b7', # h + ]): + r = d.decode(c) + assert r == output + + def test_decode_hz_final(self): + d = self.IncrementalHzDecoder() + r = d.decode("~{", True) + assert r == u'' + raises(UnicodeDecodeError, d.decode, "~", True) + raises(UnicodeDecodeError, d.decode, "~{a", True) From noreply at buildbot.pypy.org Mon Aug 1 16:18:37 2011 From: noreply at buildbot.pypy.org (arigo) Date: Mon, 1 Aug 2011 16:18:37 +0200 (CEST) Subject: [pypy-commit] pypy default: Test and fix for reset(). Message-ID: <20110801141837.127DA822AB@wyvern.cs.uni-duesseldorf.de> Author: Armin Rigo Branch: Changeset: r46146:368b4ab4862f Date: 2011-07-31 18:37 +0200 http://bitbucket.org/pypy/pypy/changeset/368b4ab4862f/ Log: Test and fix for reset(). diff --git a/pypy/module/_multibytecodec/interp_incremental.py b/pypy/module/_multibytecodec/interp_incremental.py --- a/pypy/module/_multibytecodec/interp_incremental.py +++ b/pypy/module/_multibytecodec/interp_incremental.py @@ -28,8 +28,8 @@ def _free(self): self.pending = None if self.decodebuf: - pypy_cjk_dec_free(self.decodebuf) - self.decodebuf = lltype.nullptr(DECODEBUF_P.TO) + c_codecs.pypy_cjk_dec_free(self.decodebuf) + self.decodebuf = lltype.nullptr(c_codecs.DECODEBUF_P.TO) def __del__(self): self._free() diff --git a/pypy/module/_multibytecodec/test/test_app_incremental.py b/pypy/module/_multibytecodec/test/test_app_incremental.py --- a/pypy/module/_multibytecodec/test/test_app_incremental.py +++ b/pypy/module/_multibytecodec/test/test_app_incremental.py @@ -49,3 +49,17 @@ assert r == u'' raises(UnicodeDecodeError, d.decode, "~", True) raises(UnicodeDecodeError, d.decode, "~{a", True) + + def test_decode_hz_reset(self): + d = self.IncrementalHzDecoder() + r = d.decode("ab") + assert r == u'ab' + r = d.decode("~{") + assert r == u'' + r = d.decode("ab") + assert r == u'\u5f95' + r = d.decode("ab") + assert r == u'\u5f95' + d.reset() + r = d.decode("ab") + assert r == u'ab' From noreply at buildbot.pypy.org Mon Aug 1 16:18:38 2011 From: noreply at buildbot.pypy.org (arigo) Date: Mon, 1 Aug 2011 16:18:38 +0200 (CEST) Subject: [pypy-commit] pypy default: Support custom errors. Message-ID: <20110801141838.44D9A822AB@wyvern.cs.uni-duesseldorf.de> Author: Armin Rigo Branch: Changeset: r46147:e1a78e27a180 Date: 2011-07-31 18:44 +0200 http://bitbucket.org/pypy/pypy/changeset/e1a78e27a180/ Log: Support custom errors. diff --git a/pypy/module/_multibytecodec/interp_incremental.py b/pypy/module/_multibytecodec/interp_incremental.py --- a/pypy/module/_multibytecodec/interp_incremental.py +++ b/pypy/module/_multibytecodec/interp_incremental.py @@ -4,7 +4,7 @@ MultibyteCodec, wrap_unicodedecodeerror, wrap_runtimeerror) from pypy.interpreter.baseobjspace import Wrappable from pypy.interpreter.gateway import interp2app, unwrap_spec -from pypy.interpreter.typedef import TypeDef +from pypy.interpreter.typedef import TypeDef, GetSetProperty from pypy.module._codecs.interp_codecs import CodecState @@ -57,6 +57,12 @@ self.pending = object[pos:] return space.wrap(output) + def fget_errors(self, space): + return space.wrap(self.errors) + + def fset_errors(self, space, w_errors): + self.errors = space.str_w(w_errors) + @unwrap_spec(errors="str_or_None") def mbidecoder_new(space, w_subtype, errors=None): @@ -70,6 +76,8 @@ __new__ = interp2app(mbidecoder_new), decode = interp2app(MultibyteIncrementalDecoder.decode_w), reset = interp2app(MultibyteIncrementalDecoder.reset_w), + errors = GetSetProperty(MultibyteIncrementalDecoder.fget_errors, + MultibyteIncrementalDecoder.fset_errors), ) diff --git a/pypy/module/_multibytecodec/test/test_app_incremental.py b/pypy/module/_multibytecodec/test/test_app_incremental.py --- a/pypy/module/_multibytecodec/test/test_app_incremental.py +++ b/pypy/module/_multibytecodec/test/test_app_incremental.py @@ -63,3 +63,14 @@ d.reset() r = d.decode("ab") assert r == u'ab' + + def test_decode_hz_error(self): + d = self.IncrementalHzDecoder() + raises(UnicodeDecodeError, d.decode, "~{abc", True) + d = self.IncrementalHzDecoder("ignore") + r = d.decode("~{abc", True) + assert r == u'\u5f95' + d = self.IncrementalHzDecoder() + d.errors = "replace" + r = d.decode("~{abc", True) + assert r == u'\u5f95\ufffd' From noreply at buildbot.pypy.org Mon Aug 1 16:18:39 2011 From: noreply at buildbot.pypy.org (arigo) Date: Mon, 1 Aug 2011 16:18:39 +0200 (CEST) Subject: [pypy-commit] pypy default: Incremental encoder, first try. Message-ID: <20110801141839.7B145822AB@wyvern.cs.uni-duesseldorf.de> Author: Armin Rigo Branch: Changeset: r46148:67f047ef8c7a Date: 2011-08-01 13:16 +0200 http://bitbucket.org/pypy/pypy/changeset/67f047ef8c7a/ Log: Incremental encoder, first try. diff --git a/pypy/module/_multibytecodec/__init__.py b/pypy/module/_multibytecodec/__init__.py --- a/pypy/module/_multibytecodec/__init__.py +++ b/pypy/module/_multibytecodec/__init__.py @@ -10,11 +10,11 @@ 'MultibyteIncrementalDecoder': 'interp_incremental.MultibyteIncrementalDecoder', + 'MultibyteIncrementalEncoder': + 'interp_incremental.MultibyteIncrementalEncoder', } appleveldefs = { - 'MultibyteIncrementalEncoder': - 'app_multibytecodec.MultibyteIncrementalEncoder', 'MultibyteStreamReader': 'app_multibytecodec.MultibyteStreamReader', 'MultibyteStreamWriter': diff --git a/pypy/module/_multibytecodec/c_codecs.py b/pypy/module/_multibytecodec/c_codecs.py --- a/pypy/module/_multibytecodec/c_codecs.py +++ b/pypy/module/_multibytecodec/c_codecs.py @@ -183,9 +183,11 @@ # ____________________________________________________________ # Encoding ENCODEBUF_P = rffi.COpaquePtr('struct pypy_cjk_enc_s', compilation_info=eci) +pypy_cjk_enc_new = llexternal('pypy_cjk_enc_new', + [MULTIBYTECODEC_P], ENCODEBUF_P) pypy_cjk_enc_init = llexternal('pypy_cjk_enc_init', - [MULTIBYTECODEC_P, rffi.CWCHARP, rffi.SSIZE_T], - ENCODEBUF_P) + [ENCODEBUF_P, rffi.CWCHARP, rffi.SSIZE_T], + rffi.SSIZE_T) pypy_cjk_enc_free = llexternal('pypy_cjk_enc_free', [ENCODEBUF_P], lltype.Void) pypy_cjk_enc_chunk = llexternal('pypy_cjk_enc_chunk', [ENCODEBUF_P], @@ -204,39 +206,46 @@ [ENCODEBUF_P, rffi.CCHARP, rffi.SSIZE_T, rffi.SSIZE_T], rffi.SSIZE_T) +pypy_cjk_enc_getcodec = llexternal('pypy_cjk_enc_getcodec', + [ENCODEBUF_P], MULTIBYTECODEC_P) def encode(codec, unicodedata, errors="strict", errorcb=None, namecb=None): + encodebuf = pypy_cjk_enc_new(codec) + if not encodebuf: + raise MemoryError + try: + return encodeex(encodebuf, unicodedata, errors, errorcb, namecb) + finally: + pypy_cjk_enc_free(encodebuf) + +def encodeex(encodebuf, unicodedata, errors="strict", errorcb=None, + namecb=None, ignore_error=0): inleft = len(unicodedata) inbuf = rffi.get_nonmoving_unicodebuffer(unicodedata) try: - encodebuf = pypy_cjk_enc_init(codec, inbuf, inleft) - if not encodebuf: + if pypy_cjk_enc_init(encodebuf, inbuf, inleft) < 0: raise MemoryError - try: - while True: - r = pypy_cjk_enc_chunk(encodebuf) - if r == 0: - break - multibytecodec_encerror(encodebuf, r, errors, - codec, errorcb, namecb, unicodedata) - while True: - r = pypy_cjk_enc_reset(encodebuf) - if r == 0: - break - multibytecodec_encerror(encodebuf, r, errors, - codec, errorcb, namecb, unicodedata) - src = pypy_cjk_enc_outbuf(encodebuf) - length = pypy_cjk_enc_outlen(encodebuf) - return rffi.charpsize2str(src, length) - # - finally: - pypy_cjk_enc_free(encodebuf) + while True: + r = pypy_cjk_enc_chunk(encodebuf) + if r == 0 or r == ignore_error: + break + multibytecodec_encerror(encodebuf, r, errors, + errorcb, namecb, unicodedata) + while True: + r = pypy_cjk_enc_reset(encodebuf) + if r == 0: + break + multibytecodec_encerror(encodebuf, r, errors, + errorcb, namecb, unicodedata) + src = pypy_cjk_enc_outbuf(encodebuf) + length = pypy_cjk_enc_outlen(encodebuf) + return rffi.charpsize2str(src, length) # finally: rffi.free_nonmoving_unicodebuffer(unicodedata, inbuf) def multibytecodec_encerror(encodebuf, e, errors, - codec, errorcb, namecb, unicodedata): + errorcb, namecb, unicodedata): if e > 0: reason = "illegal multibyte sequence" esize = e @@ -257,6 +266,7 @@ elif errors == "ignore": replace = "" elif errors == "replace": + codec = pypy_cjk_enc_getcodec(encodebuf) try: replace = encode(codec, u"?") except EncodeDecodeError: diff --git a/pypy/module/_multibytecodec/interp_incremental.py b/pypy/module/_multibytecodec/interp_incremental.py --- a/pypy/module/_multibytecodec/interp_incremental.py +++ b/pypy/module/_multibytecodec/interp_incremental.py @@ -1,14 +1,15 @@ from pypy.rpython.lltypesystem import lltype from pypy.module._multibytecodec import c_codecs from pypy.module._multibytecodec.interp_multibytecodec import ( - MultibyteCodec, wrap_unicodedecodeerror, wrap_runtimeerror) + MultibyteCodec, wrap_unicodedecodeerror, wrap_runtimeerror, + wrap_unicodeencodeerror) from pypy.interpreter.baseobjspace import Wrappable from pypy.interpreter.gateway import interp2app, unwrap_spec from pypy.interpreter.typedef import TypeDef, GetSetProperty from pypy.module._codecs.interp_codecs import CodecState -class MultibyteIncrementalDecoder(Wrappable): +class MultibyteIncrementalBase(Wrappable): def __init__(self, space, errors): if errors is None: @@ -21,6 +22,22 @@ self.name = codec.name self._initialize() + def __del__(self): + self._free() + + def reset_w(self): + self._free() + self._initialize() + + def fget_errors(self, space): + return space.wrap(self.errors) + + def fset_errors(self, space, w_errors): + self.errors = space.str_w(w_errors) + + +class MultibyteIncrementalDecoder(MultibyteIncrementalBase): + def _initialize(self): self.decodebuf = c_codecs.pypy_cjk_dec_new(self.codec) self.pending = "" @@ -31,13 +48,6 @@ c_codecs.pypy_cjk_dec_free(self.decodebuf) self.decodebuf = lltype.nullptr(c_codecs.DECODEBUF_P.TO) - def __del__(self): - self._free() - - def reset_w(self): - self._free() - self._initialize() - @unwrap_spec(object=str, final=bool) def decode_w(self, object, final=False): space = self.space @@ -57,12 +67,6 @@ self.pending = object[pos:] return space.wrap(output) - def fget_errors(self, space): - return space.wrap(self.errors) - - def fset_errors(self, space, w_errors): - self.errors = space.str_w(w_errors) - @unwrap_spec(errors="str_or_None") def mbidecoder_new(space, w_subtype, errors=None): @@ -81,6 +85,55 @@ ) +class MultibyteIncrementalEncoder(MultibyteIncrementalBase): + + def _initialize(self): + self.encodebuf = c_codecs.pypy_cjk_enc_new(self.codec) + self.pending = u"" + + def _free(self): + self.pending = None + if self.encodebuf: + c_codecs.pypy_cjk_enc_free(self.encodebuf) + self.encodebuf = lltype.nullptr(c_codecs.ENCODEBUF_P.TO) + + @unwrap_spec(object=unicode, final=bool) + def encode_w(self, object, final=False): + space = self.space + state = space.fromcache(CodecState) + if len(self.pending) > 0: + object = self.pending + object + try: + output = c_codecs.encodeex(self.encodebuf, object, self.errors, + state.encode_error_handler, self.name, + get_ignore_error(final)) + except c_codecs.EncodeDecodeError, e: + raise wrap_unicodeencodeerror(space, e, object, self.name) + except RuntimeError: + raise wrap_runtimeerror(space) + pos = c_codecs.pypy_cjk_enc_inbuf_consumed(self.encodebuf) + assert 0 <= pos <= len(object) + self.pending = object[pos:] + return space.wrap(output) + + + at unwrap_spec(errors="str_or_None") +def mbiencoder_new(space, w_subtype, errors=None): + r = space.allocate_instance(MultibyteIncrementalEncoder, w_subtype) + r.__init__(space, errors) + return space.wrap(r) + +MultibyteIncrementalEncoder.typedef = TypeDef( + 'MultibyteIncrementalEncoder', + __module__ = '_multibytecodec', + __new__ = interp2app(mbiencoder_new), + encode = interp2app(MultibyteIncrementalEncoder.encode_w), + reset = interp2app(MultibyteIncrementalEncoder.reset_w), + errors = GetSetProperty(MultibyteIncrementalEncoder.fget_errors, + MultibyteIncrementalEncoder.fset_errors), + ) + + def get_ignore_error(final): if final: return 0 # don't ignore any error diff --git a/pypy/module/_multibytecodec/test/test_app_incremental.py b/pypy/module/_multibytecodec/test/test_app_incremental.py --- a/pypy/module/_multibytecodec/test/test_app_incremental.py +++ b/pypy/module/_multibytecodec/test/test_app_incremental.py @@ -13,6 +13,15 @@ return IncrementalHzDecoder """) + cls.w_IncrementalHzEncoder = cls.space.appexec([], """(): + import _codecs_cn + from _multibytecodec import MultibyteIncrementalEncoder + + class IncrementalHzEncoder(MultibyteIncrementalEncoder): + codec = _codecs_cn.getcodec('hz') + + return IncrementalHzEncoder + """) def test_decode_hz(self): d = self.IncrementalHzDecoder() @@ -74,3 +83,14 @@ d.errors = "replace" r = d.decode("~{abc", True) assert r == u'\u5f95\ufffd' + + def test_decode_hz_buffer_grow(self): + d = self.IncrementalHzDecoder() + for i in range(13): + r = d.decode("a" * (2**i)) + assert r == unicode("a" * (2**i)) + + def test_encode_hz(self): + e = self.IncrementalHzEncoder() + r = e.encode("abcd") + assert r == u'abcd' diff --git a/pypy/translator/c/src/cjkcodecs/multibytecodec.c b/pypy/translator/c/src/cjkcodecs/multibytecodec.c --- a/pypy/translator/c/src/cjkcodecs/multibytecodec.c +++ b/pypy/translator/c/src/cjkcodecs/multibytecodec.c @@ -119,34 +119,40 @@ /************************************************************/ -struct pypy_cjk_enc_s *pypy_cjk_enc_init(const MultibyteCodec *codec, - Py_UNICODE *inbuf, Py_ssize_t inlen) +struct pypy_cjk_enc_s *pypy_cjk_enc_new(const MultibyteCodec *codec) { - Py_ssize_t outlen; struct pypy_cjk_enc_s *d = malloc(sizeof(struct pypy_cjk_enc_s)); if (!d) return NULL; if (codec->encinit != NULL && codec->encinit(&d->state, codec->config) != 0) - goto errorexit; + { + free(d); + return NULL; + } + d->codec = codec; + d->outbuf_start = NULL; + return d; +} - d->codec = codec; +Py_ssize_t pypy_cjk_enc_init(struct pypy_cjk_enc_s *d, + Py_UNICODE *inbuf, Py_ssize_t inlen) +{ + Py_ssize_t outlen; d->inbuf_start = inbuf; d->inbuf = inbuf; d->inbuf_end = inbuf + inlen; - - if (inlen > (PY_SSIZE_T_MAX - 16) / 2) - goto errorexit; - outlen = inlen * 2 + 16; - d->outbuf_start = malloc(outlen); - if (!d->outbuf_start) - goto errorexit; + if (d->outbuf_start == NULL) + { + if (inlen > (PY_SSIZE_T_MAX - 16) / 2) + return -1; + outlen = inlen * 2 + 16; + d->outbuf_start = malloc(outlen); + if (d->outbuf_start == NULL) + return -1; + d->outbuf_end = d->outbuf_start + outlen; + } d->outbuf = d->outbuf_start; - d->outbuf_end = d->outbuf_start + outlen; - return d; - - errorexit: - free(d); - return NULL; + return 0; } void pypy_cjk_enc_free(struct pypy_cjk_enc_s *d) @@ -249,3 +255,8 @@ d->inbuf = d->inbuf_start + in_offset; return 0; } + +const MultibyteCodec *pypy_cjk_enc_getcodec(struct pypy_cjk_enc_s *d) +{ + return d->codec; +} diff --git a/pypy/translator/c/src/cjkcodecs/multibytecodec.h b/pypy/translator/c/src/cjkcodecs/multibytecodec.h --- a/pypy/translator/c/src/cjkcodecs/multibytecodec.h +++ b/pypy/translator/c/src/cjkcodecs/multibytecodec.h @@ -113,8 +113,9 @@ unsigned char *outbuf_start, *outbuf, *outbuf_end; }; -struct pypy_cjk_enc_s *pypy_cjk_enc_init(const MultibyteCodec *codec, - Py_UNICODE *inbuf, Py_ssize_t inlen); +struct pypy_cjk_enc_s *pypy_cjk_enc_new(const MultibyteCodec *codec); +Py_ssize_t pypy_cjk_enc_init(struct pypy_cjk_enc_s *d, + Py_UNICODE *inbuf, Py_ssize_t inlen); void pypy_cjk_enc_free(struct pypy_cjk_enc_s *); Py_ssize_t pypy_cjk_enc_chunk(struct pypy_cjk_enc_s *); Py_ssize_t pypy_cjk_enc_reset(struct pypy_cjk_enc_s *); @@ -124,6 +125,7 @@ Py_ssize_t pypy_cjk_enc_inbuf_consumed(struct pypy_cjk_enc_s* d); Py_ssize_t pypy_cjk_enc_replace_on_error(struct pypy_cjk_enc_s* d, char *, Py_ssize_t, Py_ssize_t); +const MultibyteCodec *pypy_cjk_enc_getcodec(struct pypy_cjk_enc_s *); /* list of codecs defined in the .c files */ From noreply at buildbot.pypy.org Mon Aug 1 16:18:40 2011 From: noreply at buildbot.pypy.org (arigo) Date: Mon, 1 Aug 2011 16:18:40 +0200 (CEST) Subject: [pypy-commit] pypy default: Tests for IncrementalEncoder. Pass, thanks to sharing of code. Message-ID: <20110801141840.AB974822AB@wyvern.cs.uni-duesseldorf.de> Author: Armin Rigo Branch: Changeset: r46149:bcc5502ec625 Date: 2011-08-01 13:28 +0200 http://bitbucket.org/pypy/pypy/changeset/bcc5502ec625/ Log: Tests for IncrementalEncoder. Pass, thanks to sharing of code. diff --git a/pypy/module/_multibytecodec/test/test_app_incremental.py b/pypy/module/_multibytecodec/test/test_app_incremental.py --- a/pypy/module/_multibytecodec/test/test_app_incremental.py +++ b/pypy/module/_multibytecodec/test/test_app_incremental.py @@ -88,9 +88,51 @@ d = self.IncrementalHzDecoder() for i in range(13): r = d.decode("a" * (2**i)) - assert r == unicode("a" * (2**i)) + assert r == u"a" * (2**i) def test_encode_hz(self): e = self.IncrementalHzEncoder() r = e.encode("abcd") - assert r == u'abcd' + assert r == 'abcd' + r = e.encode(u"\u5f95\u6c85") + assert r == '~{abcd~}' + r = e.encode(u"\u5f50") + assert r == '~{ef~}' + r = e.encode(u"\u73b7") + assert r == '~{gh~}' + + def test_encode_hz_final(self): + e = self.IncrementalHzEncoder() + r = e.encode(u"xyz\u5f95\u6c85", True) + assert r == 'xyz~{abcd~}' + # This is a bit hard to test, because the only way I can see that + # encoders can return MBERR_TOOFEW is with surrogates, which only + # occur with 2-byte unicode characters... We will just have to + # trust that the logic works, because it is exactly the same one + # as in the decode case :-/ + + def test_encode_hz_reset(self): + # Same issue as with test_encode_hz_final + e = self.IncrementalHzEncoder() + r = e.encode(u"xyz\u5f95\u6c85", True) + assert r == 'xyz~{abcd~}' + e.reset() + r = e.encode(u"xyz\u5f95\u6c85") + assert r == 'xyz~{abcd~}' + + def test_encode_hz_error(self): + e = self.IncrementalHzEncoder() + raises(UnicodeEncodeError, e.encode, u"\u4321", True) + e = self.IncrementalHzEncoder("ignore") + r = e.encode(u"xy\u4321z", True) + assert r == 'xyz' + e = self.IncrementalHzEncoder() + e.errors = "replace" + r = e.encode(u"xy\u4321z", True) + assert r == 'xy?z' + + def test_encode_hz_buffer_grow(self): + e = self.IncrementalHzEncoder() + for i in range(13): + r = e.encode(u"a" * (2**i)) + assert r == "a" * (2**i) From noreply at buildbot.pypy.org Mon Aug 1 16:18:41 2011 From: noreply at buildbot.pypy.org (arigo) Date: Mon, 1 Aug 2011 16:18:41 +0200 (CEST) Subject: [pypy-commit] pypy default: A quick writing and quick testing of MultibyteStream{Reader, Writer}. Message-ID: <20110801141841.ECC8E822AB@wyvern.cs.uni-duesseldorf.de> Author: Armin Rigo Branch: Changeset: r46150:69fabe6fa63e Date: 2011-08-01 14:15 +0200 http://bitbucket.org/pypy/pypy/changeset/69fabe6fa63e/ Log: A quick writing and quick testing of MultibyteStream{Reader,Writer}. diff --git a/pypy/module/_multibytecodec/app_multibytecodec.py b/pypy/module/_multibytecodec/app_multibytecodec.py --- a/pypy/module/_multibytecodec/app_multibytecodec.py +++ b/pypy/module/_multibytecodec/app_multibytecodec.py @@ -1,34 +1,48 @@ # NOT_RPYTHON # -# These classes are not supported so far. -# -# My theory is that they are not widely used on CPython either, because -# I found two bugs just by looking at their .c source: they always call -# encreset() after a piece of data, even though I think it's wrong --- -# it should be called only once at the end; and mbiencoder_reset() calls -# decreset() instead of encreset(). -# +# The interface here may be a little bit on the lightweight side. -class MultibyteIncrementalEncoder(object): - def __init__(self, *args, **kwds): - raise LookupError( - "MultibyteIncrementalEncoder not implemented; " - "see pypy/module/_multibytecodec/app_multibytecodec.py") +from _multibytecodec import MultibyteIncrementalDecoder +from _multibytecodec import MultibyteIncrementalEncoder -class MultibyteIncrementalDecoder(object): - def __init__(self, *args, **kwds): - raise LookupError( - "MultibyteIncrementalDecoder not implemented; " - "see pypy/module/_multibytecodec/app_multibytecodec.py") -class MultibyteStreamReader(object): - def __init__(self, *args, **kwds): - raise LookupError( - "MultibyteStreamReader not implemented; " - "see pypy/module/_multibytecodec/app_multibytecodec.py") +class MultibyteStreamReader(MultibyteIncrementalDecoder): + def __new__(cls, stream, errors=None): + self = MultibyteIncrementalDecoder.__new__(cls, errors) + self.stream = stream + return self -class MultibyteStreamWriter(object): - def __init__(self, *args, **kwds): - raise LookupError( - "MultibyteStreamWriter not implemented; " - "see pypy/module/_multibytecodec/app_multibytecodec.py") + def __read(self, read, size): + while True: + if size is None: + data = read() + else: + data = read(size) + final = not data + output = self.decode(data, final) + if output or final: + return output + size = 1 # read 1 more byte and retry + + def read(self, size=None): + return self.__read(self.stream.read, size) + + def readline(self, size=None): + return self.__read(self.stream.readline, size) + + def readlines(self, sizehint=None): + return self.__read(self.stream.read, sizehint).splitlines(True) + + +class MultibyteStreamWriter(MultibyteIncrementalEncoder): + def __new__(cls, stream, errors=None): + self = MultibyteIncrementalEncoder.__new__(cls, errors) + self.stream = stream + return self + + def write(self, data): + self.stream.write(self.encode(data)) + + def writelines(self, lines): + for data in lines: + self.write(data) diff --git a/pypy/module/_multibytecodec/test/test_app_stream.py b/pypy/module/_multibytecodec/test/test_app_stream.py new file mode 100644 --- /dev/null +++ b/pypy/module/_multibytecodec/test/test_app_stream.py @@ -0,0 +1,54 @@ +from pypy.conftest import gettestobjspace + + +class AppTestStreams: + def setup_class(cls): + cls.space = gettestobjspace(usemodules=['_multibytecodec']) + cls.w_HzStreamReader = cls.space.appexec([], """(): + import _codecs_cn + from _multibytecodec import MultibyteStreamReader + + class HzStreamReader(MultibyteStreamReader): + codec = _codecs_cn.getcodec('hz') + + return HzStreamReader + """) + cls.w_HzStreamWriter = cls.space.appexec([], """(): + import _codecs_cn + from _multibytecodec import MultibyteStreamWriter + + class HzStreamWriter(MultibyteStreamWriter): + codec = _codecs_cn.getcodec('hz') + + return HzStreamWriter + """) + + def test_reader(self): + class FakeFile: + def __init__(self, data): + self.data = data + self.pos = 0 + def read(self, size): + res = self.data[self.pos : self.pos + size] + self.pos += size + return res + # + r = self.HzStreamReader(FakeFile("!~{abcd~}xyz~{efgh")) + for expected in u'!\u5f95\u6c85xyz\u5f50\u73b7': + c = r.read(1) + assert c == expected + c = r.read(1) + assert c == '' + + def test_writer(self): + class FakeFile: + def __init__(self): + self.output = [] + def write(self, data): + self.output.append(data) + # + w = self.HzStreamWriter(FakeFile()) + for input in u'!\u5f95\u6c85xyz\u5f50\u73b7': + w.write(input) + assert w.stream.output == ['!', '~{ab~}', '~{cd~}', 'x', 'y', 'z', + '~{ef~}', '~{gh~}'] From noreply at buildbot.pypy.org Mon Aug 1 16:18:43 2011 From: noreply at buildbot.pypy.org (arigo) Date: Mon, 1 Aug 2011 16:18:43 +0200 (CEST) Subject: [pypy-commit] pypy default: Don't do a lookup based on 'self' here. Message-ID: <20110801141843.2EB17822AB@wyvern.cs.uni-duesseldorf.de> Author: Armin Rigo Branch: Changeset: r46151:2cd06a7a2bdb Date: 2011-08-01 15:42 +0200 http://bitbucket.org/pypy/pypy/changeset/2cd06a7a2bdb/ Log: Don't do a lookup based on 'self' here. Seems to be needed for tests. diff --git a/pypy/module/_multibytecodec/app_multibytecodec.py b/pypy/module/_multibytecodec/app_multibytecodec.py --- a/pypy/module/_multibytecodec/app_multibytecodec.py +++ b/pypy/module/_multibytecodec/app_multibytecodec.py @@ -19,7 +19,7 @@ else: data = read(size) final = not data - output = self.decode(data, final) + output = MultibyteIncrementalDecoder.decode(self, data, final) if output or final: return output size = 1 # read 1 more byte and retry @@ -41,7 +41,7 @@ return self def write(self, data): - self.stream.write(self.encode(data)) + self.stream.write(MultibyteIncrementalEncoder.encode(self, data)) def writelines(self, lines): for data in lines: From noreply at buildbot.pypy.org Mon Aug 1 16:18:44 2011 From: noreply at buildbot.pypy.org (arigo) Date: Mon, 1 Aug 2011 16:18:44 +0200 (CEST) Subject: [pypy-commit] pypy default: Test and fix. Message-ID: <20110801141844.64125822AB@wyvern.cs.uni-duesseldorf.de> Author: Armin Rigo Branch: Changeset: r46152:9aa3003707ff Date: 2011-08-01 15:45 +0200 http://bitbucket.org/pypy/pypy/changeset/9aa3003707ff/ Log: Test and fix. diff --git a/pypy/module/_multibytecodec/app_multibytecodec.py b/pypy/module/_multibytecodec/app_multibytecodec.py --- a/pypy/module/_multibytecodec/app_multibytecodec.py +++ b/pypy/module/_multibytecodec/app_multibytecodec.py @@ -16,9 +16,10 @@ while True: if size is None: data = read() + final = True else: data = read(size) - final = not data + final = not data output = MultibyteIncrementalDecoder.decode(self, data, final) if output or final: return output @@ -41,7 +42,7 @@ return self def write(self, data): - self.stream.write(MultibyteIncrementalEncoder.encode(self, data)) + self.stream.write(MultibyteIncrementalEncoder.encode(self, data, True)) def writelines(self, lines): for data in lines: diff --git a/pypy/module/_multibytecodec/test/test_app_stream.py b/pypy/module/_multibytecodec/test/test_app_stream.py --- a/pypy/module/_multibytecodec/test/test_app_stream.py +++ b/pypy/module/_multibytecodec/test/test_app_stream.py @@ -40,6 +40,23 @@ c = r.read(1) assert c == '' + def test_reader_replace(self): + class FakeFile: + def __init__(self, data): + self.data = data + def read(self): + return self.data + # + r = self.HzStreamReader(FakeFile("!~{a"), "replace") + c = r.read() + assert c == u'!\ufffd' + # + r = self.HzStreamReader(FakeFile("!~{a")) + r.errors = "replace" + assert r.errors == "replace" + c = r.read() + assert c == u'!\ufffd' + def test_writer(self): class FakeFile: def __init__(self): From noreply at buildbot.pypy.org Mon Aug 1 16:18:45 2011 From: noreply at buildbot.pypy.org (arigo) Date: Mon, 1 Aug 2011 16:18:45 +0200 (CEST) Subject: [pypy-commit] pypy default: Add missing 'usemodules'. Message-ID: <20110801141845.974DF822AB@wyvern.cs.uni-duesseldorf.de> Author: Armin Rigo Branch: Changeset: r46153:75cf4d4122bc Date: 2011-08-01 15:54 +0200 http://bitbucket.org/pypy/pypy/changeset/75cf4d4122bc/ Log: Add missing 'usemodules'. diff --git a/lib-python/conftest.py b/lib-python/conftest.py --- a/lib-python/conftest.py +++ b/lib-python/conftest.py @@ -154,18 +154,18 @@ RegrTest('test_cmd.py'), RegrTest('test_cmd_line_script.py'), RegrTest('test_codeccallbacks.py', core=True), - RegrTest('test_codecencodings_cn.py'), - RegrTest('test_codecencodings_hk.py'), - RegrTest('test_codecencodings_jp.py'), - RegrTest('test_codecencodings_kr.py'), - RegrTest('test_codecencodings_tw.py'), + RegrTest('test_codecencodings_cn.py', usemodules='_multibytecodec'), + RegrTest('test_codecencodings_hk.py', usemodules='_multibytecodec'), + RegrTest('test_codecencodings_jp.py', usemodules='_multibytecodec'), + RegrTest('test_codecencodings_kr.py', usemodules='_multibytecodec'), + RegrTest('test_codecencodings_tw.py', usemodules='_multibytecodec'), - RegrTest('test_codecmaps_cn.py'), - RegrTest('test_codecmaps_hk.py'), - RegrTest('test_codecmaps_jp.py'), - RegrTest('test_codecmaps_kr.py'), - RegrTest('test_codecmaps_tw.py'), - RegrTest('test_codecs.py', core=True), + RegrTest('test_codecmaps_cn.py', usemodules='_multibytecodec'), + RegrTest('test_codecmaps_hk.py', usemodules='_multibytecodec'), + RegrTest('test_codecmaps_jp.py', usemodules='_multibytecodec'), + RegrTest('test_codecmaps_kr.py', usemodules='_multibytecodec'), + RegrTest('test_codecmaps_tw.py', usemodules='_multibytecodec'), + RegrTest('test_codecs.py', core=True, usemodules='_multibytecodec'), RegrTest('test_codeop.py', core=True), RegrTest('test_coercion.py', core=True), RegrTest('test_collections.py'), @@ -314,7 +314,7 @@ RegrTest('test_mmap.py'), RegrTest('test_module.py', core=True), RegrTest('test_modulefinder.py'), - RegrTest('test_multibytecodec.py'), + RegrTest('test_multibytecodec.py', usemodules='_multibytecodec'), RegrTest('test_multibytecodec_support.py', skip="not a test"), RegrTest('test_multifile.py'), RegrTest('test_multiprocessing.py', skip='FIXME leaves subprocesses'), From noreply at buildbot.pypy.org Mon Aug 1 16:18:46 2011 From: noreply at buildbot.pypy.org (arigo) Date: Mon, 1 Aug 2011 16:18:46 +0200 (CEST) Subject: [pypy-commit] pypy default: merge heads Message-ID: <20110801141846.D3807822AB@wyvern.cs.uni-duesseldorf.de> Author: Armin Rigo Branch: Changeset: r46154:cd97971c87bb Date: 2011-08-01 16:18 +0200 http://bitbucket.org/pypy/pypy/changeset/cd97971c87bb/ Log: merge heads diff --git a/lib-python/conftest.py b/lib-python/conftest.py --- a/lib-python/conftest.py +++ b/lib-python/conftest.py @@ -154,18 +154,18 @@ RegrTest('test_cmd.py'), RegrTest('test_cmd_line_script.py'), RegrTest('test_codeccallbacks.py', core=True), - RegrTest('test_codecencodings_cn.py'), - RegrTest('test_codecencodings_hk.py'), - RegrTest('test_codecencodings_jp.py'), - RegrTest('test_codecencodings_kr.py'), - RegrTest('test_codecencodings_tw.py'), + RegrTest('test_codecencodings_cn.py', usemodules='_multibytecodec'), + RegrTest('test_codecencodings_hk.py', usemodules='_multibytecodec'), + RegrTest('test_codecencodings_jp.py', usemodules='_multibytecodec'), + RegrTest('test_codecencodings_kr.py', usemodules='_multibytecodec'), + RegrTest('test_codecencodings_tw.py', usemodules='_multibytecodec'), - RegrTest('test_codecmaps_cn.py'), - RegrTest('test_codecmaps_hk.py'), - RegrTest('test_codecmaps_jp.py'), - RegrTest('test_codecmaps_kr.py'), - RegrTest('test_codecmaps_tw.py'), - RegrTest('test_codecs.py', core=True), + RegrTest('test_codecmaps_cn.py', usemodules='_multibytecodec'), + RegrTest('test_codecmaps_hk.py', usemodules='_multibytecodec'), + RegrTest('test_codecmaps_jp.py', usemodules='_multibytecodec'), + RegrTest('test_codecmaps_kr.py', usemodules='_multibytecodec'), + RegrTest('test_codecmaps_tw.py', usemodules='_multibytecodec'), + RegrTest('test_codecs.py', core=True, usemodules='_multibytecodec'), RegrTest('test_codeop.py', core=True), RegrTest('test_coercion.py', core=True), RegrTest('test_collections.py'), @@ -314,7 +314,7 @@ RegrTest('test_mmap.py'), RegrTest('test_module.py', core=True), RegrTest('test_modulefinder.py'), - RegrTest('test_multibytecodec.py'), + RegrTest('test_multibytecodec.py', usemodules='_multibytecodec'), RegrTest('test_multibytecodec_support.py', skip="not a test"), RegrTest('test_multifile.py'), RegrTest('test_multiprocessing.py', skip='FIXME leaves subprocesses'), diff --git a/pypy/module/_multibytecodec/__init__.py b/pypy/module/_multibytecodec/__init__.py --- a/pypy/module/_multibytecodec/__init__.py +++ b/pypy/module/_multibytecodec/__init__.py @@ -7,13 +7,14 @@ # for compatibility this name is obscured, and should be called # via the _codecs_*.py modules written in lib_pypy. '__getcodec': 'interp_multibytecodec.getcodec', + + 'MultibyteIncrementalDecoder': + 'interp_incremental.MultibyteIncrementalDecoder', + 'MultibyteIncrementalEncoder': + 'interp_incremental.MultibyteIncrementalEncoder', } appleveldefs = { - 'MultibyteIncrementalEncoder': - 'app_multibytecodec.MultibyteIncrementalEncoder', - 'MultibyteIncrementalDecoder': - 'app_multibytecodec.MultibyteIncrementalDecoder', 'MultibyteStreamReader': 'app_multibytecodec.MultibyteStreamReader', 'MultibyteStreamWriter': diff --git a/pypy/module/_multibytecodec/app_multibytecodec.py b/pypy/module/_multibytecodec/app_multibytecodec.py --- a/pypy/module/_multibytecodec/app_multibytecodec.py +++ b/pypy/module/_multibytecodec/app_multibytecodec.py @@ -1,34 +1,49 @@ # NOT_RPYTHON # -# These classes are not supported so far. -# -# My theory is that they are not widely used on CPython either, because -# I found two bugs just by looking at their .c source: they always call -# encreset() after a piece of data, even though I think it's wrong --- -# it should be called only once at the end; and mbiencoder_reset() calls -# decreset() instead of encreset(). -# +# The interface here may be a little bit on the lightweight side. -class MultibyteIncrementalEncoder(object): - def __init__(self, *args, **kwds): - raise LookupError( - "MultibyteIncrementalEncoder not implemented; " - "see pypy/module/_multibytecodec/app_multibytecodec.py") +from _multibytecodec import MultibyteIncrementalDecoder +from _multibytecodec import MultibyteIncrementalEncoder -class MultibyteIncrementalDecoder(object): - def __init__(self, *args, **kwds): - raise LookupError( - "MultibyteIncrementalDecoder not implemented; " - "see pypy/module/_multibytecodec/app_multibytecodec.py") -class MultibyteStreamReader(object): - def __init__(self, *args, **kwds): - raise LookupError( - "MultibyteStreamReader not implemented; " - "see pypy/module/_multibytecodec/app_multibytecodec.py") +class MultibyteStreamReader(MultibyteIncrementalDecoder): + def __new__(cls, stream, errors=None): + self = MultibyteIncrementalDecoder.__new__(cls, errors) + self.stream = stream + return self -class MultibyteStreamWriter(object): - def __init__(self, *args, **kwds): - raise LookupError( - "MultibyteStreamWriter not implemented; " - "see pypy/module/_multibytecodec/app_multibytecodec.py") + def __read(self, read, size): + while True: + if size is None: + data = read() + final = True + else: + data = read(size) + final = not data + output = MultibyteIncrementalDecoder.decode(self, data, final) + if output or final: + return output + size = 1 # read 1 more byte and retry + + def read(self, size=None): + return self.__read(self.stream.read, size) + + def readline(self, size=None): + return self.__read(self.stream.readline, size) + + def readlines(self, sizehint=None): + return self.__read(self.stream.read, sizehint).splitlines(True) + + +class MultibyteStreamWriter(MultibyteIncrementalEncoder): + def __new__(cls, stream, errors=None): + self = MultibyteIncrementalEncoder.__new__(cls, errors) + self.stream = stream + return self + + def write(self, data): + self.stream.write(MultibyteIncrementalEncoder.encode(self, data, True)) + + def writelines(self, lines): + for data in lines: + self.write(data) diff --git a/pypy/module/_multibytecodec/c_codecs.py b/pypy/module/_multibytecodec/c_codecs.py --- a/pypy/module/_multibytecodec/c_codecs.py +++ b/pypy/module/_multibytecodec/c_codecs.py @@ -52,11 +52,13 @@ includes = ['src/cjkcodecs/multibytecodec.h'], include_dirs = [str(srcdir)], export_symbols = [ + "pypy_cjk_dec_new", "pypy_cjk_dec_init", "pypy_cjk_dec_free", "pypy_cjk_dec_chunk", "pypy_cjk_dec_outbuf", "pypy_cjk_dec_outlen", "pypy_cjk_dec_inbuf_remaining", "pypy_cjk_dec_inbuf_consumed", "pypy_cjk_dec_replace_on_error", + "pypy_cjk_enc_new", "pypy_cjk_enc_init", "pypy_cjk_enc_free", "pypy_cjk_enc_chunk", "pypy_cjk_enc_reset", "pypy_cjk_enc_outbuf", "pypy_cjk_enc_outlen", "pypy_cjk_enc_inbuf_remaining", "pypy_cjk_enc_inbuf_consumed", @@ -92,9 +94,11 @@ # Decoding DECODEBUF_P = rffi.COpaquePtr('struct pypy_cjk_dec_s', compilation_info=eci) +pypy_cjk_dec_new = llexternal('pypy_cjk_dec_new', + [MULTIBYTECODEC_P], DECODEBUF_P) pypy_cjk_dec_init = llexternal('pypy_cjk_dec_init', - [MULTIBYTECODEC_P, rffi.CCHARP, rffi.SSIZE_T], - DECODEBUF_P) + [DECODEBUF_P, rffi.CCHARP, rffi.SSIZE_T], + rffi.SSIZE_T) pypy_cjk_dec_free = llexternal('pypy_cjk_dec_free', [DECODEBUF_P], lltype.Void) pypy_cjk_dec_chunk = llexternal('pypy_cjk_dec_chunk', [DECODEBUF_P], @@ -113,25 +117,30 @@ rffi.SSIZE_T) def decode(codec, stringdata, errors="strict", errorcb=None, namecb=None): + decodebuf = pypy_cjk_dec_new(codec) + if not decodebuf: + raise MemoryError + try: + return decodeex(decodebuf, stringdata, errors, errorcb, namecb) + finally: + pypy_cjk_dec_free(decodebuf) + +def decodeex(decodebuf, stringdata, errors="strict", errorcb=None, namecb=None, + ignore_error=0): inleft = len(stringdata) inbuf = rffi.get_nonmovingbuffer(stringdata) try: - decodebuf = pypy_cjk_dec_init(codec, inbuf, inleft) - if not decodebuf: + if pypy_cjk_dec_init(decodebuf, inbuf, inleft) < 0: raise MemoryError - try: - while True: - r = pypy_cjk_dec_chunk(decodebuf) - if r == 0: - break - multibytecodec_decerror(decodebuf, r, errors, - errorcb, namecb, stringdata) - src = pypy_cjk_dec_outbuf(decodebuf) - length = pypy_cjk_dec_outlen(decodebuf) - return rffi.wcharpsize2unicode(src, length) - # - finally: - pypy_cjk_dec_free(decodebuf) + while True: + r = pypy_cjk_dec_chunk(decodebuf) + if r == 0 or r == ignore_error: + break + multibytecodec_decerror(decodebuf, r, errors, + errorcb, namecb, stringdata) + src = pypy_cjk_dec_outbuf(decodebuf) + length = pypy_cjk_dec_outlen(decodebuf) + return rffi.wcharpsize2unicode(src, length) # finally: rffi.free_nonmovingbuffer(stringdata, inbuf) @@ -174,9 +183,11 @@ # ____________________________________________________________ # Encoding ENCODEBUF_P = rffi.COpaquePtr('struct pypy_cjk_enc_s', compilation_info=eci) +pypy_cjk_enc_new = llexternal('pypy_cjk_enc_new', + [MULTIBYTECODEC_P], ENCODEBUF_P) pypy_cjk_enc_init = llexternal('pypy_cjk_enc_init', - [MULTIBYTECODEC_P, rffi.CWCHARP, rffi.SSIZE_T], - ENCODEBUF_P) + [ENCODEBUF_P, rffi.CWCHARP, rffi.SSIZE_T], + rffi.SSIZE_T) pypy_cjk_enc_free = llexternal('pypy_cjk_enc_free', [ENCODEBUF_P], lltype.Void) pypy_cjk_enc_chunk = llexternal('pypy_cjk_enc_chunk', [ENCODEBUF_P], @@ -195,39 +206,46 @@ [ENCODEBUF_P, rffi.CCHARP, rffi.SSIZE_T, rffi.SSIZE_T], rffi.SSIZE_T) +pypy_cjk_enc_getcodec = llexternal('pypy_cjk_enc_getcodec', + [ENCODEBUF_P], MULTIBYTECODEC_P) def encode(codec, unicodedata, errors="strict", errorcb=None, namecb=None): + encodebuf = pypy_cjk_enc_new(codec) + if not encodebuf: + raise MemoryError + try: + return encodeex(encodebuf, unicodedata, errors, errorcb, namecb) + finally: + pypy_cjk_enc_free(encodebuf) + +def encodeex(encodebuf, unicodedata, errors="strict", errorcb=None, + namecb=None, ignore_error=0): inleft = len(unicodedata) inbuf = rffi.get_nonmoving_unicodebuffer(unicodedata) try: - encodebuf = pypy_cjk_enc_init(codec, inbuf, inleft) - if not encodebuf: + if pypy_cjk_enc_init(encodebuf, inbuf, inleft) < 0: raise MemoryError - try: - while True: - r = pypy_cjk_enc_chunk(encodebuf) - if r == 0: - break - multibytecodec_encerror(encodebuf, r, errors, - codec, errorcb, namecb, unicodedata) - while True: - r = pypy_cjk_enc_reset(encodebuf) - if r == 0: - break - multibytecodec_encerror(encodebuf, r, errors, - codec, errorcb, namecb, unicodedata) - src = pypy_cjk_enc_outbuf(encodebuf) - length = pypy_cjk_enc_outlen(encodebuf) - return rffi.charpsize2str(src, length) - # - finally: - pypy_cjk_enc_free(encodebuf) + while True: + r = pypy_cjk_enc_chunk(encodebuf) + if r == 0 or r == ignore_error: + break + multibytecodec_encerror(encodebuf, r, errors, + errorcb, namecb, unicodedata) + while True: + r = pypy_cjk_enc_reset(encodebuf) + if r == 0: + break + multibytecodec_encerror(encodebuf, r, errors, + errorcb, namecb, unicodedata) + src = pypy_cjk_enc_outbuf(encodebuf) + length = pypy_cjk_enc_outlen(encodebuf) + return rffi.charpsize2str(src, length) # finally: rffi.free_nonmoving_unicodebuffer(unicodedata, inbuf) def multibytecodec_encerror(encodebuf, e, errors, - codec, errorcb, namecb, unicodedata): + errorcb, namecb, unicodedata): if e > 0: reason = "illegal multibyte sequence" esize = e @@ -248,6 +266,7 @@ elif errors == "ignore": replace = "" elif errors == "replace": + codec = pypy_cjk_enc_getcodec(encodebuf) try: replace = encode(codec, u"?") except EncodeDecodeError: diff --git a/pypy/module/_multibytecodec/interp_incremental.py b/pypy/module/_multibytecodec/interp_incremental.py new file mode 100644 --- /dev/null +++ b/pypy/module/_multibytecodec/interp_incremental.py @@ -0,0 +1,141 @@ +from pypy.rpython.lltypesystem import lltype +from pypy.module._multibytecodec import c_codecs +from pypy.module._multibytecodec.interp_multibytecodec import ( + MultibyteCodec, wrap_unicodedecodeerror, wrap_runtimeerror, + wrap_unicodeencodeerror) +from pypy.interpreter.baseobjspace import Wrappable +from pypy.interpreter.gateway import interp2app, unwrap_spec +from pypy.interpreter.typedef import TypeDef, GetSetProperty +from pypy.module._codecs.interp_codecs import CodecState + + +class MultibyteIncrementalBase(Wrappable): + + def __init__(self, space, errors): + if errors is None: + errors = 'strict' + self.space = space + self.errors = errors + w_codec = space.getattr(space.wrap(self), space.wrap("codec")) + codec = space.interp_w(MultibyteCodec, w_codec) + self.codec = codec.codec + self.name = codec.name + self._initialize() + + def __del__(self): + self._free() + + def reset_w(self): + self._free() + self._initialize() + + def fget_errors(self, space): + return space.wrap(self.errors) + + def fset_errors(self, space, w_errors): + self.errors = space.str_w(w_errors) + + +class MultibyteIncrementalDecoder(MultibyteIncrementalBase): + + def _initialize(self): + self.decodebuf = c_codecs.pypy_cjk_dec_new(self.codec) + self.pending = "" + + def _free(self): + self.pending = None + if self.decodebuf: + c_codecs.pypy_cjk_dec_free(self.decodebuf) + self.decodebuf = lltype.nullptr(c_codecs.DECODEBUF_P.TO) + + @unwrap_spec(object=str, final=bool) + def decode_w(self, object, final=False): + space = self.space + state = space.fromcache(CodecState) + if len(self.pending) > 0: + object = self.pending + object + try: + output = c_codecs.decodeex(self.decodebuf, object, self.errors, + state.decode_error_handler, self.name, + get_ignore_error(final)) + except c_codecs.EncodeDecodeError, e: + raise wrap_unicodedecodeerror(space, e, object, self.name) + except RuntimeError: + raise wrap_runtimeerror(space) + pos = c_codecs.pypy_cjk_dec_inbuf_consumed(self.decodebuf) + assert 0 <= pos <= len(object) + self.pending = object[pos:] + return space.wrap(output) + + + at unwrap_spec(errors="str_or_None") +def mbidecoder_new(space, w_subtype, errors=None): + r = space.allocate_instance(MultibyteIncrementalDecoder, w_subtype) + r.__init__(space, errors) + return space.wrap(r) + +MultibyteIncrementalDecoder.typedef = TypeDef( + 'MultibyteIncrementalDecoder', + __module__ = '_multibytecodec', + __new__ = interp2app(mbidecoder_new), + decode = interp2app(MultibyteIncrementalDecoder.decode_w), + reset = interp2app(MultibyteIncrementalDecoder.reset_w), + errors = GetSetProperty(MultibyteIncrementalDecoder.fget_errors, + MultibyteIncrementalDecoder.fset_errors), + ) + + +class MultibyteIncrementalEncoder(MultibyteIncrementalBase): + + def _initialize(self): + self.encodebuf = c_codecs.pypy_cjk_enc_new(self.codec) + self.pending = u"" + + def _free(self): + self.pending = None + if self.encodebuf: + c_codecs.pypy_cjk_enc_free(self.encodebuf) + self.encodebuf = lltype.nullptr(c_codecs.ENCODEBUF_P.TO) + + @unwrap_spec(object=unicode, final=bool) + def encode_w(self, object, final=False): + space = self.space + state = space.fromcache(CodecState) + if len(self.pending) > 0: + object = self.pending + object + try: + output = c_codecs.encodeex(self.encodebuf, object, self.errors, + state.encode_error_handler, self.name, + get_ignore_error(final)) + except c_codecs.EncodeDecodeError, e: + raise wrap_unicodeencodeerror(space, e, object, self.name) + except RuntimeError: + raise wrap_runtimeerror(space) + pos = c_codecs.pypy_cjk_enc_inbuf_consumed(self.encodebuf) + assert 0 <= pos <= len(object) + self.pending = object[pos:] + return space.wrap(output) + + + at unwrap_spec(errors="str_or_None") +def mbiencoder_new(space, w_subtype, errors=None): + r = space.allocate_instance(MultibyteIncrementalEncoder, w_subtype) + r.__init__(space, errors) + return space.wrap(r) + +MultibyteIncrementalEncoder.typedef = TypeDef( + 'MultibyteIncrementalEncoder', + __module__ = '_multibytecodec', + __new__ = interp2app(mbiencoder_new), + encode = interp2app(MultibyteIncrementalEncoder.encode_w), + reset = interp2app(MultibyteIncrementalEncoder.reset_w), + errors = GetSetProperty(MultibyteIncrementalEncoder.fget_errors, + MultibyteIncrementalEncoder.fset_errors), + ) + + +def get_ignore_error(final): + if final: + return 0 # don't ignore any error + else: + return c_codecs.MBERR_TOOFEW diff --git a/pypy/module/_multibytecodec/interp_multibytecodec.py b/pypy/module/_multibytecodec/interp_multibytecodec.py --- a/pypy/module/_multibytecodec/interp_multibytecodec.py +++ b/pypy/module/_multibytecodec/interp_multibytecodec.py @@ -22,17 +22,9 @@ output = c_codecs.decode(self.codec, input, errors, state.decode_error_handler, self.name) except c_codecs.EncodeDecodeError, e: - raise OperationError( - space.w_UnicodeDecodeError, - space.newtuple([ - space.wrap(self.name), - space.wrap(input), - space.wrap(e.start), - space.wrap(e.end), - space.wrap(e.reason)])) + raise wrap_unicodedecodeerror(space, e, input, self.name) except RuntimeError: - raise OperationError(space.w_RuntimeError, - space.wrap("internal codec error")) + raise wrap_runtimeerror(space) return space.newtuple([space.wrap(output), space.wrap(len(input))]) @@ -46,17 +38,9 @@ output = c_codecs.encode(self.codec, input, errors, state.encode_error_handler, self.name) except c_codecs.EncodeDecodeError, e: - raise OperationError( - space.w_UnicodeEncodeError, - space.newtuple([ - space.wrap(self.name), - space.wrap(input), - space.wrap(e.start), - space.wrap(e.end), - space.wrap(e.reason)])) + raise wrap_unicodeencodeerror(space, e, input, self.name) except RuntimeError: - raise OperationError(space.w_RuntimeError, - space.wrap("internal codec error")) + raise wrap_runtimeerror(space) return space.newtuple([space.wrap(output), space.wrap(len(input))]) @@ -78,3 +62,28 @@ raise OperationError(space.w_LookupError, space.wrap("no such codec is supported.")) return space.wrap(MultibyteCodec(name, codec)) + + +def wrap_unicodedecodeerror(space, e, input, name): + return OperationError( + space.w_UnicodeDecodeError, + space.newtuple([ + space.wrap(name), + space.wrap(input), + space.wrap(e.start), + space.wrap(e.end), + space.wrap(e.reason)])) + +def wrap_unicodeencodeerror(space, e, input, name): + raise OperationError( + space.w_UnicodeEncodeError, + space.newtuple([ + space.wrap(name), + space.wrap(input), + space.wrap(e.start), + space.wrap(e.end), + space.wrap(e.reason)])) + +def wrap_runtimeerror(space): + raise OperationError(space.w_RuntimeError, + space.wrap("internal codec error")) diff --git a/pypy/module/_multibytecodec/test/test_app_incremental.py b/pypy/module/_multibytecodec/test/test_app_incremental.py new file mode 100644 --- /dev/null +++ b/pypy/module/_multibytecodec/test/test_app_incremental.py @@ -0,0 +1,138 @@ +from pypy.conftest import gettestobjspace + + +class AppTestClasses: + def setup_class(cls): + cls.space = gettestobjspace(usemodules=['_multibytecodec']) + cls.w_IncrementalHzDecoder = cls.space.appexec([], """(): + import _codecs_cn + from _multibytecodec import MultibyteIncrementalDecoder + + class IncrementalHzDecoder(MultibyteIncrementalDecoder): + codec = _codecs_cn.getcodec('hz') + + return IncrementalHzDecoder + """) + cls.w_IncrementalHzEncoder = cls.space.appexec([], """(): + import _codecs_cn + from _multibytecodec import MultibyteIncrementalEncoder + + class IncrementalHzEncoder(MultibyteIncrementalEncoder): + codec = _codecs_cn.getcodec('hz') + + return IncrementalHzEncoder + """) + + def test_decode_hz(self): + d = self.IncrementalHzDecoder() + r = d.decode("~{abcd~}") + assert r == u'\u5f95\u6c85' + r = d.decode("~{efgh~}") + assert r == u'\u5f50\u73b7' + for c, output in zip("!~{abcd~}xyz~{efgh", + [u'!', # ! + u'', # ~ + u'', # { + u'', # a + u'\u5f95', # b + u'', # c + u'\u6c85', # d + u'', # ~ + u'', # } + u'x', # x + u'y', # y + u'z', # z + u'', # ~ + u'', # { + u'', # e + u'\u5f50', # f + u'', # g + u'\u73b7', # h + ]): + r = d.decode(c) + assert r == output + + def test_decode_hz_final(self): + d = self.IncrementalHzDecoder() + r = d.decode("~{", True) + assert r == u'' + raises(UnicodeDecodeError, d.decode, "~", True) + raises(UnicodeDecodeError, d.decode, "~{a", True) + + def test_decode_hz_reset(self): + d = self.IncrementalHzDecoder() + r = d.decode("ab") + assert r == u'ab' + r = d.decode("~{") + assert r == u'' + r = d.decode("ab") + assert r == u'\u5f95' + r = d.decode("ab") + assert r == u'\u5f95' + d.reset() + r = d.decode("ab") + assert r == u'ab' + + def test_decode_hz_error(self): + d = self.IncrementalHzDecoder() + raises(UnicodeDecodeError, d.decode, "~{abc", True) + d = self.IncrementalHzDecoder("ignore") + r = d.decode("~{abc", True) + assert r == u'\u5f95' + d = self.IncrementalHzDecoder() + d.errors = "replace" + r = d.decode("~{abc", True) + assert r == u'\u5f95\ufffd' + + def test_decode_hz_buffer_grow(self): + d = self.IncrementalHzDecoder() + for i in range(13): + r = d.decode("a" * (2**i)) + assert r == u"a" * (2**i) + + def test_encode_hz(self): + e = self.IncrementalHzEncoder() + r = e.encode("abcd") + assert r == 'abcd' + r = e.encode(u"\u5f95\u6c85") + assert r == '~{abcd~}' + r = e.encode(u"\u5f50") + assert r == '~{ef~}' + r = e.encode(u"\u73b7") + assert r == '~{gh~}' + + def test_encode_hz_final(self): + e = self.IncrementalHzEncoder() + r = e.encode(u"xyz\u5f95\u6c85", True) + assert r == 'xyz~{abcd~}' + # This is a bit hard to test, because the only way I can see that + # encoders can return MBERR_TOOFEW is with surrogates, which only + # occur with 2-byte unicode characters... We will just have to + # trust that the logic works, because it is exactly the same one + # as in the decode case :-/ + + def test_encode_hz_reset(self): + # Same issue as with test_encode_hz_final + e = self.IncrementalHzEncoder() + r = e.encode(u"xyz\u5f95\u6c85", True) + assert r == 'xyz~{abcd~}' + e.reset() + r = e.encode(u"xyz\u5f95\u6c85") + assert r == 'xyz~{abcd~}' + + def test_encode_hz_error(self): + e = self.IncrementalHzEncoder() + raises(UnicodeEncodeError, e.encode, u"\u4321", True) + e = self.IncrementalHzEncoder("ignore") + r = e.encode(u"xy\u4321z", True) + assert r == 'xyz' + e = self.IncrementalHzEncoder() + e.errors = "replace" + r = e.encode(u"xy\u4321z", True) + assert r == 'xy?z' + + def test_encode_hz_buffer_grow(self): + e = self.IncrementalHzEncoder() + for i in range(13): + r = e.encode(u"a" * (2**i)) + assert r == "a" * (2**i) diff --git a/pypy/module/_multibytecodec/test/test_app_stream.py b/pypy/module/_multibytecodec/test/test_app_stream.py new file mode 100644 --- /dev/null +++ b/pypy/module/_multibytecodec/test/test_app_stream.py @@ -0,0 +1,71 @@ +from pypy.conftest import gettestobjspace + + +class AppTestStreams: + def setup_class(cls): + cls.space = gettestobjspace(usemodules=['_multibytecodec']) + cls.w_HzStreamReader = cls.space.appexec([], """(): + import _codecs_cn + from _multibytecodec import MultibyteStreamReader + + class HzStreamReader(MultibyteStreamReader): + codec = _codecs_cn.getcodec('hz') + + return HzStreamReader + """) + cls.w_HzStreamWriter = cls.space.appexec([], """(): + import _codecs_cn + from _multibytecodec import MultibyteStreamWriter + + class HzStreamWriter(MultibyteStreamWriter): + codec = _codecs_cn.getcodec('hz') + + return HzStreamWriter + """) + + def test_reader(self): + class FakeFile: + def __init__(self, data): + self.data = data + self.pos = 0 + def read(self, size): + res = self.data[self.pos : self.pos + size] + self.pos += size + return res + # + r = self.HzStreamReader(FakeFile("!~{abcd~}xyz~{efgh")) + for expected in u'!\u5f95\u6c85xyz\u5f50\u73b7': + c = r.read(1) + assert c == expected + c = r.read(1) + assert c == '' + + def test_reader_replace(self): + class FakeFile: + def __init__(self, data): + self.data = data + def read(self): + return self.data + # + r = self.HzStreamReader(FakeFile("!~{a"), "replace") + c = r.read() + assert c == u'!\ufffd' + # + r = self.HzStreamReader(FakeFile("!~{a")) + r.errors = "replace" + assert r.errors == "replace" + c = r.read() + assert c == u'!\ufffd' + + def test_writer(self): + class FakeFile: + def __init__(self): + self.output = [] + def write(self, data): + self.output.append(data) + # + w = self.HzStreamWriter(FakeFile()) + for input in u'!\u5f95\u6c85xyz\u5f50\u73b7': + w.write(input) + assert w.stream.output == ['!', '~{ab~}', '~{cd~}', 'x', 'y', 'z', + '~{ef~}', '~{gh~}'] diff --git a/pypy/module/_multibytecodec/test/test_c_codecs.py b/pypy/module/_multibytecodec/test/test_c_codecs.py --- a/pypy/module/_multibytecodec/test/test_c_codecs.py +++ b/pypy/module/_multibytecodec/test/test_c_codecs.py @@ -2,6 +2,7 @@ from pypy.module._multibytecodec.c_codecs import getcodec, codecs from pypy.module._multibytecodec.c_codecs import decode, encode from pypy.module._multibytecodec.c_codecs import EncodeDecodeError +from pypy.module._multibytecodec import c_codecs def test_codecs_existence(): @@ -22,6 +23,52 @@ c = getcodec("hz") u = decode(c, "~{abc}") assert u == u'\u5f95\u6cef' + u = decode(c, "~{") + assert u == u'' + +def test_decodeex_hz(): + c = getcodec("hz") + decodebuf = c_codecs.pypy_cjk_dec_new(c) + u = c_codecs.decodeex(decodebuf, "~{abcd~}") + assert u == u'\u5f95\u6c85' + u = c_codecs.decodeex(decodebuf, "~{efgh~}") + assert u == u'\u5f50\u73b7' + u = c_codecs.decodeex(decodebuf, "!~{abcd~}xyz~{efgh") + assert u == u'!\u5f95\u6c85xyz\u5f50\u73b7' + c_codecs.pypy_cjk_dec_free(decodebuf) + +def test_decodeex_hz_incomplete(): + c = getcodec("hz") + decodebuf = c_codecs.pypy_cjk_dec_new(c) + buf = '' + for c, output in zip("!~{abcd~}xyz~{efgh", + [u'!', # ! + u'', # ~ + u'', # { + u'', # a + u'\u5f95', # b + u'', # c + u'\u6c85', # d + u'', # ~ + u'', # } + u'x', # x + u'y', # y + u'z', # z + u'', # ~ + u'', # { + u'', # e + u'\u5f50', # f + u'', # g + u'\u73b7', # h + ]): + buf += c + u = c_codecs.decodeex(decodebuf, buf, + ignore_error = c_codecs.MBERR_TOOFEW) + assert u == output + incompletepos = c_codecs.pypy_cjk_dec_inbuf_consumed(decodebuf) + buf = buf[incompletepos:] + assert buf == '' + c_codecs.pypy_cjk_dec_free(decodebuf) def test_decode_hz_error(): # error diff --git a/pypy/translator/c/src/cjkcodecs/multibytecodec.c b/pypy/translator/c/src/cjkcodecs/multibytecodec.c --- a/pypy/translator/c/src/cjkcodecs/multibytecodec.c +++ b/pypy/translator/c/src/cjkcodecs/multibytecodec.c @@ -3,31 +3,38 @@ #include "src/cjkcodecs/multibytecodec.h" -struct pypy_cjk_dec_s *pypy_cjk_dec_init(const MultibyteCodec *codec, - char *inbuf, Py_ssize_t inlen) +struct pypy_cjk_dec_s *pypy_cjk_dec_new(const MultibyteCodec *codec) { struct pypy_cjk_dec_s *d = malloc(sizeof(struct pypy_cjk_dec_s)); if (!d) return NULL; if (codec->decinit != NULL && codec->decinit(&d->state, codec->config) != 0) - goto errorexit; + { + free(d); + return NULL; + } + d->codec = codec; + d->outbuf_start = NULL; + return d; +} - d->codec = codec; +Py_ssize_t pypy_cjk_dec_init(struct pypy_cjk_dec_s *d, + char *inbuf, Py_ssize_t inlen) +{ d->inbuf_start = inbuf; d->inbuf = inbuf; d->inbuf_end = inbuf + inlen; - d->outbuf_start = (inlen <= (PY_SSIZE_T_MAX / sizeof(Py_UNICODE)) ? - malloc(inlen * sizeof(Py_UNICODE)) : - NULL); - if (!d->outbuf_start) - goto errorexit; + if (d->outbuf_start == NULL) + { + d->outbuf_start = (inlen <= (PY_SSIZE_T_MAX / sizeof(Py_UNICODE)) ? + malloc(inlen * sizeof(Py_UNICODE)) : + NULL); + if (d->outbuf_start == NULL) + return -1; + d->outbuf_end = d->outbuf_start + inlen; + } d->outbuf = d->outbuf_start; - d->outbuf_end = d->outbuf_start + inlen; - return d; - - errorexit: - free(d); - return NULL; + return 0; } void pypy_cjk_dec_free(struct pypy_cjk_dec_s *d) @@ -112,34 +119,40 @@ /************************************************************/ -struct pypy_cjk_enc_s *pypy_cjk_enc_init(const MultibyteCodec *codec, - Py_UNICODE *inbuf, Py_ssize_t inlen) +struct pypy_cjk_enc_s *pypy_cjk_enc_new(const MultibyteCodec *codec) { - Py_ssize_t outlen; struct pypy_cjk_enc_s *d = malloc(sizeof(struct pypy_cjk_enc_s)); if (!d) return NULL; if (codec->encinit != NULL && codec->encinit(&d->state, codec->config) != 0) - goto errorexit; + { + free(d); + return NULL; + } + d->codec = codec; + d->outbuf_start = NULL; + return d; +} - d->codec = codec; +Py_ssize_t pypy_cjk_enc_init(struct pypy_cjk_enc_s *d, + Py_UNICODE *inbuf, Py_ssize_t inlen) +{ + Py_ssize_t outlen; d->inbuf_start = inbuf; d->inbuf = inbuf; d->inbuf_end = inbuf + inlen; - - if (inlen > (PY_SSIZE_T_MAX - 16) / 2) - goto errorexit; - outlen = inlen * 2 + 16; - d->outbuf_start = malloc(outlen); - if (!d->outbuf_start) - goto errorexit; + if (d->outbuf_start == NULL) + { + if (inlen > (PY_SSIZE_T_MAX - 16) / 2) + return -1; + outlen = inlen * 2 + 16; + d->outbuf_start = malloc(outlen); + if (d->outbuf_start == NULL) + return -1; + d->outbuf_end = d->outbuf_start + outlen; + } d->outbuf = d->outbuf_start; - d->outbuf_end = d->outbuf_start + outlen; - return d; - - errorexit: - free(d); - return NULL; + return 0; } void pypy_cjk_enc_free(struct pypy_cjk_enc_s *d) @@ -242,3 +255,8 @@ d->inbuf = d->inbuf_start + in_offset; return 0; } + +const MultibyteCodec *pypy_cjk_enc_getcodec(struct pypy_cjk_enc_s *d) +{ + return d->codec; +} diff --git a/pypy/translator/c/src/cjkcodecs/multibytecodec.h b/pypy/translator/c/src/cjkcodecs/multibytecodec.h --- a/pypy/translator/c/src/cjkcodecs/multibytecodec.h +++ b/pypy/translator/c/src/cjkcodecs/multibytecodec.h @@ -94,8 +94,9 @@ Py_UNICODE *outbuf_start, *outbuf, *outbuf_end; }; -struct pypy_cjk_dec_s *pypy_cjk_dec_init(const MultibyteCodec *codec, - char *inbuf, Py_ssize_t inlen); +struct pypy_cjk_dec_s *pypy_cjk_dec_new(const MultibyteCodec *codec); +Py_ssize_t pypy_cjk_dec_init(struct pypy_cjk_dec_s *d, + char *inbuf, Py_ssize_t inlen); void pypy_cjk_dec_free(struct pypy_cjk_dec_s *); Py_ssize_t pypy_cjk_dec_chunk(struct pypy_cjk_dec_s *); Py_UNICODE *pypy_cjk_dec_outbuf(struct pypy_cjk_dec_s *); @@ -112,8 +113,9 @@ unsigned char *outbuf_start, *outbuf, *outbuf_end; }; -struct pypy_cjk_enc_s *pypy_cjk_enc_init(const MultibyteCodec *codec, - Py_UNICODE *inbuf, Py_ssize_t inlen); +struct pypy_cjk_enc_s *pypy_cjk_enc_new(const MultibyteCodec *codec); +Py_ssize_t pypy_cjk_enc_init(struct pypy_cjk_enc_s *d, + Py_UNICODE *inbuf, Py_ssize_t inlen); void pypy_cjk_enc_free(struct pypy_cjk_enc_s *); Py_ssize_t pypy_cjk_enc_chunk(struct pypy_cjk_enc_s *); Py_ssize_t pypy_cjk_enc_reset(struct pypy_cjk_enc_s *); @@ -123,6 +125,7 @@ Py_ssize_t pypy_cjk_enc_inbuf_consumed(struct pypy_cjk_enc_s* d); Py_ssize_t pypy_cjk_enc_replace_on_error(struct pypy_cjk_enc_s* d, char *, Py_ssize_t, Py_ssize_t); +const MultibyteCodec *pypy_cjk_enc_getcodec(struct pypy_cjk_enc_s *); /* list of codecs defined in the .c files */ From noreply at buildbot.pypy.org Mon Aug 1 17:08:17 2011 From: noreply at buildbot.pypy.org (gutworth) Date: Mon, 1 Aug 2011 17:08:17 +0200 (CEST) Subject: [pypy-commit] pypy unroll-if-alt: revert this; the implicit specialization of methods conflicts with mine Message-ID: <20110801150817.E9245822AB@wyvern.cs.uni-duesseldorf.de> Author: Benjamin Peterson Branch: unroll-if-alt Changeset: r46155:0d17eee15160 Date: 2011-08-01 10:08 -0500 http://bitbucket.org/pypy/pypy/changeset/0d17eee15160/ Log: revert this; the implicit specialization of methods conflicts with mine diff --git a/pypy/objspace/std/newformat.py b/pypy/objspace/std/newformat.py --- a/pypy/objspace/std/newformat.py +++ b/pypy/objspace/std/newformat.py @@ -55,7 +55,6 @@ self.auto_numbering_state = ANS_INIT return self._build_string(0, len(self.template), 2) - @jit.unroll_if(lambda self, start, end, level: jit.isconstant(self.template)) def _build_string(self, start, end, level): space = self.space if self.is_unicode: @@ -67,6 +66,12 @@ space.wrap("Recursion depth exceeded")) level -= 1 s = self.template + if jit.isconstant(s): + return self._do_build_string_unroll(start, end, level, out, s) + else: + return self._do_build_string(start, end, level, out, s) + + def _do_build_string(self, start, end, level, out, s): space = self.space last_literal = i = start while i < end: @@ -118,6 +123,11 @@ out.append_slice(s, last_literal, end) return out.build() + f = sourcetools.func_with_new_name(_do_build_string, + "_do_build_string_unroll") + _do_build_string_unroll = jit.unroll_safe(f) + del f + def _parse_field(self, start, end): s = self.template # Find ":" or "!" From noreply at buildbot.pypy.org Mon Aug 1 17:17:33 2011 From: noreply at buildbot.pypy.org (fijal) Date: Mon, 1 Aug 2011 17:17:33 +0200 (CEST) Subject: [pypy-commit] pypy.org extradoc: add a button id Message-ID: <20110801151733.BCC12822AB@wyvern.cs.uni-duesseldorf.de> Author: Maciej Fijalkowski Branch: extradoc Changeset: r218:7856e15ac7d6 Date: 2011-08-01 17:16 +0200 http://bitbucket.org/pypy/pypy.org/changeset/7856e15ac7d6/ Log: add a button id diff --git a/source/sponsor.txt b/source/sponsor.txt --- a/source/sponsor.txt +++ b/source/sponsor.txt @@ -28,6 +28,7 @@ + From noreply at buildbot.pypy.org Mon Aug 1 17:17:34 2011 From: noreply at buildbot.pypy.org (fijal) Date: Mon, 1 Aug 2011 17:17:34 +0200 (CEST) Subject: [pypy-commit] pypy.org extradoc: link Message-ID: <20110801151734.CFE4C822AB@wyvern.cs.uni-duesseldorf.de> Author: Maciej Fijalkowski Branch: extradoc Changeset: r219:e18bcfc19d7b Date: 2011-08-01 17:17 +0200 http://bitbucket.org/pypy/pypy.org/changeset/e18bcfc19d7b/ Log: link diff --git a/source/sponsor.txt b/source/sponsor.txt --- a/source/sponsor.txt +++ b/source/sponsor.txt @@ -32,5 +32,5 @@ -.. _`pilot program`: xxx -.. _`crowdfunding`: xxx +.. _`pilot program`: xxxbloglink +.. _`crowdfunding`: http://en.wikipedia.org/wiki/Crowdfunding From noreply at buildbot.pypy.org Mon Aug 1 17:17:35 2011 From: noreply at buildbot.pypy.org (fijal) Date: Mon, 1 Aug 2011 17:17:35 +0200 (CEST) Subject: [pypy-commit] pypy.org extradoc: merge Message-ID: <20110801151735.E6764822AB@wyvern.cs.uni-duesseldorf.de> Author: Maciej Fijalkowski Branch: extradoc Changeset: r220:41d5a0a92d16 Date: 2011-08-01 17:17 +0200 http://bitbucket.org/pypy/pypy.org/changeset/41d5a0a92d16/ Log: merge diff --git a/source/sponsor.txt b/source/sponsor.txt --- a/source/sponsor.txt +++ b/source/sponsor.txt @@ -7,10 +7,10 @@ ------------------------- As an Open Source project, PyPy is mostly an effort of volunteers. However, -we decided to run a `pilot program`_ of `crowdfunding`_ to implement numpy. +we've decided to run a `pilot program`_ of `crowdfunding`_ to implement NumPy. -Our goal is to raise 20000 euros by 1st of October 2011. That should allow us -to finish implementing NumPy by the end of 2011. +Our goal is to raise 20000 Euros by the 1st of October 2011. That should allow +us to finish implementing NumPy by the end of 2011. .. raw:: html From noreply at buildbot.pypy.org Mon Aug 1 17:18:33 2011 From: noreply at buildbot.pypy.org (fijal) Date: Mon, 1 Aug 2011 17:18:33 +0200 (CEST) Subject: [pypy-commit] pypy.org extradoc: regen and reset values to 0 Message-ID: <20110801151833.58858822AB@wyvern.cs.uni-duesseldorf.de> Author: Maciej Fijalkowski Branch: extradoc Changeset: r221:24acd671ff2b Date: 2011-08-01 17:19 +0200 http://bitbucket.org/pypy/pypy.org/changeset/24acd671ff2b/ Log: regen and reset values to 0 diff --git a/source/_layouts/sponsor.genshi b/source/_layouts/sponsor.genshi --- a/source/_layouts/sponsor.genshi +++ b/source/_layouts/sponsor.genshi @@ -8,7 +8,7 @@ diff --git a/source/sponsor.txt b/source/sponsor.txt --- a/source/sponsor.txt +++ b/source/sponsor.txt @@ -16,7 +16,7 @@
- 1234 of 20000 (5%) + 0 of 20000 (0%)
diff --git a/sponsor.html b/sponsor.html --- a/sponsor.html +++ b/sponsor.html @@ -46,18 +46,18 @@

Sponsor PyPy

As an Open Source project, PyPy is mostly an effort of volunteers. However, -we decided to run a pilot program of crowdfunding to implement numpy.

-

Our goal is to raise 20000 euros by 1st of October 2011. That should allow us -to finish implementing NumPy by the end of 2011.

+we've decided to run a pilot program of crowdfunding to implement NumPy.

+

Our goal is to raise 20000 Euros by the 1st of October 2011. That should allow +us to finish implementing NumPy by the end of 2011.

- 1234 of 20000 (5%) + 0 of 20000 (0%)
@@ -69,6 +69,7 @@ +
From noreply at buildbot.pypy.org Mon Aug 1 17:20:32 2011 From: noreply at buildbot.pypy.org (fijal) Date: Mon, 1 Aug 2011 17:20:32 +0200 (CEST) Subject: [pypy-commit] pypy.org extradoc: update to newer version Message-ID: <20110801152032.ED1B7822AB@wyvern.cs.uni-duesseldorf.de> Author: Maciej Fijalkowski Branch: extradoc Changeset: r222:f010f2ca7a5a Date: 2011-08-01 17:21 +0200 http://bitbucket.org/pypy/pypy.org/changeset/f010f2ca7a5a/ Log: update to newer version diff --git a/source/sponsor.txt b/source/sponsor.txt --- a/source/sponsor.txt +++ b/source/sponsor.txt @@ -29,7 +29,8 @@ - + + .. _`pilot program`: xxxbloglink diff --git a/sponsor.html b/sponsor.html --- a/sponsor.html +++ b/sponsor.html @@ -70,7 +70,8 @@ - + + From noreply at buildbot.pypy.org Mon Aug 1 17:21:41 2011 From: noreply at buildbot.pypy.org (fijal) Date: Mon, 1 Aug 2011 17:21:41 +0200 (CEST) Subject: [pypy-commit] pypy.org extradoc: another try Message-ID: <20110801152141.F36BE822AB@wyvern.cs.uni-duesseldorf.de> Author: Maciej Fijalkowski Branch: extradoc Changeset: r223:2612be1755b6 Date: 2011-08-01 17:22 +0200 http://bitbucket.org/pypy/pypy.org/changeset/2612be1755b6/ Log: another try diff --git a/source/sponsor.txt b/source/sponsor.txt --- a/source/sponsor.txt +++ b/source/sponsor.txt @@ -27,7 +27,7 @@ - + From noreply at buildbot.pypy.org Mon Aug 1 17:21:43 2011 From: noreply at buildbot.pypy.org (fijal) Date: Mon, 1 Aug 2011 17:21:43 +0200 (CEST) Subject: [pypy-commit] pypy.org extradoc: update Message-ID: <20110801152143.12F14822AB@wyvern.cs.uni-duesseldorf.de> Author: Maciej Fijalkowski Branch: extradoc Changeset: r224:e44e5a53718f Date: 2011-08-01 17:22 +0200 http://bitbucket.org/pypy/pypy.org/changeset/e44e5a53718f/ Log: update diff --git a/sponsor.html b/sponsor.html --- a/sponsor.html +++ b/sponsor.html @@ -68,7 +68,7 @@ - + From noreply at buildbot.pypy.org Mon Aug 1 17:26:09 2011 From: noreply at buildbot.pypy.org (fijal) Date: Mon, 1 Aug 2011 17:26:09 +0200 (CEST) Subject: [pypy-commit] pypy.org extradoc: this is called quantity Message-ID: <20110801152609.1B906822AB@wyvern.cs.uni-duesseldorf.de> Author: Maciej Fijalkowski Branch: extradoc Changeset: r225:41e6e5979cd8 Date: 2011-08-01 17:26 +0200 http://bitbucket.org/pypy/pypy.org/changeset/41e6e5979cd8/ Log: this is called quantity diff --git a/source/sponsor.txt b/source/sponsor.txt --- a/source/sponsor.txt +++ b/source/sponsor.txt @@ -21,7 +21,7 @@
Sponsor numpy implementation in PyPy by the following amount:
- + + + + + + - + + - + +