refactor chunked_array2d

This commit is contained in:
blueloveTH 2026-02-23 00:35:00 +08:00
parent 1d567445ed
commit 440acd82f8
5 changed files with 37 additions and 134 deletions

View File

@ -51,7 +51,7 @@ typedef struct c11_chunked_array2d {
c11_chunked_array2d_chunks_KV last_visited;
py_TValue default_T;
py_TValue context_builder;
bool auto_add_chunk;
} c11_chunked_array2d;
py_Ref c11_chunked_array2d__get(c11_chunked_array2d* self, int col, int row);

View File

@ -136,19 +136,14 @@ class chunked_array2d[T, TContext]:
cls,
chunk_size: int,
default: T | None = None,
context_builder: Callable[[vec2i], TContext] | None = None,
auto_add_chunk: bool = True,
): ...
@property
def chunk_size(self) -> int: ...
@property
def default(self) -> T: ...
@property
def context_builder(self) -> Callable[[vec2i], TContext] | None: ...
def __getitem__(self, index: vec2i) -> T: ...
def __setitem__(self, index: vec2i, value: T): ...
def __delitem__(self, index: vec2i): ...
def __iter__(self) -> Iterator[tuple[vec2i, TContext]]: ...
def __len__(self) -> int: ...
@ -158,7 +153,7 @@ class chunked_array2d[T, TContext]:
def world_to_chunk(self, world_pos: vec2i) -> tuple[vec2i, vec2i]:
"""Converts world position to chunk position and local position."""
def add_chunk(self, chunk_pos: vec2i) -> TContext: ...
def add_chunk(self, chunk_pos: vec2i, context: TContext | None) -> None: ...
def remove_chunk(self, chunk_pos: vec2i) -> bool: ...
def move_chunk(self, src_chunk_pos: vec2i, dst_chunk_pos: vec2i) -> bool: ...
def get_context(self, chunk_pos: vec2i) -> TContext | None: ...

View File

@ -1002,28 +1002,23 @@ static void register_array2d_view(py_Ref mod) {
#include "pocketpy/xmacros/smallmap.h"
#undef SMALLMAP_T__SOURCE
static py_TValue* c11_chunked_array2d__new_chunk(c11_chunked_array2d* self, c11_vec2i pos) {
#ifndef NDEBUG
static py_TValue* c11_chunked_array2d__new_chunk(c11_chunked_array2d* self, c11_vec2i pos, py_Ref context) {
bool exists = c11_chunked_array2d_chunks__contains(&self->chunks, pos);
assert(!exists);
#endif
int chunk_numel = self->chunk_size * self->chunk_size + 1;
py_TValue* data = PK_MALLOC(sizeof(py_TValue) * chunk_numel);
if(!py_isnone(&self->context_builder)) {
py_newvec2i(&data[0], pos);
bool ok = py_call(&self->context_builder, 1, &data[0]);
if(!ok) {
PK_FREE(data);
if(exists) {
ValueError("chunk already exists at pos (%d, %d)", pos.x, pos.y);
return NULL;
}
data[0] = *py_retval();
} else {
data[0] = *py_None();
}
int chunk_numel = self->chunk_size * self->chunk_size + 1;
py_TValue* data = PK_MALLOC(sizeof(py_TValue) * chunk_numel);
data[0] = *context;
memset(&data[1], 0, sizeof(py_TValue) * (chunk_numel - 1));
c11_chunked_array2d_chunks__set(&self->chunks, pos, data);
self->last_visited.key = pos;
self->last_visited.value = data;
// init data with default value
for(int i = 1; i < chunk_numel; i++) {
data[i] = self->default_T;
}
return data;
}
@ -1077,37 +1072,34 @@ static py_TValue* c11_chunked_array2d__parse_col_row(c11_chunked_array2d* self,
py_Ref c11_chunked_array2d__get(c11_chunked_array2d* self, int col, int row) {
c11_vec2i chunk_pos, local_pos;
py_TValue* data = c11_chunked_array2d__parse_col_row(self, col, row, &chunk_pos, &local_pos);
if(data == NULL) return &self->default_T;
py_Ref retval = &data[1 + local_pos.y * self->chunk_size + local_pos.x];
if(py_isnil(retval)) return &self->default_T;
return retval;
if(data == NULL) return NULL;
return &data[1 + local_pos.y * self->chunk_size + local_pos.x];
}
bool c11_chunked_array2d__set(c11_chunked_array2d* self, int col, int row, py_Ref value) {
c11_vec2i chunk_pos, local_pos;
py_TValue* data = c11_chunked_array2d__parse_col_row(self, col, row, &chunk_pos, &local_pos);
if(data == NULL) {
data = c11_chunked_array2d__new_chunk(self, chunk_pos);
if(self->auto_add_chunk) {
data = c11_chunked_array2d__new_chunk(self, chunk_pos, py_None());
if(data == NULL) return false;
} else {
return IndexError("(%d, %d) is out of bounds and !auto_add_chunk", col, row);
}
}
data[1 + local_pos.y * self->chunk_size + local_pos.x] = *value;
return true;
}
static void c11_chunked_array2d__del(c11_chunked_array2d* self, int col, int row) {
c11_vec2i chunk_pos, local_pos;
py_TValue* data = c11_chunked_array2d__parse_col_row(self, col, row, &chunk_pos, &local_pos);
if(data != NULL) data[1 + local_pos.y * self->chunk_size + local_pos.x] = *py_NIL();
}
static bool chunked_array2d__new__(int argc, py_Ref argv) {
PY_CHECK_ARGC(4);
PY_CHECK_ARG_TYPE(1, tp_int);
PY_CHECK_ARG_TYPE(3, tp_bool);
py_Type cls = py_totype(argv);
c11_chunked_array2d* self = py_newobject(py_retval(), cls, 0, sizeof(c11_chunked_array2d));
int chunk_size = py_toint(&argv[1]);
self->default_T = argv[2];
self->context_builder = argv[3];
self->auto_add_chunk = py_tobool(&argv[3]);
c11_chunked_array2d_chunks__ctor(&self->chunks);
self->chunk_size = chunk_size;
switch(chunk_size) {
@ -1137,26 +1129,13 @@ static bool chunked_array2d_chunk_size(int argc, py_Ref argv) {
return true;
}
static bool chunked_array2d_default(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
c11_chunked_array2d* self = py_touserdata(argv);
py_assign(py_retval(), &self->default_T);
return true;
}
static bool chunked_array2d_context_builder(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
c11_chunked_array2d* self = py_touserdata(argv);
py_assign(py_retval(), &self->context_builder);
return true;
}
static bool chunked_array2d__getitem__(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
PY_CHECK_ARG_TYPE(1, tp_vec2i);
c11_chunked_array2d* self = py_touserdata(argv);
c11_vec2i pos = py_tovec2i(&argv[1]);
py_Ref res = c11_chunked_array2d__get(self, pos.x, pos.y);
if(res == NULL) return IndexError("(%d, %d) is out of bounds", pos.x, pos.y);
py_assign(py_retval(), res);
return true;
}
@ -1172,16 +1151,6 @@ static bool chunked_array2d__setitem__(int argc, py_Ref argv) {
return true;
}
static bool chunked_array2d__delitem__(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
PY_CHECK_ARG_TYPE(1, tp_vec2i);
c11_chunked_array2d* self = py_touserdata(argv);
c11_vec2i pos = py_tovec2i(&argv[1]);
c11_chunked_array2d__del(self, pos.x, pos.y);
py_newnone(py_retval());
return true;
}
static bool chunked_array2d__iter__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
c11_chunked_array2d* self = py_touserdata(argv);
@ -1258,13 +1227,13 @@ static bool chunked_array2d_world_to_chunk(int argc, py_Ref argv) {
}
static bool chunked_array2d_add_chunk(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
PY_CHECK_ARGC(3);
PY_CHECK_ARG_TYPE(1, tp_vec2i);
c11_chunked_array2d* self = py_touserdata(argv);
c11_vec2i pos = py_tovec2i(&argv[1]);
py_TValue* data = c11_chunked_array2d__new_chunk(self, pos);
py_TValue* data = c11_chunked_array2d__new_chunk(self, pos, &argv[2]);
if(data == NULL) return false;
py_assign(py_retval(), &data[0]); // context
py_newnone(py_retval());
return true;
}
@ -1313,7 +1282,7 @@ static bool chunked_array2d_get_context(int argc, py_Ref argv) {
c11_vec2i pos = py_tovec2i(&argv[1]);
py_TValue* data = c11_chunked_array2d_chunks__get(&self->chunks, pos, NULL);
if(data == NULL) {
py_newnone(py_retval());
return IndexError("no chunk found at (%d, %d)", pos.x, pos.y);
} else {
py_assign(py_retval(), &data[0]);
}
@ -1328,7 +1297,6 @@ void c11_chunked_array2d__dtor(c11_chunked_array2d* self) {
void c11_chunked_array2d__mark(void* ud, c11_vector* p_stack) {
c11_chunked_array2d* self = ud;
pk__mark_value(&self->default_T);
pk__mark_value(&self->context_builder);
int chunk_numel = self->chunk_size * self->chunk_size + 1;
for(int i = 0; i < self->chunks.length; i++) {
py_TValue* data = c11__getitem(c11_chunked_array2d_chunks_KV, &self->chunks, i).value;
@ -1408,16 +1376,13 @@ static void register_chunked_array2d(py_Ref mod) {
assert(type == tp_chunked_array2d);
py_bind(py_tpobject(type),
"__new__(cls, chunk_size, default=None, context_builder=None)",
"__new__(cls, chunk_size, default=None, auto_add_chunk=True)",
chunked_array2d__new__);
py_bindproperty(type, "chunk_size", chunked_array2d_chunk_size, NULL);
py_bindproperty(type, "default", chunked_array2d_default, NULL);
py_bindproperty(type, "context_builder", chunked_array2d_context_builder, NULL);
py_bindmagic(type, __getitem__, chunked_array2d__getitem__);
py_bindmagic(type, __setitem__, chunked_array2d__setitem__);
py_bindmagic(type, __delitem__, chunked_array2d__delitem__);
py_bindmagic(type, __iter__, chunked_array2d__iter__);
py_bindmagic(type, __len__, chunked_array2d__len__);

View File

@ -93,15 +93,6 @@ assert data.view() == array2d.fromlist([
assert data.view()[vec2i(1,1)-data.view().origin] == data[vec2i(1,1)]
assert data.view()[vec2i(3,3)-data.view().origin] == data[vec2i(3,3)]
# ====chunked_array2d__delitem__
data = chunked_array2d(4)
for i in range(10):
for j in range(10):
data[vec2i(i,j)] = 10
del data[vec2i(0,0)]
assert data[vec2i(0,0)] == data.default
# ====chunked_array2d__len__
data = chunked_array2d(4)
for i in range(10):

View File

@ -2,66 +2,18 @@ import array2d
from vmath import vec2i
def on_builder(a:vec2i):
return str(a)
pass
default = 0
a = array2d.chunked_array2d(16, default,on_builder)
a = array2d.chunked_array2d(16, default, auto_add_chunk=False)
assert a.chunk_size == 16
a.add_chunk(vec2i(1, 1), 5.0)
a[vec2i(16, 16)] = 16
a[vec2i(15, 16)] = 15
a[vec2i(17, 16)] = 15
assert a[vec2i(16, 16)] == 16
assert a[vec2i(15, 16)] == 15
assert a[vec2i(16, 15)] == default
assert a[vec2i(17, 16)] == 15
assert a[vec2i(17, 20)] == default
a1,a2=a.world_to_chunk(vec2i(15,16))
a1, _ = a.world_to_chunk(vec2i(16, 16))
assert a.remove_chunk(a1)== True
assert a[vec2i(15, 16)] == default
assert a.get_context(vec2i(1,1))==on_builder(vec2i(1,1))
assert a.view().tolist()==[
[16 if i==0 and j==0 else 0 for j in range(16)] for i in range(16)
]
assert a.view_rect(vec2i(15,15),4,4).tolist()==[
[0,0,0,0],
[0,16,0,0],
[0,0,0,0],
[0,0,0,0]
]
a[vec2i(15, 16)] = 15
assert a.view_chunk(a1).tolist()==[
[15 if i==0 and j==15 else 0 for j in range(16)] for i in range(16)
]
a.clear()
assert a[vec2i(16, 16)] == default
assert a[vec2i(15, 16)] == default
assert a[vec2i(16, 15)] == default
from typing import Any
a = array2d.chunked_array2d[int, Any](4, default=0, context_builder=lambda x: 1)
assert a.chunk_size == 4
assert a.add_chunk(vec2i(0, 1)) == 1
assert a.get_context(vec2i(0, 1)) == 1
assert a.move_chunk(vec2i(2, 1), vec2i(1, 1)) == False
assert a.move_chunk(vec2i(0, 1), vec2i(1, 1)) == True
assert a.get_context(vec2i(1, 1)) == 1
assert a.get_context(vec2i(0, 1)) == None
b = a.copy()
assert a is not b
assert a.chunk_size == b.chunk_size
assert a.default == b.default
assert a.context_builder == b.context_builder
assert (a.view() == b.view()).all()
for pos, ctx in a:
assert b.get_context(pos) == ctx
assert a.get_context(vec2i(1,1)) == 5.0
assert a.remove_chunk(a1)