@@ -3282,14 +3282,15 @@ AK_build_pair_ssize_t(Py_ssize_t a, Py_ssize_t b)
32823282 Py_DECREF (py_a );
32833283 return NULL ;
32843284 }
3285+ // steals refs
32853286 PyTuple_SET_ITEM (t , 0 , py_a );
32863287 PyTuple_SET_ITEM (t , 1 , py_b );
32873288 return t ;
32883289}
32893290
32903291// Returns NULL on error. Returns a new reference. Note that a reference is stolen from the PyObject argument.
32913292static inline PyObject *
3292- AK_build_pair_ssize_t_slice (Py_ssize_t a , PyObject * py_b )
3293+ AK_build_pair_ssize_t_pyo (Py_ssize_t a , PyObject * py_b )
32933294{
32943295 if (py_b == NULL ) { // construction failed
32953296 return NULL ;
@@ -3303,6 +3304,7 @@ AK_build_pair_ssize_t_slice(Py_ssize_t a, PyObject* py_b)
33033304 Py_DECREF (t );
33043305 return NULL ;
33053306 }
3307+ // steals refs
33063308 PyTuple_SET_ITEM (t , 0 , py_a );
33073309 PyTuple_SET_ITEM (t , 1 , py_b );
33083310 return t ;
@@ -4264,7 +4266,7 @@ typedef struct BlockIndexRecord {
42644266} BlockIndexRecord ;
42654267
42664268typedef struct BlockIndexObject {
4267- PyObject_VAR_HEAD
4269+ PyObject_HEAD
42684270 Py_ssize_t block_count ;
42694271 Py_ssize_t row_count ;
42704272 Py_ssize_t bir_count ;
@@ -4292,7 +4294,7 @@ AK_BI_item(BlockIndexObject* self, Py_ssize_t i) {
42924294static PyTypeObject BIIterType ;
42934295
42944296typedef struct BIIterObject {
4295- PyObject_VAR_HEAD
4297+ PyObject_HEAD
42964298 BlockIndexObject * bi ;
42974299 bool reversed ;
42984300 Py_ssize_t pos ; // current index state, mutated in-place
@@ -4393,7 +4395,7 @@ BIIterSelector_new(BlockIndexObject *bi,
43934395 );
43944396
43954397typedef struct BIIterSeqObject {
4396- PyObject_VAR_HEAD
4398+ PyObject_HEAD
43974399 BlockIndexObject * bi ;
43984400 bool reversed ;
43994401 PyObject * selector ;
@@ -4484,7 +4486,6 @@ BIIterSeq_iternext_index(BIIterSeqObject *self)
44844486 return t ;
44854487}
44864488
4487-
44884489static PyObject *
44894490BIIterSeq_iternext (BIIterSeqObject * self )
44904491{
@@ -4498,7 +4499,7 @@ BIIterSeq_iternext(BIIterSeqObject *self)
44984499static PyObject *
44994500BIIterSeq_reversed (BIIterSeqObject * self )
45004501{
4501- return BIIterSelector_new (self -> bi , self -> selector , !self -> reversed , BIIS_SEQ , 0 );
4502+ return BIIterSelector_new (self -> bi , self -> selector , !self -> reversed , BIIS_SEQ , false );
45024503}
45034504
45044505static PyObject *
@@ -4529,7 +4530,7 @@ static PyTypeObject BIIterSeqType = {
45294530// BI Iterator slice selection
45304531
45314532typedef struct BIIterSliceObject {
4532- PyObject_VAR_HEAD
4533+ PyObject_HEAD
45334534 BlockIndexObject * bi ;
45344535 bool reversed ;
45354536 PyObject * selector ; // slice
@@ -4580,7 +4581,7 @@ BIIterSlice_iternext(BIIterSliceObject *self) {
45804581static PyObject *
45814582BIIterSlice_reversed (BIIterSliceObject * self )
45824583{
4583- return BIIterSelector_new (self -> bi , self -> selector , !self -> reversed , BIIS_SLICE , 0 );
4584+ return BIIterSelector_new (self -> bi , self -> selector , !self -> reversed , BIIS_SLICE , false );
45844585}
45854586
45864587static PyObject *
@@ -4611,7 +4612,7 @@ static PyTypeObject BIIterSliceType = {
46114612// BI Iterator Boolean array selection
46124613
46134614typedef struct BIIterBooleanObject {
4614- PyObject_VAR_HEAD
4615+ PyObject_HEAD
46154616 BlockIndexObject * bi ;
46164617 bool reversed ;
46174618 PyObject * selector ;
@@ -4701,10 +4702,11 @@ static PyTypeObject BIIterBoolType = {
47014702
47024703//------------------------------------------------------------------------------
47034704// BI Iterator Contigous
4705+
47044706static PyTypeObject BIIterContiguousType ;
47054707
47064708typedef struct BIIterContiguousObject {
4707- PyObject_VAR_HEAD
4709+ PyObject_HEAD
47084710 BlockIndexObject * bi ;
47094711 PyObject * iter ; // own reference to core iterator
47104712 bool reversed ;
@@ -4834,7 +4836,7 @@ BIIterContiguous_iternext(BIIterContiguousObject *self)
48344836 if (self -> last_block == -1 ) { // iter produced no values, terminate
48354837 break ;
48364838 }
4837- return AK_build_pair_ssize_t_slice ( // steals ref
4839+ return AK_build_pair_ssize_t_pyo ( // steals ref
48384840 self -> last_block ,
48394841 AK_build_slice_inclusive (slice_start ,
48404842 self -> last_column ,
@@ -4859,7 +4861,7 @@ BIIterContiguous_iternext(BIIterContiguousObject *self)
48594861 }
48604862 self -> next_block = block ;
48614863 self -> next_column = column ;
4862- return AK_build_pair_ssize_t_slice ( // steals ref
4864+ return AK_build_pair_ssize_t_pyo ( // steals ref
48634865 self -> last_block ,
48644866 AK_build_slice_inclusive (slice_start ,
48654867 self -> last_column ,
@@ -4885,6 +4887,105 @@ static PyTypeObject BIIterContiguousType = {
48854887 .tp_name = "arraykit.BlockIndexContiguousIterator" ,
48864888};
48874889
4890+ //------------------------------------------------------------------------------
4891+ // BI Iterator Block Slice
4892+
4893+ static PyTypeObject BIIterBlockType ;
4894+
4895+ typedef struct BIIterBlockObject {
4896+ PyObject_HEAD
4897+ BlockIndexObject * bi ;
4898+ bool reversed ;
4899+ Py_ssize_t pos ; // current index state, mutated in-place
4900+ PyObject * null_slice ;
4901+ } BIIterBlockObject ;
4902+
4903+ static PyObject *
4904+ BIIterBlock_new (BlockIndexObject * bi , bool reversed ) {
4905+ BIIterBlockObject * bii = PyObject_New (BIIterBlockObject , & BIIterBlockType );
4906+ if (!bii ) {
4907+ return NULL ;
4908+ }
4909+ Py_INCREF ((PyObject * )bi );
4910+ bii -> bi = bi ;
4911+ bii -> reversed = reversed ;
4912+ bii -> pos = 0 ;
4913+
4914+ // create a new ref of the null slice
4915+ PyObject * ns = AK_build_slice (-1 , -1 , 1 ); // get all null; new ref
4916+ if (ns == NULL ) {
4917+ return NULL ;
4918+ }
4919+ bii -> null_slice = ns ;
4920+ return (PyObject * )bii ;
4921+ }
4922+
4923+ static void
4924+ BIIterBlock_dealloc (BIIterBlockObject * self ) {
4925+ Py_DECREF ((PyObject * )self -> bi );
4926+ Py_DECREF (self -> null_slice );
4927+ PyObject_Del ((PyObject * )self );
4928+ }
4929+
4930+ static PyObject *
4931+ BIIterBlock_iter (BIIterBlockObject * self ) {
4932+ Py_INCREF (self );
4933+ return (PyObject * )self ;
4934+ }
4935+
4936+ static PyObject *
4937+ BIIterBlock_iternext (BIIterBlockObject * self ) {
4938+ Py_ssize_t i ;
4939+ if (self -> reversed ) {
4940+ i = self -> bi -> block_count - ++ self -> pos ;
4941+ if (i < 0 ) {
4942+ return NULL ;
4943+ }
4944+ }
4945+ else {
4946+ i = self -> pos ++ ;
4947+ }
4948+ if (self -> bi -> block_count <= i ) {
4949+ return NULL ;
4950+ }
4951+ // AK_build_pair_ssize_t_pyo steals the reference to the object; so incref here
4952+ Py_INCREF (self -> null_slice );
4953+ PyObject * t = AK_build_pair_ssize_t_pyo (i , self -> null_slice ); // return new ref
4954+ if (t == NULL ) {
4955+ // if tuple creation failed need to undo incref
4956+ Py_DECREF (self -> null_slice );
4957+ }
4958+ return t ;
4959+ }
4960+
4961+ static PyObject *
4962+ BIIterBlock_reversed (BIIterBlockObject * self ) {
4963+ return BIIterBlock_new (self -> bi , !self -> reversed );
4964+ }
4965+
4966+ static PyObject *
4967+ BIIterBlock_length_hint (BIIterBlockObject * self ) {
4968+ // this works for reversed as we use self->pos to subtract from length
4969+ Py_ssize_t len = Py_MAX (0 , self -> bi -> block_count - self -> pos );
4970+ return PyLong_FromSsize_t (len );
4971+ }
4972+
4973+ static PyMethodDef BIIterBlock_methods [] = {
4974+ {"__length_hint__" , (PyCFunction )BIIterBlock_length_hint , METH_NOARGS , NULL },
4975+ {"__reversed__" , (PyCFunction )BIIterBlock_reversed , METH_NOARGS , NULL },
4976+ {NULL },
4977+ };
4978+
4979+ static PyTypeObject BIIterBlockType = {
4980+ PyVarObject_HEAD_INIT (NULL , 0 )
4981+ .tp_basicsize = sizeof (BIIterBlockObject ),
4982+ .tp_dealloc = (destructor ) BIIterBlock_dealloc ,
4983+ .tp_iter = (getiterfunc ) BIIterBlock_iter ,
4984+ .tp_iternext = (iternextfunc ) BIIterBlock_iternext ,
4985+ .tp_methods = BIIterBlock_methods ,
4986+ .tp_name = "arraykit.BlockIndexBlockIterator" ,
4987+ };
4988+
48884989//------------------------------------------------------------------------------
48894990
48904991// NOTE: this constructor returns one of three different PyObject types. We do this to consolidate error reporting and type checks.
@@ -5203,6 +5304,7 @@ BlockIndex_register(BlockIndexObject *self, PyObject *value) {
52035304 Py_ssize_t alignment = PyArray_DIM (a , 0 );
52045305 if (self -> row_count == -1 ) {
52055306 self -> row_count = alignment ;
5307+ self -> shape_recache = true; // setting rows, must recache shape
52065308 }
52075309 else if (self -> row_count != alignment ) {
52085310 PyErr_Format (ErrorInitTypeBlocks ,
@@ -5321,12 +5423,17 @@ BlockIndex_setstate(BlockIndexObject *self, PyObject *state)
53215423//------------------------------------------------------------------------------
53225424// getters
53235425
5426+ // Never expose a negative row value to the caller
5427+ #define AK_BI_ROWS (rows ) ((rows) < 0 ? 0 : (rows))
5428+
53245429static PyObject *
53255430BlockIndex_shape_getter (BlockIndexObject * self , void * Py_UNUSED (closure ))
53265431{
53275432 if (self -> shape == NULL || self -> shape_recache ) {
53285433 Py_XDECREF (self -> shape ); // get rid of old if it exists
5329- self -> shape = AK_build_pair_ssize_t (self -> row_count , self -> bir_count );
5434+ self -> shape = AK_build_pair_ssize_t (
5435+ AK_BI_ROWS (self -> row_count ),
5436+ self -> bir_count );
53305437 }
53315438 // shape is not null and shape_recache is false
53325439 Py_INCREF (self -> shape ); // for caller
@@ -5336,7 +5443,7 @@ BlockIndex_shape_getter(BlockIndexObject *self, void* Py_UNUSED(closure))
53365443
53375444static PyObject *
53385445BlockIndex_rows_getter (BlockIndexObject * self , void * Py_UNUSED (closure )){
5339- return PyLong_FromSsize_t (self -> row_count );
5446+ return PyLong_FromSsize_t (AK_BI_ROWS ( self -> row_count ) );
53405447}
53415448
53425449static PyObject *
@@ -5457,13 +5564,18 @@ BlockIndex_get_column(BlockIndexObject *self, PyObject *key){
54575564
54585565static PyObject *
54595566BlockIndex_iter (BlockIndexObject * self ) {
5460- return BIIter_new (self , 0 );
5567+ return BIIter_new (self , false);
5568+ }
5569+
5570+ static PyObject *
5571+ BlockIndex_reversed (BlockIndexObject * self ) {
5572+ return BIIter_new (self , true);
54615573}
54625574
54635575// Given key, return an iterator of a selection.
54645576static PyObject *
54655577BlockIndex_iter_select (BlockIndexObject * self , PyObject * selector ){
5466- return BIIterSelector_new (self , selector , 0 , BIIS_UNKNOWN , 0 );
5578+ return BIIterSelector_new (self , selector , false , BIIS_UNKNOWN , false );
54675579}
54685580
54695581static char * iter_contiguous_kargs_names [] = {
@@ -5478,7 +5590,7 @@ static PyObject*
54785590BlockIndex_iter_contiguous (BlockIndexObject * self , PyObject * args , PyObject * kwargs )
54795591{
54805592 PyObject * selector ;
5481- int ascending = 0 ;
5593+ int ascending = 0 ; // must be int for parsing to "p"
54825594 int reduce = 0 ;
54835595
54845596 if (!PyArg_ParseTupleAndKeywords (args , kwargs ,
@@ -5490,14 +5602,21 @@ BlockIndex_iter_contiguous(BlockIndexObject *self, PyObject *args, PyObject *kwa
54905602 )) {
54915603 return NULL ;
54925604 }
5493- PyObject * iter = BIIterSelector_new (self , selector , 0 , BIIS_UNKNOWN , ascending );
5605+ PyObject * iter = BIIterSelector_new (self , selector , false , BIIS_UNKNOWN , ascending );
54945606 if (iter == NULL ) {
54955607 return NULL ; // exception set
54965608 }
5497- PyObject * biiter = BIIterContiguous_new (self , 0 , iter , reduce ); // might be NULL, steals iter ref
5609+ PyObject * biiter = BIIterContiguous_new (self , false , iter , reduce ); // might be NULL, steals iter ref
54985610 return biiter ;
54995611}
55005612
5613+ // Given key, return an iterator of a selection.
5614+ static PyObject *
5615+ BlockIndex_iter_block (BlockIndexObject * self ){
5616+ return BIIterBlock_new (self , false);
5617+ }
5618+
5619+
55015620//------------------------------------------------------------------------------
55025621// slot / method def
55035622
@@ -5512,6 +5631,7 @@ static PyMethodDef BlockIndex_methods[] = {
55125631 {"__getstate__" , (PyCFunction ) BlockIndex_getstate , METH_NOARGS , NULL },
55135632 {"__setstate__" , (PyCFunction ) BlockIndex_setstate , METH_O , NULL },
55145633 {"__sizeof__" , (PyCFunction ) BlockIndex_sizeof , METH_NOARGS , NULL },
5634+ {"__reversed__" , (PyCFunction ) BlockIndex_reversed , METH_NOARGS , NULL },
55155635 {"to_list" , (PyCFunction )BlockIndex_to_list , METH_NOARGS , NULL },
55165636 {"to_bytes" , (PyCFunction )BlockIndex_to_bytes , METH_NOARGS , NULL },
55175637 {"copy" , (PyCFunction )BlockIndex_copy , METH_NOARGS , NULL },
@@ -5522,6 +5642,7 @@ static PyMethodDef BlockIndex_methods[] = {
55225642 (PyCFunction ) BlockIndex_iter_contiguous ,
55235643 METH_VARARGS | METH_KEYWORDS ,
55245644 NULL },
5645+ {"iter_block" , (PyCFunction ) BlockIndex_iter_block , METH_NOARGS , NULL },
55255646 // {"__getnewargs__", (PyCFunction)BlockIndex_getnewargs, METH_NOARGS, NULL},
55265647 {NULL },
55275648};
@@ -5550,7 +5671,7 @@ static PyTypeObject BlockIndexType = {
55505671//------------------------------------------------------------------------------
55515672
55525673typedef struct {
5553- PyObject_VAR_HEAD
5674+ PyObject_HEAD
55545675 PyObject * array ;
55555676 PyObject * list ;
55565677} ArrayGOObject ;
@@ -5899,6 +6020,8 @@ PyInit__arraykit(void)
58996020 PyType_Ready (& BIIterSeqType ) ||
59006021 PyType_Ready (& BIIterSliceType ) ||
59016022 PyType_Ready (& BIIterBoolType ) ||
6023+ PyType_Ready (& BIIterContiguousType ) ||
6024+ PyType_Ready (& BIIterBlockType ) ||
59026025 PyType_Ready (& ArrayGOType ) ||
59036026 PyModule_AddObject (m , "BlockIndex" , (PyObject * ) & BlockIndexType ) ||
59046027 PyModule_AddObject (m , "ArrayGO" , (PyObject * ) & ArrayGOType ) ||
0 commit comments