1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
|
import pytest
from rpython.rlib.rarithmetic import r_ulonglong
from rpython.rlib.rstruct import standardfmttable, nativefmttable
from rpython.rlib.rstruct.error import StructOverflowError
from rpython.rlib import buffer
from rpython.rlib.buffer import SubBuffer
from rpython.rlib.mutbuffer import MutableStringBuffer
from rpython.rlib import rawstorage
import struct
class FakeFormatIter(object):
def __init__(self, bigendian, wbuf, value):
self.value = value
self.bigendian = bigendian
self.wbuf = wbuf
self.pos = 0
def advance(self, count):
self.pos += count
def _accept_arg(self):
return self.value
def __getattr__(self, name):
if name.startswith('accept_'):
return self._accept_arg
raise AttributeError(name)
class PackSupport(object):
"""
These test tests only the various pack_* functions, individually. There
is no RPython interface to them, as for now they are used only to
implement struct.pack in pypy/module/struct
"""
bigendian = None
fmt_prefix = None
fmttable = None
USE_FASTPATH = True
ALLOW_SLOWPATH = True
ALLOW_FASTPATH = True
ALLOW_UNALIGNED_ACCESS = rawstorage.misaligned_is_fine
def setup_method(self, meth):
standardfmttable.USE_FASTPATH = self.USE_FASTPATH
standardfmttable.ALLOW_SLOWPATH = self.ALLOW_SLOWPATH
standardfmttable.ALLOW_FASTPATH = self.ALLOW_FASTPATH
buffer.ALLOW_UNALIGNED_ACCESS = self.ALLOW_UNALIGNED_ACCESS
def teardown_method(self, meth):
standardfmttable.USE_FASTPATH = True
standardfmttable.ALLOW_SLOWPATH = True
standardfmttable.ALLOW_FASTPATH = True
buffer.ALLOW_UNALIGNED_ACCESS = rawstorage.misaligned_is_fine
def mypack(self, fmt, value):
size = struct.calcsize(fmt)
wbuf = MutableStringBuffer(size)
fake_fmtiter = self.mypack_into(fmt, wbuf, value)
# check that we called advance() the right number of times
assert fake_fmtiter.pos == wbuf.getlength()
return wbuf.finish()
def mypack_into(self, fmt, wbuf, value, advance=None):
fake_fmtiter = FakeFormatIter(self.bigendian, wbuf, value)
if advance:
fake_fmtiter.advance(advance)
attrs = self.fmttable[fmt]
pack = attrs['pack']
pack(fake_fmtiter)
return fake_fmtiter
def mypack_fn(self, func, size, arg, value):
wbuf = MutableStringBuffer(size)
fake_fmtiter = FakeFormatIter(self.bigendian, wbuf, value)
func(fake_fmtiter, arg)
assert fake_fmtiter.pos == wbuf.getlength()
return wbuf.finish()
def check(self, fmt, value):
expected = struct.pack(self.fmt_prefix+fmt, value)
got = self.mypack(fmt, value)
assert got == expected
class TestAllowSlowpath(PackSupport):
ALLOW_SLOWPATH = False
bigendian = not nativefmttable.native_is_bigendian
fmttable = standardfmttable.standard_fmttable
def test_slowpath_not_allowed(self):
# we are using a non-native endianess and ALLOW_SLOWPATH is False, so
# the following MUST raise
pytest.raises(ValueError, "self.mypack('i', 42)")
class TestUseFastpath(PackSupport):
ALLOW_SLOWPATH = False
bigendian = nativefmttable.native_is_bigendian
fmttable = standardfmttable.standard_fmttable
def test_fastpath_taken(self):
# we are using native endianess and slowpath is not allowed, so the
# following MUST succeed
expected = struct.pack('i', 42)
assert self.mypack('i', 42) == expected
class TestAllowFastPath(PackSupport):
ALLOW_FASTPATH = False
bigendian = nativefmttable.native_is_bigendian
fmttable = standardfmttable.standard_fmttable
def test_fastpath_not_allowed(self):
# we are using a native endianess but ALLOW_FASTPATH is False, so
# the following MUST raise
pytest.raises(ValueError, "self.mypack('i', 42)")
class BaseTestPack(PackSupport):
def test_pack_int(self):
self.check('b', 42)
self.check('B', 242)
self.check('h', 32767)
self.check('H', 32768)
self.check("i", 0x41424344)
self.check("i", -3)
self.check("i", -2147483648)
self.check("I", 0x81424344)
self.check("q", 0x4142434445464748)
self.check("q", -0x41B2B3B4B5B6B7B8)
self.check("Q", r_ulonglong(0x8142434445464748))
def test_pack_ieee(self):
self.check('f', 123.456)
self.check('d', 123.456789)
def test_pack_halffloat(self):
size = 2
wbuf = MutableStringBuffer(size)
self.mypack_into('e', wbuf, 6.5e+04)
got = wbuf.finish()
if self.bigendian:
assert got == b'\x7b\xef'
else:
assert got == b'\xef\x7b'
def test_float_overflow(self):
if self.fmt_prefix == '@':
# native packing, no overflow
self.check('f', 10e100)
else:
# non-native packing, should raise
pytest.raises(StructOverflowError, "self.mypack('f', 10e100)")
def test_pack_char(self):
self.check('c', 'a')
def test_pack_bool(self):
self.check('?', True)
self.check('?', False)
def test_pack_pad(self):
s = self.mypack_fn(standardfmttable.pack_pad,
arg=4, value=None, size=4)
assert s == '\x00'*4
def test_pack_string(self):
s = self.mypack_fn(standardfmttable.pack_string,
arg=8, value='hello', size=8)
assert s == 'hello\x00\x00\x00'
#
s = self.mypack_fn(standardfmttable.pack_string,
arg=8, value='hello world', size=8)
assert s == 'hello wo'
def test_pack_pascal(self):
s = self.mypack_fn(standardfmttable.pack_pascal,
arg=8, value='hello', size=8)
assert s == '\x05hello\x00\x00'
class TestPackLittleEndian(BaseTestPack):
bigendian = False
fmt_prefix = '<'
fmttable = standardfmttable.standard_fmttable
class TestPackLittleEndianSlowPath(TestPackLittleEndian):
USE_FASTPATH = False
class TestPackBigEndian(BaseTestPack):
bigendian = True
fmt_prefix = '>'
fmttable = standardfmttable.standard_fmttable
class TestPackBigEndianSlowPath(TestPackBigEndian):
USE_FASTPATH = False
class TestNative(BaseTestPack):
# native packing automatically use the proper endianess, so it should
# always take the fast path
ALLOW_SLOWPATH = False
bigendian = nativefmttable.native_is_bigendian
fmt_prefix = '@'
fmttable = nativefmttable.native_fmttable
class TestNativeSlowPath(BaseTestPack):
USE_FASTPATH = False
bigendian = nativefmttable.native_is_bigendian
fmt_prefix = '@'
fmttable = nativefmttable.native_fmttable
class TestUnaligned(PackSupport):
ALLOW_FASTPATH = False
ALLOW_UNALIGNED_ACCESS = False
bigendian = nativefmttable.native_is_bigendian
fmttable = nativefmttable.native_fmttable
def test_unaligned(self):
# to force a non-aligned 'i'
expected = struct.pack('=BBi', 0xAB, 0xCD, 0x1234)
#
wbuf = MutableStringBuffer(len(expected))
wbuf.setitem(0, chr(0xAB))
wbuf.setitem(1, chr(0xCD))
fake_fmtiter = self.mypack_into('i', wbuf, 0x1234, advance=2)
assert fake_fmtiter.pos == wbuf.getlength()
got = wbuf.finish()
assert got == expected
def test_subbuffer(self):
# to force a non-aligned 'i'
expected = struct.pack('=BBi', 0xAB, 0xCD, 0x1234)
size = len(expected)
#
wbuf = MutableStringBuffer(size)
wsubbuf = SubBuffer(wbuf, 2, size-4)
wbuf.setitem(0, chr(0xAB))
wbuf.setitem(1, chr(0xCD))
fake_fmtiter = self.mypack_into('i', wsubbuf, 0x1234)
assert fake_fmtiter.pos == wbuf.getlength()-2 # -2 since it's a SubBuffer
got = wbuf.finish()
assert got == expected
|