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
|
from rpython.rlib import jit
from rpython.rlib.rarithmetic import ovfcheck
from rpython.rlib.rstruct.error import StructError
from rpython.rlib.rstruct.nativefmttable import native_is_bigendian, native_fmttable
from rpython.rlib.rstruct.standardfmttable import standard_fmttable
from rpython.rlib.unroll import unrolling_iterable
class FormatIterator(object):
"""
An iterator-like object that follows format strings step by step.
It provides input to the packer/unpacker and accumulates their output.
The subclasses are specialized for either packing, unpacking, or
just computing the size.
"""
_mixin_ = True
_operate_is_specialized_ = False
@jit.look_inside_iff(lambda self, fmt: jit.isconstant(fmt))
def interpret(self, fmt):
# decode the byte order, size and alignment based on the 1st char
table = unroll_native_fmtdescs
self.bigendian = native_is_bigendian
index = 0
if len(fmt) > 0:
c = fmt[0]
index = 1
if c == '@':
pass
elif c == '=':
table = unroll_standard_fmtdescs
elif c == '<':
table = unroll_standard_fmtdescs
self.bigendian = False
elif c == '>' or c == '!':
table = unroll_standard_fmtdescs
self.bigendian = True
else:
index = 0
# interpret the format string,
# calling self.operate() for each format unit
while index < len(fmt):
c = fmt[index]
index += 1
if c.isspace():
continue
if c.isdigit():
repetitions = ord(c) - ord('0')
while True:
if index == len(fmt):
raise StructError("incomplete struct format")
c = fmt[index]
index += 1
if not c.isdigit():
break
try:
repetitions = ovfcheck(repetitions * 10)
repetitions = ovfcheck(repetitions + (ord(c) -
ord('0')))
except OverflowError:
raise StructError("overflow in item count")
assert repetitions >= 0
else:
repetitions = 1
for fmtdesc in table:
if c == fmtdesc.fmtchar:
if self._operate_is_specialized_:
if fmtdesc.alignment > 1:
self.align(fmtdesc.mask)
self.operate(fmtdesc, repetitions)
break
else:
raise StructError("bad char in struct format")
if not self._operate_is_specialized_:
if fmtdesc.alignment > 1:
self.align(fmtdesc.mask)
self.operate(fmtdesc, repetitions)
self.finished()
def finished(self):
pass
class CalcSizeFormatIterator(FormatIterator):
totalsize = 0
def operate(self, fmtdesc, repetitions):
try:
size = ovfcheck(fmtdesc.size * repetitions)
self.totalsize = ovfcheck(self.totalsize + size)
except OverflowError:
raise StructError("total struct size too long")
def align(self, mask):
pad = (-self.totalsize) & mask
try:
self.totalsize = ovfcheck(self.totalsize + pad)
except OverflowError:
raise StructError("total struct size too long")
class FmtDesc(object):
def __init__(self, fmtchar, attrs):
self.fmtchar = fmtchar
self.alignment = 1 # by default
self.needcount = False # by default
self.__dict__.update(attrs)
self.mask = self.alignment - 1
assert self.alignment & self.mask == 0, (
"this module assumes that all alignments are powers of two")
def _freeze_(self):
return True
def table2desclist(table):
items = table.items()
items.sort()
lst = [FmtDesc(key, attrs) for key, attrs in items]
return unrolling_iterable(lst)
unroll_standard_fmtdescs = table2desclist(standard_fmttable)
unroll_native_fmtdescs = table2desclist(native_fmttable)
|