Skip to content

Commit 6c97761

Browse files
authored
Merge pull request #131 from static-frame/130/null-slice-iter
`iter_block`
2 parents 4c13cb7 + 1dd270f commit 6c97761

File tree

3 files changed

+198
-28
lines changed

3 files changed

+198
-28
lines changed

src/__init__.pyi

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ _T = tp.TypeVar('_T')
66

77
__version__: str
88

9-
class ErrorInitTypeBlocks:
9+
class ErrorInitTypeBlocks(RuntimeError):
1010
def __init__(self, *args: tp.Any, **kwargs: tp.Any) -> None: ...
1111
def with_traceback(self, tb: Exception) -> Exception: ...
1212
def __setstate__(self) -> None: ...
@@ -45,6 +45,7 @@ class BlockIndex:
4545
def copy(self,) -> 'BlockIndex': ...
4646
def __len__(self,) -> int: ...
4747
def __iter__(self,) -> tp.Iterator[tp.Tuple[int, int]]: ...
48+
def __reversed__(self,) -> tp.Iterator[tp.Tuple[int, int]]: ...
4849
def __getitem__(self, __key: int) -> tp.Tuple[int, int]: ...
4950
def __getstate__(self,) -> tp.Tuple[int, int, int, int, bytes]: ...
5051
def __setstate__(self, state: tp.Tuple[int, int, int, int, bytes]) -> None: ...
@@ -59,7 +60,7 @@ class BlockIndex:
5960
ascending: bool = False,
6061
reduce: bool = False,
6162
) -> tp.Iterator[tp.Tuple[int, tp.Union[slice, int]]]: ...
62-
63+
def iter_block(self) -> tp.Iterator[tp.Tuple[int, slice]]: ...
6364

6465
def iterable_str_to_array_1d(
6566
iterable: tp.Iterable[str],

src/_arraykit.c

Lines changed: 143 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
32913292
static 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

42664268
typedef 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) {
42924294
static PyTypeObject BIIterType;
42934295

42944296
typedef 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

43954397
typedef 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-
44884489
static PyObject *
44894490
BIIterSeq_iternext(BIIterSeqObject *self)
44904491
{
@@ -4498,7 +4499,7 @@ BIIterSeq_iternext(BIIterSeqObject *self)
44984499
static PyObject *
44994500
BIIterSeq_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

45044505
static PyObject *
@@ -4529,7 +4530,7 @@ static PyTypeObject BIIterSeqType = {
45294530
// BI Iterator slice selection
45304531

45314532
typedef 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) {
45804581
static PyObject *
45814582
BIIterSlice_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

45864587
static PyObject *
@@ -4611,7 +4612,7 @@ static PyTypeObject BIIterSliceType = {
46114612
// BI Iterator Boolean array selection
46124613

46134614
typedef 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+
47044706
static PyTypeObject BIIterContiguousType;
47054707

47064708
typedef 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+
53245429
static PyObject *
53255430
BlockIndex_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

53375444
static PyObject *
53385445
BlockIndex_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

53425449
static PyObject *
@@ -5457,13 +5564,18 @@ BlockIndex_get_column(BlockIndexObject *self, PyObject *key){
54575564

54585565
static PyObject*
54595566
BlockIndex_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.
54645576
static PyObject*
54655577
BlockIndex_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

54695581
static char *iter_contiguous_kargs_names[] = {
@@ -5478,7 +5590,7 @@ static PyObject*
54785590
BlockIndex_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

55525673
typedef 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

Comments
 (0)