Compare commits

..

No commits in common. "6a20133587a5bbea9615fc715aa0088ec8a469e4" and "51dce08e31c14a3fc28d5c2ac89abc2af0f9b00b" have entirely different histories.

4 changed files with 62 additions and 253 deletions

View File

@ -35,18 +35,18 @@ typedef struct c11_chunked_array2d {
int chunk_size; int chunk_size;
int chunk_size_log2; int chunk_size_log2;
int chunk_size_mask; int chunk_size_mask;
c11_chunked_array2d_chunks_KV last_visited;
py_TValue default_T; struct {
py_TValue context_builder; py_Ref missing_chunk;
} callbacks;
c11_chunked_array2d_chunks_KV last_visited;
} c11_chunked_array2d; } c11_chunked_array2d;
void c11_chunked_array2d__ctor(c11_chunked_array2d* self, int chunk_size);
void c11_chunked_array2d__dtor(c11_chunked_array2d* self); void c11_chunked_array2d__dtor(c11_chunked_array2d* self);
py_Ref c11_chunked_array2d__get(c11_chunked_array2d* self, int col, int row); py_Ref c11_chunked_array2d__get(c11_chunked_array2d* self, int col, int row);
bool c11_chunked_array2d__set(c11_chunked_array2d* self, int col, int row, py_Ref value) PY_RAISE; void c11_chunked_array2d__set(c11_chunked_array2d* self, int col, int row, py_Ref value);
void c11_chunked_array2d__del(c11_chunked_array2d* self, int col, int row);
void pk__register_chunked_array2d(py_Ref mod);
/* array2d_view */ /* array2d_view */

View File

@ -3,7 +3,7 @@ from linalg import vec2i
Neighborhood = Literal['Moore', 'von Neumann'] Neighborhood = Literal['Moore', 'von Neumann']
class array2d_like[T]: class array2d[T]:
@property @property
def n_cols(self) -> int: ... def n_cols(self) -> int: ...
@property @property
@ -15,39 +15,33 @@ class array2d_like[T]:
@property @property
def numel(self) -> int: ... def numel(self) -> int: ...
def __new__(cls, n_cols: int, n_rows: int, default: T | Callable[[vec2i], T] | None = None): ...
def __eq__(self, other: object) -> array2d[bool]: ... # type: ignore
def __ne__(self, other: object) -> array2d[bool]: ... # type: ignore
def __repr__(self) -> str: ...
def __iter__(self) -> Iterator[tuple[vec2i, T]]: ...
@overload @overload
def is_valid(self, col: int, row: int) -> bool: ... def is_valid(self, col: int, row: int) -> bool: ...
@overload @overload
def is_valid(self, pos: vec2i) -> bool: ... def is_valid(self, pos: vec2i) -> bool: ...
@overload
def __getitem__(self, index: vec2i) -> T: ...
@overload
def __getitem__(self, index: tuple[int, int]) -> T: ...
@overload
def __setitem__(self, index: vec2i, value: T): ...
@overload
def __setitem__(self, index: tuple[int, int], value: T): ...
class array2d_view[T](array2d_like[T]):
origin: vec2i
class array2d[T](array2d_like[T]):
def __new__(cls, n_cols: int, n_rows: int, default: T | Callable[[vec2i], T] | None = None): ...
def __eq__(self, other: object) -> array2d[bool]: ... # type: ignore
def __ne__(self, other: object) -> array2d[bool]: ... # type: ignore
def __iter__(self) -> Iterator[tuple[vec2i, T]]: ...
def get[R](self, col: int, row: int, default: R = None) -> T | R: def get[R](self, col: int, row: int, default: R = None) -> T | R:
"""Gets the value at the given position. If the position is out of bounds, return the default value.""" """Gets the value at the given position. If the position is out of bounds, return the default value."""
@overload
def __getitem__(self, index: tuple[int, int]) -> T: ...
@overload
def __getitem__(self, index: vec2i) -> T: ...
@overload @overload
def __getitem__(self, index: tuple[slice, slice]) -> array2d[T]: ... def __getitem__(self, index: tuple[slice, slice]) -> array2d[T]: ...
@overload @overload
def __getitem__(self, mask: array2d[bool]) -> list[T]: ... def __getitem__(self, mask: array2d[bool]) -> list[T]: ...
@overload @overload
def __setitem__(self, index: tuple[int, int], value: T): ...
@overload
def __setitem__(self, index: vec2i, value: T): ...
@overload
def __setitem__(self, index: tuple[slice, slice], value: int | float | str | bool | None | 'array2d[T]'): ... def __setitem__(self, index: tuple[slice, slice], value: int | float | str | bool | None | 'array2d[T]'): ...
@overload @overload
def __setitem__(self, mask: array2d[bool], value: T): ... def __setitem__(self, mask: array2d[bool], value: T): ...
@ -92,30 +86,30 @@ class array2d[T](array2d_like[T]):
""" """
class chunked_array2d[T, TContext]: class array2d_view[T]:
def __init__( mask: array2d[bool] | None
self,
chunk_size: int,
default: T = None,
context_builder: Callable[[vec2i], TContext] | None = None,
): ...
@property @property
def chunk_size(self) -> int: ... def n_cols(self) -> int: ...
@property
def n_rows(self) -> int: ...
@property
def width(self) -> int: ...
@property
def height(self) -> int: ...
def __getitem__(self, index: vec2i) -> T: ...
def __setitem__(self, index: vec2i, value: T): ...
class chunked_array2d[T]:
def __init__(self, chunk_size: int): ...
def __getitem__(self, index: vec2i) -> T: ... def __getitem__(self, index: vec2i) -> T: ...
def __setitem__(self, index: vec2i, value: T): ... def __setitem__(self, index: vec2i, value: T): ...
def __delitem__(self, index: vec2i): ... def __delitem__(self, index: vec2i): ...
def __iter__(self) -> Iterator[tuple[vec2i, TContext]]: ...
def clear(self) -> None: ... def get[R](self, col: int, row: int, default: R = None) -> T | R: ...
def world_to_chunk(self, world_pos: vec2i) -> tuple[vec2i, vec2i]: ... def view(self, pos: vec2i, width: int, height: int) -> array2d_view[T]:
def add_chunk(self, chunk_pos: vec2i) -> TContext: ... """Return a view of the grid in the given rectangle."""
def remove_chunk(self, chunk_pos: vec2i) -> bool: ...
def get_context(self, chunk_pos: vec2i) -> TContext | None: ...
def view(self) -> array2d_view[T]: ...
def view_rect(self, pos: vec2i, width: int, height: int) -> array2d_view[T]: ...
def view_chunk(self, chunk_pos: vec2i) -> array2d_view[T]: ...
def view_chunks(self, chunk_pos: vec2i, width: int, height: int) -> array2d_view[T]: ...

View File

@ -732,8 +732,6 @@ void pk__add_module_array2d() {
py_printexc(); py_printexc();
c11__abort("failed to execute array2d.py"); c11__abort("failed to execute array2d.py");
} }
pk__register_chunked_array2d(mod);
} }
#undef INC_COUNT #undef INC_COUNT
@ -750,28 +748,20 @@ void pk__add_module_array2d() {
#undef SMALLMAP_T__SOURCE #undef SMALLMAP_T__SOURCE
static py_TValue* c11_chunked_array2d__new_chunk(c11_chunked_array2d* self, c11_vec2i pos) { static py_TValue* c11_chunked_array2d__new_chunk(c11_chunked_array2d* self, c11_vec2i pos) {
int chunk_numel = self->chunk_size * self->chunk_size;
py_TValue* data = PK_MALLOC(sizeof(py_TValue) * chunk_numel);
memset(data, 0, sizeof(py_TValue) * chunk_numel);
#ifndef NDEBUG #ifndef NDEBUG
bool exists = c11_chunked_array2d_chunks__contains(&self->chunks, pos); bool exists = c11_chunked_array2d_chunks__contains(&self->chunks, pos);
assert(!exists); assert(!exists);
#endif #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) return NULL;
data[0] = *py_retval();
} else {
data[0] = *py_None();
}
memset(&data[1], 0, sizeof(py_TValue) * (chunk_numel - 1));
c11_chunked_array2d_chunks__set(&self->chunks, pos, data); c11_chunked_array2d_chunks__set(&self->chunks, pos, data);
self->last_visited.key = pos; self->last_visited.key = pos;
self->last_visited.value = data; self->last_visited.value = data;
return data; return data;
} }
void c11_chunked_array2d__world_to_chunk(c11_chunked_array2d* self, static py_TValue* c11_chunked_array2d__parse_col_row(c11_chunked_array2d* self,
int col, int col,
int row, int row,
c11_vec2i* chunk_pos, c11_vec2i* chunk_pos,
@ -790,16 +780,8 @@ void c11_chunked_array2d__world_to_chunk(c11_chunked_array2d* self,
chunk_pos->y = -((-row) >> self->chunk_size_log2); chunk_pos->y = -((-row) >> self->chunk_size_log2);
local_pos->y = (-row) & self->chunk_size_mask; local_pos->y = (-row) & self->chunk_size_mask;
} }
}
static py_TValue* c11_chunked_array2d__parse_col_row(c11_chunked_array2d* self,
int col,
int row,
c11_vec2i* chunk_pos,
c11_vec2i* local_pos) {
c11_chunked_array2d__world_to_chunk(self, col, row, chunk_pos, local_pos);
py_TValue* data; py_TValue* data;
if(self->last_visited.value != NULL && chunk_pos->_i64 == self->last_visited.key._i64) { if(chunk_pos->_i64 == self->last_visited.key._i64) {
data = self->last_visited.value; data = self->last_visited.value;
} else { } else {
data = c11_chunked_array2d_chunks__get(&self->chunks, *chunk_pos, NULL); data = c11_chunked_array2d_chunks__get(&self->chunks, *chunk_pos, NULL);
@ -808,22 +790,10 @@ static py_TValue* c11_chunked_array2d__parse_col_row(c11_chunked_array2d* self,
self->last_visited.key = *chunk_pos; self->last_visited.key = *chunk_pos;
self->last_visited.value = data; self->last_visited.value = data;
} }
return data + 1; // skip context return data;
} }
static bool chunked_array2d__new__(int argc, py_Ref argv) { void c11_chunked_array2d__ctor(c11_chunked_array2d* self, int chunk_size) {
py_Type cls = py_totype(argv);
py_newobject(py_retval(), cls, 0, sizeof(c11_chunked_array2d));
return true;
}
static bool chunked_array2d__init__(int argc, py_Ref argv) {
c11_chunked_array2d* self = py_touserdata(&argv[0]);
PY_CHECK_ARG_TYPE(1, tp_int);
int chunk_size = py_toint(&argv[1]);
self->default_T = argv[2];
self->context_builder = argv[3];
c11_chunked_array2d_chunks__ctor(&self->chunks); c11_chunked_array2d_chunks__ctor(&self->chunks);
self->chunk_size = chunk_size; self->chunk_size = chunk_size;
switch(chunk_size) { switch(chunk_size) {
@ -839,125 +809,17 @@ static bool chunked_array2d__init__(int argc, py_Ref argv) {
case 1024: self->chunk_size_log2 = 10; break; case 1024: self->chunk_size_log2 = 10; break;
case 2048: self->chunk_size_log2 = 11; break; case 2048: self->chunk_size_log2 = 11; break;
case 4096: self->chunk_size_log2 = 12; break; case 4096: self->chunk_size_log2 = 12; break;
default: return ValueError("invalid chunk_size: %d", chunk_size); default: c11__abort("invalid chunk_size: %d", chunk_size);
} }
self->chunk_size_mask = chunk_size - 1; self->chunk_size_mask = chunk_size - 1;
memset(&self->last_visited, 0, sizeof(c11_chunked_array2d_chunks_KV)); c11_chunked_array2d__new_chunk(self,
py_newnone(py_retval()); (c11_vec2i){
return true; {0, 0}
} });
static bool chunked_array2d__chunk_size(int argc, py_Ref argv) {
c11_chunked_array2d* self = py_touserdata(argv);
py_newint(py_retval(), self->chunk_size);
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) {
py_assign(py_retval(), res);
} else {
py_assign(py_retval(), &self->default_T);
}
return true;
}
static bool chunked_array2d__setitem__(int argc, py_Ref argv) {
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]);
bool ok = c11_chunked_array2d__set(self, pos.x, pos.y, &argv[2]);
if(!ok) return false;
py_newnone(py_retval());
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);
py_newtuple(py_retval(), self->chunks.length);
for(int i = 0; i < self->chunks.length; i++) {
py_TValue* data = c11__getitem(c11_chunked_array2d_chunks_KV, &self->chunks, i).value;
py_tuple_setitem(py_retval(), i, &data[0]);
}
return py_iter(py_retval());
}
static bool chunked_array2d__clear(int argc, py_Ref argv) {
c11_chunked_array2d* self = py_touserdata(argv);
c11_chunked_array2d_chunks__clear(&self->chunks);
self->last_visited.value = NULL;
py_newnone(py_retval());
return true;
}
static bool chunked_array2d__world_to_chunk(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
PY_CHECK_ARG_TYPE(1, tp_vec2);
c11_chunked_array2d* self = py_touserdata(argv);
c11_vec2i pos = py_tovec2i(&argv[1]);
c11_vec2i chunk_pos, local_pos;
c11_chunked_array2d__world_to_chunk(self, pos.x, pos.y, &chunk_pos, &local_pos);
py_newtuple(py_retval(), 2);
py_TValue* data = py_tuple_data(py_retval());
py_newvec2i(&data[0], chunk_pos);
py_newvec2i(&data[1], local_pos);
return true;
}
static bool chunked_array2d__add_chunk(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_TValue* data = c11_chunked_array2d__new_chunk(self, pos);
if(data == NULL) return false;
py_assign(py_retval(), &data[0]); // context
return true;
}
static bool chunked_array2d__remove_chunk(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]);
bool ok = c11_chunked_array2d_chunks__del(&self->chunks, pos);
py_newbool(py_retval(), ok);
return true;
}
static bool chunked_array2d__get_context(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_TValue* data = c11_chunked_array2d_chunks__get(&self->chunks, pos, NULL);
if(data == NULL) {
py_newnone(py_retval());
} else {
py_assign(py_retval(), &data[0]);
}
return true;
} }
void c11_chunked_array2d__dtor(c11_chunked_array2d* self) { void c11_chunked_array2d__dtor(c11_chunked_array2d* self) {
c11__foreach(c11_chunked_array2d_chunks_KV, &self->chunks, p_kv) PK_FREE(p_kv->value); c11__foreach(c11_chunked_array2d_chunks_KV, &self->chunks, p_kv) { PK_FREE(p_kv->value); }
c11_chunked_array2d_chunks__dtor(&self->chunks); c11_chunked_array2d_chunks__dtor(&self->chunks);
} }
@ -970,55 +832,9 @@ py_Ref c11_chunked_array2d__get(c11_chunked_array2d* self, int col, int row) {
return retval; return retval;
} }
bool c11_chunked_array2d__set(c11_chunked_array2d* self, int col, int row, py_Ref value) { void c11_chunked_array2d__set(c11_chunked_array2d* self, int col, int row, py_Ref value) {
c11_vec2i chunk_pos, local_pos; c11_vec2i chunk_pos, local_pos;
py_TValue* data = c11_chunked_array2d__parse_col_row(self, col, row, &chunk_pos, &local_pos); py_TValue* data = c11_chunked_array2d__parse_col_row(self, col, row, &chunk_pos, &local_pos);
if(data == NULL) { if(data == NULL) data = c11_chunked_array2d__new_chunk(self, chunk_pos);
data = c11_chunked_array2d__new_chunk(self, chunk_pos);
if(data == NULL) return false;
}
data[local_pos.y * self->chunk_size + local_pos.x] = *value; data[local_pos.y * self->chunk_size + local_pos.x] = *value;
return true;
}
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[local_pos.y * self->chunk_size + local_pos.x] = *py_NIL();
}
static void c11_chunked_array2d__mark(void* ud) {
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;
for(int j = 0; j < chunk_numel; j++) {
pk__mark_value(data + j);
}
}
}
void pk__register_chunked_array2d(py_Ref mod) {
py_Type cls = py_newtype("chunked_array2d", tp_object, mod, (py_Dtor)c11_chunked_array2d__dtor);
pk__tp_set_marker(cls, c11_chunked_array2d__mark);
py_bindmagic(cls, __new__, chunked_array2d__new__);
py_bind(py_tpobject(cls),
"__init__(self, chunk_size, default=None, context_builder=None)",
chunked_array2d__init__);
py_bindproperty(cls, "chunk_size", chunked_array2d__chunk_size, NULL);
py_bindmagic(cls, __getitem__, chunked_array2d__getitem__);
py_bindmagic(cls, __setitem__, chunked_array2d__setitem__);
py_bindmagic(cls, __delitem__, chunked_array2d__delitem__);
py_bindmagic(cls, __iter__, chunked_array2d__iter__);
py_bindmethod(cls, "clear", chunked_array2d__clear);
py_bindmethod(cls, "world_to_chunk", chunked_array2d__world_to_chunk);
py_bindmethod(cls, "add_chunk", chunked_array2d__add_chunk);
py_bindmethod(cls, "remove_chunk", chunked_array2d__remove_chunk);
py_bindmethod(cls, "get_context", chunked_array2d__get_context);
} }

View File

@ -165,7 +165,7 @@ bool py_importlib_reload(py_GlobalRef module) {
c11_sv path = py_tosv(py_getdict(module, __path__)); c11_sv path = py_tosv(py_getdict(module, __path__));
c11_string* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP); c11_string* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP);
c11_string* filename = c11_string__new3("%s.py", slashed_path->data); c11_string* filename = c11_string__new3("%s.py", slashed_path->data);
char* data = vm->callbacks.importfile(filename->data); const char* data = vm->callbacks.importfile(filename->data);
if(data == NULL) { if(data == NULL) {
c11_string__delete(filename); c11_string__delete(filename);
filename = c11_string__new3("%s%c__init__.py", slashed_path->data, PK_PLATFORM_SEP); filename = c11_string__new3("%s%c__init__.py", slashed_path->data, PK_PLATFORM_SEP);
@ -175,7 +175,6 @@ bool py_importlib_reload(py_GlobalRef module) {
if(data == NULL) return ImportError("module '%v' not found", path); if(data == NULL) return ImportError("module '%v' not found", path);
bool ok = py_exec(data, filename->data, EXEC_MODE, module); bool ok = py_exec(data, filename->data, EXEC_MODE, module);
c11_string__delete(filename); c11_string__delete(filename);
PK_FREE(data);
py_assign(py_retval(), module); py_assign(py_retval(), module);
return ok; return ok;
} }