PageRenderTime 75ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/rpython/rlib/rstruct/standardfmttable.py

https://bitbucket.org/pypy/pypy/
Python | 316 lines | 296 code | 14 blank | 6 comment | 23 complexity | b82640165ce0200eab0e63624bf8cacb MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. """
  2. The format table for standard sizes and alignments.
  3. """
  4. # Note: we follow Python 2.5 in being strict about the ranges of accepted
  5. # values when packing.
  6. import struct
  7. from rpython.rlib.objectmodel import specialize
  8. from rpython.rlib.rarithmetic import r_uint, r_longlong, r_ulonglong
  9. from rpython.rlib.rstruct import ieee
  10. from rpython.rlib.rstruct.error import StructError, StructOverflowError
  11. from rpython.rlib.unroll import unrolling_iterable
  12. from rpython.rlib.strstorage import str_storage_getitem
  13. from rpython.rlib import rarithmetic
  14. from rpython.rtyper.lltypesystem import rffi
  15. native_is_bigendian = struct.pack("=i", 1) == struct.pack(">i", 1)
  16. native_is_ieee754 = float.__getformat__('double').startswith('IEEE')
  17. def pack_pad(fmtiter, count):
  18. fmtiter.result.append_multiple_char('\x00', count)
  19. def pack_char(fmtiter):
  20. string = fmtiter.accept_str_arg()
  21. if len(string) != 1:
  22. raise StructError("expected a string of length 1")
  23. c = string[0] # string->char conversion for the annotator
  24. fmtiter.result.append(c)
  25. def pack_bool(fmtiter):
  26. c = '\x01' if fmtiter.accept_bool_arg() else '\x00'
  27. fmtiter.result.append(c)
  28. def pack_string(fmtiter, count):
  29. string = fmtiter.accept_str_arg()
  30. if len(string) < count:
  31. fmtiter.result.append(string)
  32. fmtiter.result.append_multiple_char('\x00', count - len(string))
  33. else:
  34. fmtiter.result.append_slice(string, 0, count)
  35. def pack_pascal(fmtiter, count):
  36. string = fmtiter.accept_str_arg()
  37. prefix = len(string)
  38. if prefix >= count:
  39. prefix = count - 1
  40. if prefix < 0:
  41. raise StructError("bad '0p' in struct format")
  42. if prefix > 255:
  43. prefixchar = '\xff'
  44. else:
  45. prefixchar = chr(prefix)
  46. fmtiter.result.append(prefixchar)
  47. fmtiter.result.append_slice(string, 0, prefix)
  48. fmtiter.result.append_multiple_char('\x00', count - (1 + prefix))
  49. def make_float_packer(size):
  50. def packer(fmtiter):
  51. fl = fmtiter.accept_float_arg()
  52. try:
  53. return ieee.pack_float(fmtiter.result, fl, size, fmtiter.bigendian)
  54. except OverflowError:
  55. assert size == 4
  56. raise StructOverflowError("float too large for format 'f'")
  57. return packer
  58. # ____________________________________________________________
  59. native_int_size = struct.calcsize("l")
  60. def min_max_acc_method(size, signed):
  61. if signed:
  62. min = -(2 ** (8*size-1))
  63. max = (2 ** (8*size-1)) - 1
  64. if size <= native_int_size:
  65. accept_method = 'accept_int_arg'
  66. min = int(min)
  67. max = int(max)
  68. else:
  69. accept_method = 'accept_longlong_arg'
  70. min = r_longlong(min)
  71. max = r_longlong(max)
  72. else:
  73. min = 0
  74. max = (2 ** (8*size)) - 1
  75. if size < native_int_size:
  76. accept_method = 'accept_int_arg'
  77. elif size == native_int_size:
  78. accept_method = 'accept_uint_arg'
  79. min = r_uint(min)
  80. max = r_uint(max)
  81. else:
  82. accept_method = 'accept_ulonglong_arg'
  83. min = r_ulonglong(min)
  84. max = r_ulonglong(max)
  85. return min, max, accept_method
  86. def make_int_packer(size, signed, _memo={}):
  87. key = size, signed
  88. try:
  89. return _memo[key]
  90. except KeyError:
  91. pass
  92. min, max, accept_method = min_max_acc_method(size, signed)
  93. if size > 1:
  94. plural = "s"
  95. else:
  96. plural = ""
  97. errormsg = "argument out of range for %d-byte%s integer format" % (size,
  98. plural)
  99. unroll_revrange_size = unrolling_iterable(range(size-1, -1, -1))
  100. def pack_int(fmtiter):
  101. method = getattr(fmtiter, accept_method)
  102. value = method()
  103. if not min <= value <= max:
  104. raise StructError(errormsg)
  105. if fmtiter.bigendian:
  106. for i in unroll_revrange_size:
  107. x = (value >> (8*i)) & 0xff
  108. fmtiter.result.append(chr(x))
  109. else:
  110. for i in unroll_revrange_size:
  111. fmtiter.result.append(chr(value & 0xff))
  112. value >>= 8
  113. _memo[key] = pack_int
  114. return pack_int
  115. # ____________________________________________________________
  116. USE_FASTPATH = True # set to False by some tests
  117. ALLOW_SLOWPATH = True # set to False by some tests
  118. class CannotUnpack(Exception):
  119. pass
  120. @specialize.memo()
  121. def unpack_fastpath(TYPE):
  122. @specialize.argtype(0)
  123. def do_unpack_fastpath(fmtiter):
  124. size = rffi.sizeof(TYPE)
  125. strbuf, pos = fmtiter.get_buffer_as_string_maybe()
  126. if strbuf is None or pos % size != 0 or not USE_FASTPATH:
  127. raise CannotUnpack
  128. fmtiter.skip(size)
  129. return str_storage_getitem(TYPE, strbuf, pos)
  130. return do_unpack_fastpath
  131. @specialize.argtype(0)
  132. def unpack_pad(fmtiter, count):
  133. fmtiter.read(count)
  134. @specialize.argtype(0)
  135. def unpack_char(fmtiter):
  136. fmtiter.appendobj(fmtiter.read(1))
  137. @specialize.argtype(0)
  138. def unpack_bool(fmtiter):
  139. c = ord(fmtiter.read(1)[0])
  140. fmtiter.appendobj(bool(c))
  141. @specialize.argtype(0)
  142. def unpack_string(fmtiter, count):
  143. fmtiter.appendobj(fmtiter.read(count))
  144. @specialize.argtype(0)
  145. def unpack_pascal(fmtiter, count):
  146. if count == 0:
  147. raise StructError("bad '0p' in struct format")
  148. data = fmtiter.read(count)
  149. end = 1 + ord(data[0])
  150. if end > count:
  151. end = count
  152. fmtiter.appendobj(data[1:end])
  153. def make_ieee_unpacker(TYPE):
  154. @specialize.argtype(0)
  155. def unpack_ieee(fmtiter):
  156. size = rffi.sizeof(TYPE)
  157. if fmtiter.bigendian != native_is_bigendian or not native_is_ieee754:
  158. # fallback to the very slow unpacking code in ieee.py
  159. data = fmtiter.read(size)
  160. fmtiter.appendobj(ieee.unpack_float(data, fmtiter.bigendian))
  161. return
  162. ## XXX check if the following code is still needed
  163. ## if not str_storage_supported(TYPE):
  164. ## # this happens e.g. on win32 and ARM32: we cannot read the string
  165. ## # content as an array of doubles because it's not properly
  166. ## # aligned. But we can read a longlong and convert to float
  167. ## assert TYPE == rffi.DOUBLE
  168. ## assert rffi.sizeof(TYPE) == 8
  169. ## return unpack_longlong2float(fmtiter)
  170. try:
  171. # fast path
  172. val = unpack_fastpath(TYPE)(fmtiter)
  173. except CannotUnpack:
  174. # slow path, take the slice
  175. input = fmtiter.read(size)
  176. val = str_storage_getitem(TYPE, input, 0)
  177. fmtiter.appendobj(float(val))
  178. return unpack_ieee
  179. @specialize.argtype(0)
  180. def unpack_longlong2float(fmtiter):
  181. from rpython.rlib.rstruct.runpack import runpack
  182. from rpython.rlib.longlong2float import longlong2float
  183. s = fmtiter.read(8)
  184. llval = runpack('q', s) # this is a bit recursive, I know
  185. doubleval = longlong2float(llval)
  186. fmtiter.appendobj(doubleval)
  187. unpack_double = make_ieee_unpacker(rffi.DOUBLE)
  188. unpack_float = make_ieee_unpacker(rffi.FLOAT)
  189. # ____________________________________________________________
  190. def get_rffi_int_type(size, signed):
  191. for TYPE in rffi.platform.numbertype_to_rclass:
  192. if (rffi.sizeof(TYPE) == size and
  193. rarithmetic.is_signed_integer_type(TYPE) == signed):
  194. return TYPE
  195. raise KeyError("Cannot find an int type size=%d, signed=%d" % (size, signed))
  196. def make_int_unpacker(size, signed, _memo={}):
  197. try:
  198. return _memo[size, signed]
  199. except KeyError:
  200. pass
  201. if signed:
  202. if size <= native_int_size:
  203. inttype = int
  204. else:
  205. inttype = r_longlong
  206. else:
  207. if size < native_int_size:
  208. inttype = int
  209. elif size == native_int_size:
  210. inttype = r_uint
  211. else:
  212. inttype = r_ulonglong
  213. unroll_range_size = unrolling_iterable(range(size))
  214. TYPE = get_rffi_int_type(size, signed)
  215. @specialize.argtype(0)
  216. def unpack_int_fastpath_maybe(fmtiter):
  217. if fmtiter.bigendian != native_is_bigendian or not native_is_ieee754: ## or not str_storage_supported(TYPE):
  218. return False
  219. try:
  220. intvalue = unpack_fastpath(TYPE)(fmtiter)
  221. except CannotUnpack:
  222. return False
  223. if not signed and size < native_int_size:
  224. intvalue = rarithmetic.intmask(intvalue)
  225. intvalue = inttype(intvalue)
  226. fmtiter.appendobj(intvalue)
  227. return True
  228. @specialize.argtype(0)
  229. def unpack_int(fmtiter):
  230. if unpack_int_fastpath_maybe(fmtiter):
  231. return
  232. # slow path
  233. if not ALLOW_SLOWPATH:
  234. # we enter here only on some tests
  235. raise ValueError("fastpath not taken :(")
  236. intvalue = inttype(0)
  237. s = fmtiter.read(size)
  238. idx = 0
  239. if fmtiter.bigendian:
  240. for i in unroll_range_size:
  241. x = ord(s[idx])
  242. if signed and i == 0 and x >= 128:
  243. x -= 256
  244. intvalue <<= 8
  245. intvalue |= inttype(x)
  246. idx += 1
  247. else:
  248. for i in unroll_range_size:
  249. x = ord(s[idx])
  250. if signed and i == size - 1 and x >= 128:
  251. x -= 256
  252. intvalue |= inttype(x) << (8*i)
  253. idx += 1
  254. fmtiter.appendobj(intvalue)
  255. _memo[size, signed] = unpack_int
  256. return unpack_int
  257. # ____________________________________________________________
  258. standard_fmttable = {
  259. 'x':{ 'size' : 1, 'pack' : pack_pad, 'unpack' : unpack_pad,
  260. 'needcount' : True },
  261. 'c':{ 'size' : 1, 'pack' : pack_char, 'unpack' : unpack_char},
  262. 's':{ 'size' : 1, 'pack' : pack_string, 'unpack' : unpack_string,
  263. 'needcount' : True },
  264. 'p':{ 'size' : 1, 'pack' : pack_pascal, 'unpack' : unpack_pascal,
  265. 'needcount' : True },
  266. 'f':{ 'size' : 4, 'pack' : make_float_packer(4),
  267. 'unpack' : unpack_float},
  268. 'd':{ 'size' : 8, 'pack' : make_float_packer(8),
  269. 'unpack' : unpack_double},
  270. '?':{ 'size' : 1, 'pack' : pack_bool, 'unpack' : unpack_bool},
  271. }
  272. for c, size in [('b', 1), ('h', 2), ('i', 4), ('l', 4), ('q', 8)]:
  273. standard_fmttable[c] = {'size': size,
  274. 'pack': make_int_packer(size, True),
  275. 'unpack': make_int_unpacker(size, True)}
  276. standard_fmttable[c.upper()] = {'size': size,
  277. 'pack': make_int_packer(size, False),
  278. 'unpack': make_int_unpacker(size, False)}