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
|
"""
The format table for standard sizes and alignments.
"""
# Note: we follow Python 2.5 in being strict about the ranges of accepted
# values when packing.
import struct
from rpython.rlib.objectmodel import specialize
from rpython.rlib.rarithmetic import r_uint, r_longlong, r_ulonglong
from rpython.rlib.rstruct import ieee
from rpython.rlib.rstruct.error import StructError, StructOverflowError
from rpython.rlib.unroll import unrolling_iterable
def pack_pad(fmtiter, count):
fmtiter.result.append_multiple_char('\x00', count)
def pack_char(fmtiter):
string = fmtiter.accept_str_arg()
if len(string) != 1:
raise StructError("expected a string of length 1")
c = string[0] # string->char conversion for the annotator
fmtiter.result.append(c)
def pack_bool(fmtiter):
c = '\x01' if fmtiter.accept_bool_arg() else '\x00'
fmtiter.result.append(c)
def pack_string(fmtiter, count):
string = fmtiter.accept_str_arg()
if len(string) < count:
fmtiter.result.append(string)
fmtiter.result.append_multiple_char('\x00', count - len(string))
else:
fmtiter.result.append_slice(string, 0, count)
def pack_pascal(fmtiter, count):
string = fmtiter.accept_str_arg()
prefix = len(string)
if prefix >= count:
prefix = count - 1
if prefix < 0:
raise StructError("bad '0p' in struct format")
if prefix > 255:
prefixchar = '\xff'
else:
prefixchar = chr(prefix)
fmtiter.result.append(prefixchar)
fmtiter.result.append_slice(string, 0, prefix)
fmtiter.result.append_multiple_char('\x00', count - (1 + prefix))
def make_float_packer(size):
def packer(fmtiter):
fl = fmtiter.accept_float_arg()
try:
return ieee.pack_float(fmtiter.result, fl, size, fmtiter.bigendian)
except OverflowError:
assert size == 4
raise StructOverflowError("float too large for format 'f'")
return packer
# ____________________________________________________________
native_int_size = struct.calcsize("l")
def min_max_acc_method(size, signed):
if signed:
min = -(2 ** (8*size-1))
max = (2 ** (8*size-1)) - 1
if size <= native_int_size:
accept_method = 'accept_int_arg'
min = int(min)
max = int(max)
else:
accept_method = 'accept_longlong_arg'
min = r_longlong(min)
max = r_longlong(max)
else:
min = 0
max = (2 ** (8*size)) - 1
if size < native_int_size:
accept_method = 'accept_int_arg'
elif size == native_int_size:
accept_method = 'accept_uint_arg'
min = r_uint(min)
max = r_uint(max)
else:
accept_method = 'accept_ulonglong_arg'
min = r_ulonglong(min)
max = r_ulonglong(max)
return min, max, accept_method
def make_int_packer(size, signed, _memo={}):
key = size, signed
try:
return _memo[key]
except KeyError:
pass
min, max, accept_method = min_max_acc_method(size, signed)
if size > 1:
plural = "s"
else:
plural = ""
errormsg = "argument out of range for %d-byte%s integer format" % (size,
plural)
unroll_revrange_size = unrolling_iterable(range(size-1, -1, -1))
def pack_int(fmtiter):
method = getattr(fmtiter, accept_method)
value = method()
if not min <= value <= max:
raise StructError(errormsg)
if fmtiter.bigendian:
for i in unroll_revrange_size:
x = (value >> (8*i)) & 0xff
fmtiter.result.append(chr(x))
else:
for i in unroll_revrange_size:
fmtiter.result.append(chr(value & 0xff))
value >>= 8
_memo[key] = pack_int
return pack_int
# ____________________________________________________________
@specialize.argtype(0)
def unpack_pad(fmtiter, count):
fmtiter.read(count)
@specialize.argtype(0)
def unpack_char(fmtiter):
fmtiter.appendobj(fmtiter.read(1))
@specialize.argtype(0)
def unpack_bool(fmtiter):
c = ord(fmtiter.read(1)[0])
fmtiter.appendobj(bool(c))
@specialize.argtype(0)
def unpack_string(fmtiter, count):
fmtiter.appendobj(fmtiter.read(count))
@specialize.argtype(0)
def unpack_pascal(fmtiter, count):
if count == 0:
raise StructError("bad '0p' in struct format")
data = fmtiter.read(count)
end = 1 + ord(data[0])
if end > count:
end = count
fmtiter.appendobj(data[1:end])
def make_float_unpacker(size):
@specialize.argtype(0)
def unpacker(fmtiter):
data = fmtiter.read(size)
fmtiter.appendobj(ieee.unpack_float(data, fmtiter.bigendian))
return unpacker
# ____________________________________________________________
def make_int_unpacker(size, signed, _memo={}):
try:
return _memo[size, signed]
except KeyError:
pass
if signed:
if size <= native_int_size:
inttype = int
else:
inttype = r_longlong
else:
if size < native_int_size:
inttype = int
elif size == native_int_size:
inttype = r_uint
else:
inttype = r_ulonglong
unroll_range_size = unrolling_iterable(range(size))
@specialize.argtype(0)
def unpack_int(fmtiter):
intvalue = inttype(0)
s = fmtiter.read(size)
idx = 0
if fmtiter.bigendian:
for i in unroll_range_size:
x = ord(s[idx])
if signed and i == 0 and x >= 128:
x -= 256
intvalue <<= 8
intvalue |= inttype(x)
idx += 1
else:
for i in unroll_range_size:
x = ord(s[idx])
if signed and i == size - 1 and x >= 128:
x -= 256
intvalue |= inttype(x) << (8*i)
idx += 1
fmtiter.appendobj(intvalue)
_memo[size, signed] = unpack_int
return unpack_int
# ____________________________________________________________
standard_fmttable = {
'x':{ 'size' : 1, 'pack' : pack_pad, 'unpack' : unpack_pad,
'needcount' : True },
'c':{ 'size' : 1, 'pack' : pack_char, 'unpack' : unpack_char},
's':{ 'size' : 1, 'pack' : pack_string, 'unpack' : unpack_string,
'needcount' : True },
'p':{ 'size' : 1, 'pack' : pack_pascal, 'unpack' : unpack_pascal,
'needcount' : True },
'f':{ 'size' : 4, 'pack' : make_float_packer(4),
'unpack' : make_float_unpacker(4)},
'd':{ 'size' : 8, 'pack' : make_float_packer(8),
'unpack' : make_float_unpacker(8)},
'?':{ 'size' : 1, 'pack' : pack_bool, 'unpack' : unpack_bool},
}
for c, size in [('b', 1), ('h', 2), ('i', 4), ('l', 4), ('q', 8)]:
standard_fmttable[c] = {'size': size,
'pack': make_int_packer(size, True),
'unpack': make_int_unpacker(size, True)}
standard_fmttable[c.upper()] = {'size': size,
'pack': make_int_packer(size, False),
'unpack': make_int_unpacker(size, False)}
|