ソースを参照

refactor `chunked_array2d`

blueloveTH 1 週間 前
コミット
440acd82f8

+ 1 - 1
include/pocketpy/interpreter/array2d.h

@@ -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);

+ 2 - 7
include/typings/array2d.pyi

@@ -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: ...

+ 26 - 61
src/modules/array2d.c

@@ -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
+    if(exists) {
+        ValueError("chunk already exists at pos (%d, %d)", pos.x, pos.y);
+        return NULL;
+    }
     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);
-            return NULL;
-        }
-        data[0] = *py_retval();
-    } else {
-        data[0] = *py_None();
-    }
+    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(data == NULL) return false;
+        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__);
 

+ 0 - 9
tests/901_array2d_extra1.py

@@ -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):

+ 8 - 56
tests/902_chunked_array2d.py

@@ -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
-
-a1,a2=a.world_to_chunk(vec2i(15,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
+assert a[vec2i(17, 16)] == 15
+assert a[vec2i(17, 20)] == default
 
-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()
+a1, _ = a.world_to_chunk(vec2i(16, 16))
 
-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)