PageRenderTime 46ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/module/_rawffi/structure.py

https://bitbucket.org/pypy/pypy/
Python | 409 lines | 339 code | 48 blank | 22 comment | 70 complexity | 83b0a64b6ace0f2037124c7308686273 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. """ Interpreter-level implementation of structure, exposing ll-structure
  2. to app-level with apropriate interface
  3. """
  4. from pypy.interpreter.gateway import interp2app, unwrap_spec
  5. from pypy.interpreter.typedef import interp_attrproperty
  6. from pypy.interpreter.typedef import TypeDef, GetSetProperty
  7. from pypy.interpreter.error import OperationError, oefmt
  8. from pypy.module._rawffi.interp_rawffi import segfault_exception, _MS_WINDOWS
  9. from pypy.module._rawffi.interp_rawffi import W_DataShape, W_DataInstance
  10. from pypy.module._rawffi.interp_rawffi import wrap_value, unwrap_value
  11. from pypy.module._rawffi.interp_rawffi import unpack_shape_with_length
  12. from pypy.module._rawffi.interp_rawffi import LL_TYPEMAP
  13. from pypy.module._rawffi.interp_rawffi import unroll_letters_for_numbers
  14. from pypy.module._rawffi.interp_rawffi import size_alignment
  15. from pypy.module._rawffi.interp_rawffi import read_ptr, write_ptr
  16. from rpython.rlib import clibffi, rgc
  17. from rpython.rlib.rarithmetic import intmask, signedtype, r_uint, \
  18. r_ulonglong
  19. from rpython.rtyper.lltypesystem import lltype, rffi
  20. import sys
  21. IS_BIG_ENDIAN = sys.byteorder == 'big'
  22. def unpack_fields(space, w_fields):
  23. fields_w = space.unpackiterable(w_fields)
  24. fields = []
  25. for w_tup in fields_w:
  26. l_w = space.unpackiterable(w_tup)
  27. len_l = len(l_w)
  28. if len_l < 2 or len_l > 3:
  29. raise oefmt(space.w_ValueError,
  30. "Expected list of 2- or 3-size tuples")
  31. try:
  32. name = space.str_w(l_w[0])
  33. except OperationError:
  34. raise oefmt(space.w_TypeError,
  35. "structure field name must be string not %T", l_w[0])
  36. tp = unpack_shape_with_length(space, l_w[1])
  37. if len_l == 3:
  38. for c in unroll_letters_for_numbers:
  39. if c == tp.itemcode:
  40. break
  41. else:
  42. raise oefmt(space.w_TypeError,
  43. "bit fields not allowed for type")
  44. bitsize = space.int_w(l_w[2])
  45. if bitsize <= 0 or bitsize > tp.size * 8:
  46. raise oefmt(space.w_ValueError,
  47. "number of bits invalid for bit field")
  48. else:
  49. bitsize = 0
  50. fields.append((name, tp, bitsize))
  51. return fields
  52. def round_up(size, alignment):
  53. return (size + alignment - 1) & -alignment
  54. NO_BITFIELD, NEW_BITFIELD, CONT_BITFIELD, EXPAND_BITFIELD = range(4)
  55. def size_alignment_pos(fields, is_union=False, pack=0):
  56. size = 0
  57. alignment = 1
  58. pos = []
  59. bitsizes = []
  60. bitoffset = 0
  61. has_bitfield = False
  62. last_size = 0
  63. for fieldname, fieldtype, bitsize in fields:
  64. # fieldtype is a W_Array
  65. fieldsize = fieldtype.size
  66. fieldalignment = fieldtype.alignment
  67. if pack:
  68. fieldalignment = min(fieldalignment, pack)
  69. alignment = max(alignment, fieldalignment)
  70. if not bitsize:
  71. # not a bit field
  72. field_type = NO_BITFIELD
  73. last_size = 0
  74. bitoffset = 0
  75. elif (last_size and # we have a bitfield open
  76. (not _MS_WINDOWS or fieldsize * 8 == last_size) and
  77. fieldsize * 8 <= last_size and
  78. bitoffset + bitsize <= last_size):
  79. # continue bit field
  80. field_type = CONT_BITFIELD
  81. elif (not _MS_WINDOWS and
  82. last_size and # we have a bitfield open
  83. fieldsize * 8 >= last_size and
  84. bitoffset + bitsize <= fieldsize * 8):
  85. # expand bit field
  86. field_type = EXPAND_BITFIELD
  87. else:
  88. # start new bitfield
  89. field_type = NEW_BITFIELD
  90. has_bitfield = True
  91. bitoffset = 0
  92. last_size = fieldsize * 8
  93. if is_union:
  94. pos.append(0)
  95. bitsizes.append(fieldsize)
  96. size = max(size, fieldsize)
  97. else:
  98. if field_type == NO_BITFIELD:
  99. # the usual case
  100. size = round_up(size, fieldalignment)
  101. pos.append(size)
  102. size += intmask(fieldsize)
  103. bitsizes.append(fieldsize)
  104. elif field_type == NEW_BITFIELD:
  105. if IS_BIG_ENDIAN:
  106. off = last_size - bitoffset - bitsize
  107. else:
  108. off = bitoffset
  109. bitsizes.append((bitsize << 16) + off)
  110. bitoffset = bitsize
  111. size = round_up(size, fieldalignment)
  112. pos.append(size)
  113. size += fieldsize
  114. elif field_type == CONT_BITFIELD:
  115. if IS_BIG_ENDIAN:
  116. off = last_size - bitoffset - bitsize
  117. else:
  118. off = bitoffset
  119. bitsizes.append((bitsize << 16) + off)
  120. bitoffset += bitsize
  121. # offset is already updated for the NEXT field
  122. pos.append(size - fieldsize)
  123. elif field_type == EXPAND_BITFIELD:
  124. size += fieldsize - last_size / 8
  125. last_size = fieldsize * 8
  126. if IS_BIG_ENDIAN:
  127. off = last_size - bitoffset - bitsize
  128. else:
  129. off = bitoffset
  130. bitsizes.append((bitsize << 16) + off)
  131. bitoffset += bitsize
  132. # offset is already updated for the NEXT field
  133. pos.append(size - fieldsize)
  134. if not has_bitfield:
  135. bitsizes = None
  136. size = round_up(size, alignment)
  137. return size, alignment, pos, bitsizes
  138. class W_Structure(W_DataShape):
  139. def __init__(self, space, fields, size, alignment, is_union=False, pack=0):
  140. name_to_index = {}
  141. if fields is not None:
  142. for i in range(len(fields)):
  143. name, tp, bitsize = fields[i]
  144. if name in name_to_index:
  145. raise oefmt(space.w_ValueError,
  146. "duplicate field name %s", name)
  147. name_to_index[name] = i
  148. size, alignment, pos, bitsizes = size_alignment_pos(
  149. fields, is_union, pack)
  150. else: # opaque case
  151. fields = []
  152. pos = []
  153. bitsizes = None
  154. self.fields = fields
  155. self.size = size
  156. self.alignment = alignment
  157. self.ll_positions = pos
  158. self.ll_bitsizes = bitsizes
  159. self.name_to_index = name_to_index
  160. def allocate(self, space, length, autofree=False):
  161. # length is ignored!
  162. if autofree:
  163. return W_StructureInstanceAutoFree(space, self)
  164. return W_StructureInstance(space, self, 0)
  165. def getindex(self, space, attr):
  166. try:
  167. return self.name_to_index[attr]
  168. except KeyError:
  169. raise oefmt(space.w_AttributeError,
  170. "C Structure has no attribute %s", attr)
  171. @unwrap_spec(autofree=bool)
  172. def descr_call(self, space, autofree=False):
  173. return space.wrap(self.allocate(space, 1, autofree))
  174. def descr_repr(self, space):
  175. fieldnames = ' '.join(["'%s'" % name for name, _, _ in self.fields])
  176. return space.wrap("<_rawffi.Structure %s (%d, %d)>" % (fieldnames,
  177. self.size,
  178. self.alignment))
  179. @unwrap_spec(address=r_uint)
  180. def fromaddress(self, space, address):
  181. return space.wrap(W_StructureInstance(space, self, address))
  182. @unwrap_spec(attr=str)
  183. def descr_fieldoffset(self, space, attr):
  184. index = self.getindex(space, attr)
  185. return space.wrap(self.ll_positions[index])
  186. @unwrap_spec(attr=str)
  187. def descr_fieldsize(self, space, attr):
  188. index = self.getindex(space, attr)
  189. if self.ll_bitsizes and index < len(self.ll_bitsizes):
  190. return space.wrap(self.ll_bitsizes[index])
  191. else:
  192. return space.wrap(self.fields[index][1].size)
  193. # get the corresponding ffi_type
  194. ffi_struct = lltype.nullptr(clibffi.FFI_STRUCT_P.TO)
  195. def get_basic_ffi_type(self):
  196. if not self.ffi_struct:
  197. # Repeated fields are delicate. Consider for example
  198. # struct { int a[5]; }
  199. # or struct { struct {int x;} a[5]; }
  200. # Seeing no corresponding doc in clibffi, let's just repeat
  201. # the field 5 times...
  202. fieldtypes = []
  203. for name, tp, bitsize in self.fields:
  204. basic_ffi_type = tp.get_basic_ffi_type()
  205. basic_size, _ = size_alignment(basic_ffi_type)
  206. total_size = tp.size
  207. count = 0
  208. while count + basic_size <= total_size:
  209. fieldtypes.append(basic_ffi_type)
  210. count += basic_size
  211. if basic_size == 0: # corner case. get out of this infinite
  212. break # loop after 1 iteration ("why not")
  213. self.ffi_struct = clibffi.make_struct_ffitype_e(self.size,
  214. self.alignment,
  215. fieldtypes)
  216. return self.ffi_struct.ffistruct
  217. @rgc.must_be_light_finalizer
  218. def __del__(self):
  219. if self.ffi_struct:
  220. lltype.free(self.ffi_struct, flavor='raw')
  221. @unwrap_spec(union=bool, pack=int)
  222. def descr_new_structure(space, w_type, w_shapeinfo, union=False, pack=0):
  223. if pack < 0:
  224. raise oefmt(space.w_ValueError,
  225. "_pack_ must be a non-negative integer")
  226. if space.isinstance_w(w_shapeinfo, space.w_tuple):
  227. w_size, w_alignment = space.fixedview(w_shapeinfo, expected_length=2)
  228. S = W_Structure(space, None, space.int_w(w_size),
  229. space.int_w(w_alignment), union)
  230. else:
  231. fields = unpack_fields(space, w_shapeinfo)
  232. S = W_Structure(space, fields, 0, 0, union, pack)
  233. return space.wrap(S)
  234. W_Structure.typedef = TypeDef(
  235. 'Structure',
  236. __new__ = interp2app(descr_new_structure),
  237. __call__ = interp2app(W_Structure.descr_call),
  238. __repr__ = interp2app(W_Structure.descr_repr),
  239. fromaddress = interp2app(W_Structure.fromaddress),
  240. size = interp_attrproperty('size', W_Structure),
  241. alignment = interp_attrproperty('alignment', W_Structure),
  242. fieldoffset = interp2app(W_Structure.descr_fieldoffset),
  243. fieldsize = interp2app(W_Structure.descr_fieldsize),
  244. size_alignment = interp2app(W_Structure.descr_size_alignment),
  245. get_ffi_type = interp2app(W_Structure.descr_get_ffi_type),
  246. )
  247. W_Structure.typedef.acceptable_as_base_class = False
  248. def LOW_BIT(x):
  249. return x & 0xFFFF
  250. def NUM_BITS(x):
  251. return x >> 16
  252. def BIT_MASK(x, ll_t):
  253. if ll_t is lltype.SignedLongLong or ll_t is lltype.UnsignedLongLong:
  254. one = r_ulonglong(1)
  255. else:
  256. one = r_uint(1)
  257. # to avoid left shift by x == sizeof(ll_t)
  258. return (((one << (x - 1)) - 1) << 1) + 1
  259. BIT_MASK._annspecialcase_ = 'specialize:arg(1)'
  260. def push_field(self, num, value):
  261. ptr = rffi.ptradd(self.ll_buffer, self.shape.ll_positions[num])
  262. TP = lltype.typeOf(value)
  263. # Handle bitfields
  264. for c in unroll_letters_for_numbers:
  265. if LL_TYPEMAP[c] is TP and self.shape.ll_bitsizes:
  266. # Modify the current value with the bitfield changed
  267. bitsize = self.shape.ll_bitsizes[num]
  268. numbits = NUM_BITS(bitsize)
  269. if numbits:
  270. lowbit = LOW_BIT(bitsize)
  271. bitmask = BIT_MASK(numbits, TP)
  272. masktype = lltype.typeOf(bitmask)
  273. value = rffi.cast(masktype, value)
  274. current = rffi.cast(masktype, read_ptr(ptr, 0, TP))
  275. current &= ~(bitmask << lowbit)
  276. current |= (value & bitmask) << lowbit
  277. value = rffi.cast(TP, current)
  278. break
  279. write_ptr(ptr, 0, value)
  280. push_field._annspecialcase_ = 'specialize:argtype(2)'
  281. def cast_pos(self, i, ll_t):
  282. pos = rffi.ptradd(self.ll_buffer, self.shape.ll_positions[i])
  283. value = read_ptr(pos, 0, ll_t)
  284. # Handle bitfields
  285. for c in unroll_letters_for_numbers:
  286. if LL_TYPEMAP[c] is ll_t and self.shape.ll_bitsizes:
  287. bitsize = self.shape.ll_bitsizes[i]
  288. numbits = NUM_BITS(bitsize)
  289. if numbits:
  290. lowbit = LOW_BIT(bitsize)
  291. bitmask = BIT_MASK(numbits, ll_t)
  292. masktype = lltype.typeOf(bitmask)
  293. value = rffi.cast(masktype, value)
  294. value >>= lowbit
  295. value &= bitmask
  296. if ll_t is lltype.Bool or signedtype(ll_t._type):
  297. sign = (value >> (numbits - 1)) & 1
  298. if sign:
  299. value -= bitmask + 1
  300. value = rffi.cast(ll_t, value)
  301. break
  302. return value
  303. cast_pos._annspecialcase_ = 'specialize:arg(2)'
  304. class W_StructureInstance(W_DataInstance):
  305. def __init__(self, space, shape, address):
  306. W_DataInstance.__init__(self, space, shape.size, address)
  307. self.shape = shape
  308. def descr_repr(self, space):
  309. addr = rffi.cast(lltype.Unsigned, self.ll_buffer)
  310. return space.wrap("<_rawffi struct %x>" % (addr,))
  311. @unwrap_spec(attr=str)
  312. def getattr(self, space, attr):
  313. if not self.ll_buffer:
  314. raise segfault_exception(space, "accessing NULL pointer")
  315. i = self.shape.getindex(space, attr)
  316. _, tp, _ = self.shape.fields[i]
  317. return wrap_value(space, cast_pos, self, i, tp.itemcode)
  318. @unwrap_spec(attr=str)
  319. def setattr(self, space, attr, w_value):
  320. if not self.ll_buffer:
  321. raise segfault_exception(space, "accessing NULL pointer")
  322. i = self.shape.getindex(space, attr)
  323. _, tp, _ = self.shape.fields[i]
  324. unwrap_value(space, push_field, self, i, tp.itemcode, w_value)
  325. @unwrap_spec(attr=str)
  326. def descr_fieldaddress(self, space, attr):
  327. i = self.shape.getindex(space, attr)
  328. ptr = rffi.ptradd(self.ll_buffer, self.shape.ll_positions[i])
  329. return space.wrap(rffi.cast(lltype.Unsigned, ptr))
  330. def getrawsize(self):
  331. return self.shape.size
  332. W_StructureInstance.typedef = TypeDef(
  333. 'StructureInstance',
  334. __repr__ = interp2app(W_StructureInstance.descr_repr),
  335. __getattr__ = interp2app(W_StructureInstance.getattr),
  336. __setattr__ = interp2app(W_StructureInstance.setattr),
  337. buffer = GetSetProperty(W_StructureInstance.getbuffer),
  338. free = interp2app(W_StructureInstance.free),
  339. shape = interp_attrproperty('shape', W_StructureInstance),
  340. byptr = interp2app(W_StructureInstance.byptr),
  341. fieldaddress= interp2app(W_StructureInstance.descr_fieldaddress),
  342. )
  343. W_StructureInstance.typedef.acceptable_as_base_class = False
  344. class W_StructureInstanceAutoFree(W_StructureInstance):
  345. def __init__(self, space, shape):
  346. W_StructureInstance.__init__(self, space, shape, 0)
  347. @rgc.must_be_light_finalizer
  348. def __del__(self):
  349. if self.ll_buffer:
  350. self._free()
  351. W_StructureInstanceAutoFree.typedef = TypeDef(
  352. 'StructureInstanceAutoFree',
  353. __repr__ = interp2app(W_StructureInstance.descr_repr),
  354. __getattr__ = interp2app(W_StructureInstance.getattr),
  355. __setattr__ = interp2app(W_StructureInstance.setattr),
  356. buffer = GetSetProperty(W_StructureInstance.getbuffer),
  357. shape = interp_attrproperty('shape', W_StructureInstance),
  358. byptr = interp2app(W_StructureInstance.byptr),
  359. fieldaddress= interp2app(W_StructureInstance.descr_fieldaddress),
  360. )
  361. W_StructureInstanceAutoFree.typedef.acceptable_as_base_class = False