MemoryView.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858
  1. from __future__ import absolute_import
  2. from .Errors import CompileError, error
  3. from . import ExprNodes
  4. from .ExprNodes import IntNode, NameNode, AttributeNode
  5. from . import Options
  6. from .Code import UtilityCode, TempitaUtilityCode
  7. from .UtilityCode import CythonUtilityCode
  8. from . import Buffer
  9. from . import PyrexTypes
  10. from . import ModuleNode
  11. START_ERR = "Start must not be given."
  12. STOP_ERR = "Axis specification only allowed in the 'step' slot."
  13. STEP_ERR = "Step must be omitted, 1, or a valid specifier."
  14. BOTH_CF_ERR = "Cannot specify an array that is both C and Fortran contiguous."
  15. INVALID_ERR = "Invalid axis specification."
  16. NOT_CIMPORTED_ERR = "Variable was not cimported from cython.view"
  17. EXPR_ERR = "no expressions allowed in axis spec, only names and literals."
  18. CF_ERR = "Invalid axis specification for a C/Fortran contiguous array."
  19. ERR_UNINITIALIZED = ("Cannot check if memoryview %s is initialized without the "
  20. "GIL, consider using initializedcheck(False)")
  21. def concat_flags(*flags):
  22. return "(%s)" % "|".join(flags)
  23. format_flag = "PyBUF_FORMAT"
  24. memview_c_contiguous = "(PyBUF_C_CONTIGUOUS | PyBUF_FORMAT)"
  25. memview_f_contiguous = "(PyBUF_F_CONTIGUOUS | PyBUF_FORMAT)"
  26. memview_any_contiguous = "(PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT)"
  27. memview_full_access = "PyBUF_FULL_RO"
  28. #memview_strided_access = "PyBUF_STRIDED_RO"
  29. memview_strided_access = "PyBUF_RECORDS_RO"
  30. MEMVIEW_DIRECT = '__Pyx_MEMVIEW_DIRECT'
  31. MEMVIEW_PTR = '__Pyx_MEMVIEW_PTR'
  32. MEMVIEW_FULL = '__Pyx_MEMVIEW_FULL'
  33. MEMVIEW_CONTIG = '__Pyx_MEMVIEW_CONTIG'
  34. MEMVIEW_STRIDED= '__Pyx_MEMVIEW_STRIDED'
  35. MEMVIEW_FOLLOW = '__Pyx_MEMVIEW_FOLLOW'
  36. _spec_to_const = {
  37. 'direct' : MEMVIEW_DIRECT,
  38. 'ptr' : MEMVIEW_PTR,
  39. 'full' : MEMVIEW_FULL,
  40. 'contig' : MEMVIEW_CONTIG,
  41. 'strided': MEMVIEW_STRIDED,
  42. 'follow' : MEMVIEW_FOLLOW,
  43. }
  44. _spec_to_abbrev = {
  45. 'direct' : 'd',
  46. 'ptr' : 'p',
  47. 'full' : 'f',
  48. 'contig' : 'c',
  49. 'strided' : 's',
  50. 'follow' : '_',
  51. }
  52. memslice_entry_init = "{ 0, 0, { 0 }, { 0 }, { 0 } }"
  53. memview_name = u'memoryview'
  54. memview_typeptr_cname = '__pyx_memoryview_type'
  55. memview_objstruct_cname = '__pyx_memoryview_obj'
  56. memviewslice_cname = u'__Pyx_memviewslice'
  57. def put_init_entry(mv_cname, code):
  58. code.putln("%s.data = NULL;" % mv_cname)
  59. code.putln("%s.memview = NULL;" % mv_cname)
  60. #def axes_to_str(axes):
  61. # return "".join([access[0].upper()+packing[0] for (access, packing) in axes])
  62. def put_acquire_memoryviewslice(lhs_cname, lhs_type, lhs_pos, rhs, code,
  63. have_gil=False, first_assignment=True):
  64. "We can avoid decreffing the lhs if we know it is the first assignment"
  65. assert rhs.type.is_memoryviewslice
  66. pretty_rhs = rhs.result_in_temp() or rhs.is_simple()
  67. if pretty_rhs:
  68. rhstmp = rhs.result()
  69. else:
  70. rhstmp = code.funcstate.allocate_temp(lhs_type, manage_ref=False)
  71. code.putln("%s = %s;" % (rhstmp, rhs.result_as(lhs_type)))
  72. # Allow uninitialized assignment
  73. #code.putln(code.put_error_if_unbound(lhs_pos, rhs.entry))
  74. put_assign_to_memviewslice(lhs_cname, rhs, rhstmp, lhs_type, code,
  75. have_gil=have_gil, first_assignment=first_assignment)
  76. if not pretty_rhs:
  77. code.funcstate.release_temp(rhstmp)
  78. def put_assign_to_memviewslice(lhs_cname, rhs, rhs_cname, memviewslicetype, code,
  79. have_gil=False, first_assignment=False):
  80. if not first_assignment:
  81. code.put_xdecref_memoryviewslice(lhs_cname, have_gil=have_gil)
  82. if not rhs.result_in_temp():
  83. rhs.make_owned_memoryviewslice(code)
  84. code.putln("%s = %s;" % (lhs_cname, rhs_cname))
  85. def get_buf_flags(specs):
  86. is_c_contig, is_f_contig = is_cf_contig(specs)
  87. if is_c_contig:
  88. return memview_c_contiguous
  89. elif is_f_contig:
  90. return memview_f_contiguous
  91. access, packing = zip(*specs)
  92. if 'full' in access or 'ptr' in access:
  93. return memview_full_access
  94. else:
  95. return memview_strided_access
  96. def insert_newaxes(memoryviewtype, n):
  97. axes = [('direct', 'strided')] * n
  98. axes.extend(memoryviewtype.axes)
  99. return PyrexTypes.MemoryViewSliceType(memoryviewtype.dtype, axes)
  100. def broadcast_types(src, dst):
  101. n = abs(src.ndim - dst.ndim)
  102. if src.ndim < dst.ndim:
  103. return insert_newaxes(src, n), dst
  104. else:
  105. return src, insert_newaxes(dst, n)
  106. def valid_memslice_dtype(dtype, i=0):
  107. """
  108. Return whether type dtype can be used as the base type of a
  109. memoryview slice.
  110. We support structs, numeric types and objects
  111. """
  112. if dtype.is_complex and dtype.real_type.is_int:
  113. return False
  114. if dtype is PyrexTypes.c_bint_type:
  115. return False
  116. if dtype.is_struct and dtype.kind == 'struct':
  117. for member in dtype.scope.var_entries:
  118. if not valid_memslice_dtype(member.type):
  119. return False
  120. return True
  121. return (
  122. dtype.is_error or
  123. # Pointers are not valid (yet)
  124. # (dtype.is_ptr and valid_memslice_dtype(dtype.base_type)) or
  125. (dtype.is_array and i < 8 and
  126. valid_memslice_dtype(dtype.base_type, i + 1)) or
  127. dtype.is_numeric or
  128. dtype.is_pyobject or
  129. dtype.is_fused or # accept this as it will be replaced by specializations later
  130. (dtype.is_typedef and valid_memslice_dtype(dtype.typedef_base_type))
  131. )
  132. class MemoryViewSliceBufferEntry(Buffer.BufferEntry):
  133. """
  134. May be used during code generation time to be queried for
  135. shape/strides/suboffsets attributes, or to perform indexing or slicing.
  136. """
  137. def __init__(self, entry):
  138. self.entry = entry
  139. self.type = entry.type
  140. self.cname = entry.cname
  141. self.buf_ptr = "%s.data" % self.cname
  142. dtype = self.entry.type.dtype
  143. self.buf_ptr_type = PyrexTypes.CPtrType(dtype)
  144. self.init_attributes()
  145. def get_buf_suboffsetvars(self):
  146. return self._for_all_ndim("%s.suboffsets[%d]")
  147. def get_buf_stridevars(self):
  148. return self._for_all_ndim("%s.strides[%d]")
  149. def get_buf_shapevars(self):
  150. return self._for_all_ndim("%s.shape[%d]")
  151. def generate_buffer_lookup_code(self, code, index_cnames):
  152. axes = [(dim, index_cnames[dim], access, packing)
  153. for dim, (access, packing) in enumerate(self.type.axes)]
  154. return self._generate_buffer_lookup_code(code, axes)
  155. def _generate_buffer_lookup_code(self, code, axes, cast_result=True):
  156. """
  157. Generate a single expression that indexes the memory view slice
  158. in each dimension.
  159. """
  160. bufp = self.buf_ptr
  161. type_decl = self.type.dtype.empty_declaration_code()
  162. for dim, index, access, packing in axes:
  163. shape = "%s.shape[%d]" % (self.cname, dim)
  164. stride = "%s.strides[%d]" % (self.cname, dim)
  165. suboffset = "%s.suboffsets[%d]" % (self.cname, dim)
  166. flag = get_memoryview_flag(access, packing)
  167. if flag in ("generic", "generic_contiguous"):
  168. # Note: we cannot do cast tricks to avoid stride multiplication
  169. # for generic_contiguous, as we may have to do (dtype *)
  170. # or (dtype **) arithmetic, we won't know which unless
  171. # we check suboffsets
  172. code.globalstate.use_utility_code(memviewslice_index_helpers)
  173. bufp = ('__pyx_memviewslice_index_full(%s, %s, %s, %s)' %
  174. (bufp, index, stride, suboffset))
  175. elif flag == "indirect":
  176. bufp = "(%s + %s * %s)" % (bufp, index, stride)
  177. bufp = ("(*((char **) %s) + %s)" % (bufp, suboffset))
  178. elif flag == "indirect_contiguous":
  179. # Note: we do char ** arithmetic
  180. bufp = "(*((char **) %s + %s) + %s)" % (bufp, index, suboffset)
  181. elif flag == "strided":
  182. bufp = "(%s + %s * %s)" % (bufp, index, stride)
  183. else:
  184. assert flag == 'contiguous', flag
  185. bufp = '((char *) (((%s *) %s) + %s))' % (type_decl, bufp, index)
  186. bufp = '( /* dim=%d */ %s )' % (dim, bufp)
  187. if cast_result:
  188. return "((%s *) %s)" % (type_decl, bufp)
  189. return bufp
  190. def generate_buffer_slice_code(self, code, indices, dst, have_gil,
  191. have_slices, directives):
  192. """
  193. Slice a memoryviewslice.
  194. indices - list of index nodes. If not a SliceNode, or NoneNode,
  195. then it must be coercible to Py_ssize_t
  196. Simply call __pyx_memoryview_slice_memviewslice with the right
  197. arguments, unless the dimension is omitted or a bare ':', in which
  198. case we copy over the shape/strides/suboffsets attributes directly
  199. for that dimension.
  200. """
  201. src = self.cname
  202. code.putln("%(dst)s.data = %(src)s.data;" % locals())
  203. code.putln("%(dst)s.memview = %(src)s.memview;" % locals())
  204. code.put_incref_memoryviewslice(dst)
  205. all_dimensions_direct = all(access == 'direct' for access, packing in self.type.axes)
  206. suboffset_dim_temp = []
  207. def get_suboffset_dim():
  208. # create global temp variable at request
  209. if not suboffset_dim_temp:
  210. suboffset_dim = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
  211. code.putln("%s = -1;" % suboffset_dim)
  212. suboffset_dim_temp.append(suboffset_dim)
  213. return suboffset_dim_temp[0]
  214. dim = -1
  215. new_ndim = 0
  216. for index in indices:
  217. if index.is_none:
  218. # newaxis
  219. for attrib, value in [('shape', 1), ('strides', 0), ('suboffsets', -1)]:
  220. code.putln("%s.%s[%d] = %d;" % (dst, attrib, new_ndim, value))
  221. new_ndim += 1
  222. continue
  223. dim += 1
  224. access, packing = self.type.axes[dim]
  225. if isinstance(index, ExprNodes.SliceNode):
  226. # slice, unspecified dimension, or part of ellipsis
  227. d = dict(locals())
  228. for s in "start stop step".split():
  229. idx = getattr(index, s)
  230. have_idx = d['have_' + s] = not idx.is_none
  231. d[s] = idx.result() if have_idx else "0"
  232. if not (d['have_start'] or d['have_stop'] or d['have_step']):
  233. # full slice (:), simply copy over the extent, stride
  234. # and suboffset. Also update suboffset_dim if needed
  235. d['access'] = access
  236. util_name = "SimpleSlice"
  237. else:
  238. util_name = "ToughSlice"
  239. d['error_goto'] = code.error_goto(index.pos)
  240. new_ndim += 1
  241. else:
  242. # normal index
  243. idx = index.result()
  244. indirect = access != 'direct'
  245. if indirect:
  246. generic = access == 'full'
  247. if new_ndim != 0:
  248. return error(index.pos,
  249. "All preceding dimensions must be "
  250. "indexed and not sliced")
  251. d = dict(
  252. locals(),
  253. wraparound=int(directives['wraparound']),
  254. boundscheck=int(directives['boundscheck']),
  255. )
  256. if d['boundscheck']:
  257. d['error_goto'] = code.error_goto(index.pos)
  258. util_name = "SliceIndex"
  259. _, impl = TempitaUtilityCode.load_as_string(util_name, "MemoryView_C.c", context=d)
  260. code.put(impl)
  261. if suboffset_dim_temp:
  262. code.funcstate.release_temp(suboffset_dim_temp[0])
  263. def empty_slice(pos):
  264. none = ExprNodes.NoneNode(pos)
  265. return ExprNodes.SliceNode(pos, start=none,
  266. stop=none, step=none)
  267. def unellipsify(indices, ndim):
  268. result = []
  269. seen_ellipsis = False
  270. have_slices = False
  271. newaxes = [newaxis for newaxis in indices if newaxis.is_none]
  272. n_indices = len(indices) - len(newaxes)
  273. for index in indices:
  274. if isinstance(index, ExprNodes.EllipsisNode):
  275. have_slices = True
  276. full_slice = empty_slice(index.pos)
  277. if seen_ellipsis:
  278. result.append(full_slice)
  279. else:
  280. nslices = ndim - n_indices + 1
  281. result.extend([full_slice] * nslices)
  282. seen_ellipsis = True
  283. else:
  284. have_slices = have_slices or index.is_slice or index.is_none
  285. result.append(index)
  286. result_length = len(result) - len(newaxes)
  287. if result_length < ndim:
  288. have_slices = True
  289. nslices = ndim - result_length
  290. result.extend([empty_slice(indices[-1].pos)] * nslices)
  291. return have_slices, result, newaxes
  292. def get_memoryview_flag(access, packing):
  293. if access == 'full' and packing in ('strided', 'follow'):
  294. return 'generic'
  295. elif access == 'full' and packing == 'contig':
  296. return 'generic_contiguous'
  297. elif access == 'ptr' and packing in ('strided', 'follow'):
  298. return 'indirect'
  299. elif access == 'ptr' and packing == 'contig':
  300. return 'indirect_contiguous'
  301. elif access == 'direct' and packing in ('strided', 'follow'):
  302. return 'strided'
  303. else:
  304. assert (access, packing) == ('direct', 'contig'), (access, packing)
  305. return 'contiguous'
  306. def get_is_contig_func_name(contig_type, ndim):
  307. assert contig_type in ('C', 'F')
  308. return "__pyx_memviewslice_is_contig_%s%d" % (contig_type, ndim)
  309. def get_is_contig_utility(contig_type, ndim):
  310. assert contig_type in ('C', 'F')
  311. C = dict(context, ndim=ndim, contig_type=contig_type)
  312. utility = load_memview_c_utility("MemviewSliceCheckContig", C, requires=[is_contig_utility])
  313. return utility
  314. def slice_iter(slice_type, slice_result, ndim, code):
  315. if slice_type.is_c_contig or slice_type.is_f_contig:
  316. return ContigSliceIter(slice_type, slice_result, ndim, code)
  317. else:
  318. return StridedSliceIter(slice_type, slice_result, ndim, code)
  319. class SliceIter(object):
  320. def __init__(self, slice_type, slice_result, ndim, code):
  321. self.slice_type = slice_type
  322. self.slice_result = slice_result
  323. self.code = code
  324. self.ndim = ndim
  325. class ContigSliceIter(SliceIter):
  326. def start_loops(self):
  327. code = self.code
  328. code.begin_block()
  329. type_decl = self.slice_type.dtype.empty_declaration_code()
  330. total_size = ' * '.join("%s.shape[%d]" % (self.slice_result, i)
  331. for i in range(self.ndim))
  332. code.putln("Py_ssize_t __pyx_temp_extent = %s;" % total_size)
  333. code.putln("Py_ssize_t __pyx_temp_idx;")
  334. code.putln("%s *__pyx_temp_pointer = (%s *) %s.data;" % (
  335. type_decl, type_decl, self.slice_result))
  336. code.putln("for (__pyx_temp_idx = 0; "
  337. "__pyx_temp_idx < __pyx_temp_extent; "
  338. "__pyx_temp_idx++) {")
  339. return "__pyx_temp_pointer"
  340. def end_loops(self):
  341. self.code.putln("__pyx_temp_pointer += 1;")
  342. self.code.putln("}")
  343. self.code.end_block()
  344. class StridedSliceIter(SliceIter):
  345. def start_loops(self):
  346. code = self.code
  347. code.begin_block()
  348. for i in range(self.ndim):
  349. t = i, self.slice_result, i
  350. code.putln("Py_ssize_t __pyx_temp_extent_%d = %s.shape[%d];" % t)
  351. code.putln("Py_ssize_t __pyx_temp_stride_%d = %s.strides[%d];" % t)
  352. code.putln("char *__pyx_temp_pointer_%d;" % i)
  353. code.putln("Py_ssize_t __pyx_temp_idx_%d;" % i)
  354. code.putln("__pyx_temp_pointer_0 = %s.data;" % self.slice_result)
  355. for i in range(self.ndim):
  356. if i > 0:
  357. code.putln("__pyx_temp_pointer_%d = __pyx_temp_pointer_%d;" % (i, i - 1))
  358. code.putln("for (__pyx_temp_idx_%d = 0; "
  359. "__pyx_temp_idx_%d < __pyx_temp_extent_%d; "
  360. "__pyx_temp_idx_%d++) {" % (i, i, i, i))
  361. return "__pyx_temp_pointer_%d" % (self.ndim - 1)
  362. def end_loops(self):
  363. code = self.code
  364. for i in range(self.ndim - 1, -1, -1):
  365. code.putln("__pyx_temp_pointer_%d += __pyx_temp_stride_%d;" % (i, i))
  366. code.putln("}")
  367. code.end_block()
  368. def copy_c_or_fortran_cname(memview):
  369. if memview.is_c_contig:
  370. c_or_f = 'c'
  371. else:
  372. c_or_f = 'f'
  373. return "__pyx_memoryview_copy_slice_%s_%s" % (
  374. memview.specialization_suffix(), c_or_f)
  375. def get_copy_new_utility(pos, from_memview, to_memview):
  376. if (from_memview.dtype != to_memview.dtype and
  377. not (from_memview.dtype.is_const and from_memview.dtype.const_base_type == to_memview.dtype)):
  378. error(pos, "dtypes must be the same!")
  379. return
  380. if len(from_memview.axes) != len(to_memview.axes):
  381. error(pos, "number of dimensions must be same")
  382. return
  383. if not (to_memview.is_c_contig or to_memview.is_f_contig):
  384. error(pos, "to_memview must be c or f contiguous.")
  385. return
  386. for (access, packing) in from_memview.axes:
  387. if access != 'direct':
  388. error(pos, "cannot handle 'full' or 'ptr' access at this time.")
  389. return
  390. if to_memview.is_c_contig:
  391. mode = 'c'
  392. contig_flag = memview_c_contiguous
  393. elif to_memview.is_f_contig:
  394. mode = 'fortran'
  395. contig_flag = memview_f_contiguous
  396. return load_memview_c_utility(
  397. "CopyContentsUtility",
  398. context=dict(
  399. context,
  400. mode=mode,
  401. dtype_decl=to_memview.dtype.empty_declaration_code(),
  402. contig_flag=contig_flag,
  403. ndim=to_memview.ndim,
  404. func_cname=copy_c_or_fortran_cname(to_memview),
  405. dtype_is_object=int(to_memview.dtype.is_pyobject)),
  406. requires=[copy_contents_new_utility])
  407. def get_axes_specs(env, axes):
  408. '''
  409. get_axes_specs(env, axes) -> list of (access, packing) specs for each axis.
  410. access is one of 'full', 'ptr' or 'direct'
  411. packing is one of 'contig', 'strided' or 'follow'
  412. '''
  413. cythonscope = env.global_scope().context.cython_scope
  414. cythonscope.load_cythonscope()
  415. viewscope = cythonscope.viewscope
  416. access_specs = tuple([viewscope.lookup(name)
  417. for name in ('full', 'direct', 'ptr')])
  418. packing_specs = tuple([viewscope.lookup(name)
  419. for name in ('contig', 'strided', 'follow')])
  420. is_f_contig, is_c_contig = False, False
  421. default_access, default_packing = 'direct', 'strided'
  422. cf_access, cf_packing = default_access, 'follow'
  423. axes_specs = []
  424. # analyse all axes.
  425. for idx, axis in enumerate(axes):
  426. if not axis.start.is_none:
  427. raise CompileError(axis.start.pos, START_ERR)
  428. if not axis.stop.is_none:
  429. raise CompileError(axis.stop.pos, STOP_ERR)
  430. if axis.step.is_none:
  431. axes_specs.append((default_access, default_packing))
  432. elif isinstance(axis.step, IntNode):
  433. # the packing for the ::1 axis is contiguous,
  434. # all others are cf_packing.
  435. if axis.step.compile_time_value(env) != 1:
  436. raise CompileError(axis.step.pos, STEP_ERR)
  437. axes_specs.append((cf_access, 'cfcontig'))
  438. elif isinstance(axis.step, (NameNode, AttributeNode)):
  439. entry = _get_resolved_spec(env, axis.step)
  440. if entry.name in view_constant_to_access_packing:
  441. axes_specs.append(view_constant_to_access_packing[entry.name])
  442. else:
  443. raise CompileError(axis.step.pos, INVALID_ERR)
  444. else:
  445. raise CompileError(axis.step.pos, INVALID_ERR)
  446. # First, find out if we have a ::1 somewhere
  447. contig_dim = 0
  448. is_contig = False
  449. for idx, (access, packing) in enumerate(axes_specs):
  450. if packing == 'cfcontig':
  451. if is_contig:
  452. raise CompileError(axis.step.pos, BOTH_CF_ERR)
  453. contig_dim = idx
  454. axes_specs[idx] = (access, 'contig')
  455. is_contig = True
  456. if is_contig:
  457. # We have a ::1 somewhere, see if we're C or Fortran contiguous
  458. if contig_dim == len(axes) - 1:
  459. is_c_contig = True
  460. else:
  461. is_f_contig = True
  462. if contig_dim and not axes_specs[contig_dim - 1][0] in ('full', 'ptr'):
  463. raise CompileError(axes[contig_dim].pos,
  464. "Fortran contiguous specifier must follow an indirect dimension")
  465. if is_c_contig:
  466. # Contiguous in the last dimension, find the last indirect dimension
  467. contig_dim = -1
  468. for idx, (access, packing) in enumerate(reversed(axes_specs)):
  469. if access in ('ptr', 'full'):
  470. contig_dim = len(axes) - idx - 1
  471. # Replace 'strided' with 'follow' for any dimension following the last
  472. # indirect dimension, the first dimension or the dimension following
  473. # the ::1.
  474. # int[::indirect, ::1, :, :]
  475. # ^ ^
  476. # int[::indirect, :, :, ::1]
  477. # ^ ^
  478. start = contig_dim + 1
  479. stop = len(axes) - is_c_contig
  480. for idx, (access, packing) in enumerate(axes_specs[start:stop]):
  481. idx = contig_dim + 1 + idx
  482. if access != 'direct':
  483. raise CompileError(axes[idx].pos,
  484. "Indirect dimension may not follow "
  485. "Fortran contiguous dimension")
  486. if packing == 'contig':
  487. raise CompileError(axes[idx].pos,
  488. "Dimension may not be contiguous")
  489. axes_specs[idx] = (access, cf_packing)
  490. if is_c_contig:
  491. # For C contiguity, we need to fix the 'contig' dimension
  492. # after the loop
  493. a, p = axes_specs[-1]
  494. axes_specs[-1] = a, 'contig'
  495. validate_axes_specs([axis.start.pos for axis in axes],
  496. axes_specs,
  497. is_c_contig,
  498. is_f_contig)
  499. return axes_specs
  500. def validate_axes(pos, axes):
  501. if len(axes) >= Options.buffer_max_dims:
  502. error(pos, "More dimensions than the maximum number"
  503. " of buffer dimensions were used.")
  504. return False
  505. return True
  506. def is_cf_contig(specs):
  507. is_c_contig = is_f_contig = False
  508. if len(specs) == 1 and specs == [('direct', 'contig')]:
  509. is_c_contig = True
  510. elif (specs[-1] == ('direct','contig') and
  511. all(axis == ('direct','follow') for axis in specs[:-1])):
  512. # c_contiguous: 'follow', 'follow', ..., 'follow', 'contig'
  513. is_c_contig = True
  514. elif (len(specs) > 1 and
  515. specs[0] == ('direct','contig') and
  516. all(axis == ('direct','follow') for axis in specs[1:])):
  517. # f_contiguous: 'contig', 'follow', 'follow', ..., 'follow'
  518. is_f_contig = True
  519. return is_c_contig, is_f_contig
  520. def get_mode(specs):
  521. is_c_contig, is_f_contig = is_cf_contig(specs)
  522. if is_c_contig:
  523. return 'c'
  524. elif is_f_contig:
  525. return 'fortran'
  526. for access, packing in specs:
  527. if access in ('ptr', 'full'):
  528. return 'full'
  529. return 'strided'
  530. view_constant_to_access_packing = {
  531. 'generic': ('full', 'strided'),
  532. 'strided': ('direct', 'strided'),
  533. 'indirect': ('ptr', 'strided'),
  534. 'generic_contiguous': ('full', 'contig'),
  535. 'contiguous': ('direct', 'contig'),
  536. 'indirect_contiguous': ('ptr', 'contig'),
  537. }
  538. def validate_axes_specs(positions, specs, is_c_contig, is_f_contig):
  539. packing_specs = ('contig', 'strided', 'follow')
  540. access_specs = ('direct', 'ptr', 'full')
  541. # is_c_contig, is_f_contig = is_cf_contig(specs)
  542. has_contig = has_follow = has_strided = has_generic_contig = False
  543. last_indirect_dimension = -1
  544. for idx, (access, packing) in enumerate(specs):
  545. if access == 'ptr':
  546. last_indirect_dimension = idx
  547. for idx, (pos, (access, packing)) in enumerate(zip(positions, specs)):
  548. if not (access in access_specs and
  549. packing in packing_specs):
  550. raise CompileError(pos, "Invalid axes specification.")
  551. if packing == 'strided':
  552. has_strided = True
  553. elif packing == 'contig':
  554. if has_contig:
  555. raise CompileError(pos, "Only one direct contiguous "
  556. "axis may be specified.")
  557. valid_contig_dims = last_indirect_dimension + 1, len(specs) - 1
  558. if idx not in valid_contig_dims and access != 'ptr':
  559. if last_indirect_dimension + 1 != len(specs) - 1:
  560. dims = "dimensions %d and %d" % valid_contig_dims
  561. else:
  562. dims = "dimension %d" % valid_contig_dims[0]
  563. raise CompileError(pos, "Only %s may be contiguous and direct" % dims)
  564. has_contig = access != 'ptr'
  565. elif packing == 'follow':
  566. if has_strided:
  567. raise CompileError(pos, "A memoryview cannot have both follow and strided axis specifiers.")
  568. if not (is_c_contig or is_f_contig):
  569. raise CompileError(pos, "Invalid use of the follow specifier.")
  570. if access in ('ptr', 'full'):
  571. has_strided = False
  572. def _get_resolved_spec(env, spec):
  573. # spec must be a NameNode or an AttributeNode
  574. if isinstance(spec, NameNode):
  575. return _resolve_NameNode(env, spec)
  576. elif isinstance(spec, AttributeNode):
  577. return _resolve_AttributeNode(env, spec)
  578. else:
  579. raise CompileError(spec.pos, INVALID_ERR)
  580. def _resolve_NameNode(env, node):
  581. try:
  582. resolved_name = env.lookup(node.name).name
  583. except AttributeError:
  584. raise CompileError(node.pos, INVALID_ERR)
  585. viewscope = env.global_scope().context.cython_scope.viewscope
  586. entry = viewscope.lookup(resolved_name)
  587. if entry is None:
  588. raise CompileError(node.pos, NOT_CIMPORTED_ERR)
  589. return entry
  590. def _resolve_AttributeNode(env, node):
  591. path = []
  592. while isinstance(node, AttributeNode):
  593. path.insert(0, node.attribute)
  594. node = node.obj
  595. if isinstance(node, NameNode):
  596. path.insert(0, node.name)
  597. else:
  598. raise CompileError(node.pos, EXPR_ERR)
  599. modnames = path[:-1]
  600. # must be at least 1 module name, o/w not an AttributeNode.
  601. assert modnames
  602. scope = env
  603. for modname in modnames:
  604. mod = scope.lookup(modname)
  605. if not mod or not mod.as_module:
  606. raise CompileError(
  607. node.pos, "undeclared name not builtin: %s" % modname)
  608. scope = mod.as_module
  609. entry = scope.lookup(path[-1])
  610. if not entry:
  611. raise CompileError(node.pos, "No such attribute '%s'" % path[-1])
  612. return entry
  613. #
  614. ### Utility loading
  615. #
  616. def load_memview_cy_utility(util_code_name, context=None, **kwargs):
  617. return CythonUtilityCode.load(util_code_name, "MemoryView.pyx",
  618. context=context, **kwargs)
  619. def load_memview_c_utility(util_code_name, context=None, **kwargs):
  620. if context is None:
  621. return UtilityCode.load(util_code_name, "MemoryView_C.c", **kwargs)
  622. else:
  623. return TempitaUtilityCode.load(util_code_name, "MemoryView_C.c",
  624. context=context, **kwargs)
  625. def use_cython_array_utility_code(env):
  626. cython_scope = env.global_scope().context.cython_scope
  627. cython_scope.load_cythonscope()
  628. cython_scope.viewscope.lookup('array_cwrapper').used = True
  629. context = {
  630. 'memview_struct_name': memview_objstruct_cname,
  631. 'max_dims': Options.buffer_max_dims,
  632. 'memviewslice_name': memviewslice_cname,
  633. 'memslice_init': memslice_entry_init,
  634. }
  635. memviewslice_declare_code = load_memview_c_utility(
  636. "MemviewSliceStruct",
  637. context=context,
  638. requires=[])
  639. atomic_utility = load_memview_c_utility("Atomics", context)
  640. memviewslice_init_code = load_memview_c_utility(
  641. "MemviewSliceInit",
  642. context=dict(context, BUF_MAX_NDIMS=Options.buffer_max_dims),
  643. requires=[memviewslice_declare_code,
  644. atomic_utility],
  645. )
  646. memviewslice_index_helpers = load_memview_c_utility("MemviewSliceIndex")
  647. typeinfo_to_format_code = load_memview_cy_utility(
  648. "BufferFormatFromTypeInfo", requires=[Buffer._typeinfo_to_format_code])
  649. is_contig_utility = load_memview_c_utility("MemviewSliceIsContig", context)
  650. overlapping_utility = load_memview_c_utility("OverlappingSlices", context)
  651. copy_contents_new_utility = load_memview_c_utility(
  652. "MemviewSliceCopyTemplate",
  653. context,
  654. requires=[], # require cython_array_utility_code
  655. )
  656. view_utility_code = load_memview_cy_utility(
  657. "View.MemoryView",
  658. context=context,
  659. requires=[Buffer.GetAndReleaseBufferUtilityCode(),
  660. Buffer.buffer_struct_declare_code,
  661. Buffer.buffer_formats_declare_code,
  662. memviewslice_init_code,
  663. is_contig_utility,
  664. overlapping_utility,
  665. copy_contents_new_utility,
  666. ModuleNode.capsule_utility_code],
  667. )
  668. view_utility_whitelist = ('array', 'memoryview', 'array_cwrapper',
  669. 'generic', 'strided', 'indirect', 'contiguous',
  670. 'indirect_contiguous')
  671. memviewslice_declare_code.requires.append(view_utility_code)
  672. copy_contents_new_utility.requires.append(view_utility_code)