Compare commits

..

No commits in common. "7d6b911a306f842a4f0b10bb1dc05dc77ab323b9" and "a566ca01f9192dcdcb4464430269501c6af6992d" have entirely different histories.

12 changed files with 641 additions and 1195 deletions

View File

@ -22,7 +22,7 @@ os.chdir("build")
code = os.system(f"cmake .. -DPK_ENABLE_OS=ON -DPK_BUILD_MODULE_LZ4=ON -DPK_BUILD_MODULE_LIBHV=ON -DCMAKE_BUILD_TYPE={config} {extra_flags}")
assert code == 0
code = os.system(f"cmake --build . --config {config} -j 4")
code = os.system(f"cmake --build . --config {config}")
assert code == 0
if sys.platform == "win32":

View File

@ -1,58 +1,21 @@
#pragma once
#include "pocketpy/pocketpy.h"
#include "pocketpy/common/smallmap.h"
#include "pocketpy/objects/base.h"
typedef struct c11_array2d_like {
#include "pocketpy/common/utils.h"
#include "pocketpy/common/sstream.h"
#include "pocketpy/interpreter/vm.h"
typedef struct c11_array2d {
py_TValue* data; // slots
int n_cols;
int n_rows;
int numel;
py_Ref (*f_get)(struct c11_array2d_like* self, int col, int row);
bool (*f_set)(struct c11_array2d_like* self, int col, int row, py_Ref value);
} c11_array2d_like;
typedef struct c11_array2d_like_iterator {
c11_array2d_like* array;
int j;
int i;
} c11_array2d_like_iterator;
typedef struct c11_array2d {
c11_array2d_like header;
py_TValue* data; // slots
} c11_array2d;
typedef struct c11_array2d_view {
c11_array2d_like header;
void* ctx;
py_Ref (*f_get)(void* ctx, int col, int row);
bool (*f_set)(void* ctx, int col, int row, py_Ref value);
c11_vec2i origin;
} c11_array2d_view;
typedef struct c11_array2d_iterator {
c11_array2d* array;
int index;
} c11_array2d_iterator;
c11_array2d* py_newarray2d(py_OutRef out, int n_cols, int n_rows);
/* chunked_array2d */
#define SMALLMAP_T__HEADER
#define K c11_vec2i
#define V py_TValue*
#define NAME c11_chunked_array2d_chunks
#define less(a, b) (a._i64 < b._i64)
#define equal(a, b) (a._i64 == b._i64)
#include "pocketpy/xmacros/smallmap.h"
#undef SMALLMAP_T__HEADER
typedef struct c11_chunked_array2d {
c11_chunked_array2d_chunks chunks;
int chunk_size;
int chunk_size_log2;
int chunk_size_mask;
c11_chunked_array2d_chunks_KV last_visited;
py_TValue default_T;
py_TValue context_builder;
} c11_chunked_array2d;
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);

View File

@ -1,11 +1,8 @@
#pragma once
#include <stdint.h>
typedef union c11_vec2i {
struct { int x, y; };
int data[2];
int64_t _i64;
} c11_vec2i;
typedef union c11_vec3i {

View File

@ -302,14 +302,10 @@ PK_API const char* py_tpname(py_Type type);
/// Call a type to create a new instance.
PK_API bool py_tpcall(py_Type type, int argc, py_Ref argv) PY_RAISE PY_RETURN;
/// Check if the object is an instance of the given type exactly.
/// Check if the object is an instance of the given type.
/// Raise `TypeError` if the check fails.
PK_API bool py_checktype(py_Ref self, py_Type type) PY_RAISE;
/// Check if the object is an instance of the given type or its subclass.
/// Raise `TypeError` if the check fails.
PK_API bool py_checkinstance(py_Ref self, py_Type type) PY_RAISE;
#define py_checkint(self) py_checktype(self, tp_int)
#define py_checkfloat(self) py_checktype(self, tp_float)
#define py_checkbool(self) py_checktype(self, tp_bool)
@ -741,11 +737,8 @@ enum py_PredefinedTypes {
tp_vec3i,
tp_mat3x3,
/* array2d */
tp_array2d_like,
tp_array2d_like_iterator,
tp_array2d,
tp_array2d_view,
tp_chunked_array2d,
tp_array2d_iterator,
};
#ifdef __cplusplus

View File

@ -1,9 +1,9 @@
from typing import Callable, Literal, overload, Iterator
from typing import Callable, Any, Generic, TypeVar, Literal, overload, Iterator
from linalg import vec2i
Neighborhood = Literal['Moore', 'von Neumann']
class array2d_like[T]:
class array2d[T]:
@property
def n_cols(self) -> int: ...
@property
@ -13,120 +13,74 @@ class array2d_like[T]:
@property
def height(self) -> int: ...
@property
def shape(self) -> vec2i: ...
@property
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
def is_valid(self, col: int, row: int) -> bool: ...
@overload
def is_valid(self, pos: vec2i) -> bool: ...
def get[R](self, col: int, row: int, default: R = None) -> T | R:
"""Get 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."""
def render(self) -> str: ...
def all(self: array2d_like[bool]) -> bool: ...
def any(self: array2d_like[bool]) -> bool: ...
def map[R](self, f: Callable[[T], R]) -> array2d[R]: ...
def apply(self, f: Callable[[T], T]) -> None: ...
def copy(self) -> 'array2d[T]': ...
def tolist(self) -> list[list[T]]: ...
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 __repr__(self) -> str: ...
@overload
def __getitem__(self, index: vec2i) -> T: ...
@overload
def __getitem__(self, index: tuple[int, int]) -> T: ...
@overload
def __getitem__(self, index: tuple[slice, slice]) -> array2d_view[T]: ...
def __getitem__(self, index: vec2i) -> T: ...
@overload
def __getitem__(self, mask: array2d_like[bool]) -> list[T]: ...
def __getitem__(self, index: tuple[slice, slice]) -> array2d[T]: ...
@overload
def __setitem__(self, index: vec2i, value: T): ...
def __getitem__(self, mask: array2d[bool]) -> list[T]: ...
@overload
def __setitem__(self, index: tuple[int, int], value: T): ...
@overload
def __setitem__(self, index: tuple[slice, slice], value: T | 'array2d_like[T]'): ...
def __setitem__(self, index: vec2i, value: T): ...
@overload
def __setitem__(self, mask: array2d_like[bool], value: T): ...
def __setitem__(self, index: tuple[slice, slice], value: int | float | str | bool | None | 'array2d[T]'): ...
@overload
def __setitem__(self, mask: array2d[bool], value: T): ...
def map[R](self, f: Callable[[T], R]) -> array2d[R]: ...
def copy(self) -> 'array2d[T]': ...
def fill_(self, value: T) -> None: ...
def apply_(self, f: Callable[[T], T]) -> None: ...
def copy_(self, other: array2d[T] | list[T]) -> None: ...
def render(self) -> str: ...
def all(self: array2d[bool]) -> bool: ...
def any(self: array2d[bool]) -> bool: ...
@staticmethod
def fromlist(data: list[list[T]]) -> array2d[T]: ...
def tolist(self) -> list[list[T]]: ...
# algorithms
def count(self, value: T) -> int:
"""Count the number of cells with the given value."""
"""Counts the number of cells with the given value."""
def count_neighbors(self, value: T, neighborhood: Neighborhood) -> array2d[int]:
"""Count the number of neighbors with the given value for each cell."""
"""Counts the number of neighbors with the given value for each cell."""
def get_bounding_rect(self, value: T) -> tuple[int, int, int, int]:
"""Get the bounding rectangle of the given value.
"""Gets the bounding rectangle of the given value.
Returns a tuple `(x, y, width, height)` or raise `ValueError` if the value is not found.
"""
def convolve(self: array2d_like[int], kernel: array2d_like[int], padding: int) -> array2d[int]:
"""Convolve the array with the given kernel."""
def convolve(self: array2d[int], kernel: array2d[int], padding: int) -> array2d[int]:
"""Convolves the array with the given kernel."""
def get_connected_components(self, value: T, neighborhood: Neighborhood) -> tuple[array2d[int], int]:
"""Get connected components of the grid.
"""Gets connected components of the grid.
Returns the `visited` array and the number of connected components,
where `0` means unvisited, and non-zero means the index of the connected component.
"""
class array2d_view[T](array2d_like[T]):
@property
def origin(self) -> vec2i: ...
class array2d[T](array2d_like[T]):
def __new__(
cls,
n_cols: int,
n_rows: int,
default: T | Callable[[vec2i], T] | None = None
): ...
@staticmethod
def fromlist(data: list[list[T]]) -> array2d[T]: ...
class chunked_array2d[T, TContext]:
def __init__(
self,
chunk_size: int,
default: T = None,
context_builder: Callable[[vec2i], TContext] | None = None,
): ...
@property
def chunk_size(self) -> int: ...
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 clear(self) -> None: ...
def world_to_chunk(self, world_pos: vec2i) -> tuple[vec2i, vec2i]:
"""Convert world position to chunk position and local position."""
def add_chunk(self, chunk_pos: vec2i) -> TContext: ...
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]: ...

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,6 @@
#include "pocketpy/common/utils.h"
#include "pocketpy/common/sstream.h"
#include "pocketpy/interpreter/vm.h"
#include "pocketpy/interpreter/array2d.h"
#include <stdint.h>
@ -325,16 +324,16 @@ static bool pkl__write_object(PickleObject* buf, py_TValue* obj) {
return true;
else {
c11_array2d* arr = py_touserdata(obj);
for(int i = 0; i < arr->header.numel; i++) {
for(int i = 0; i < arr->numel; i++) {
if(arr->data[i].is_ptr)
return TypeError(
"'array2d' object is not picklable because it contains heap-allocated objects");
buf->used_types[arr->data[i].type] = true;
}
pkl__emit_op(buf, PKL_ARRAY2D);
pkl__emit_int(buf, arr->header.n_cols);
pkl__emit_int(buf, arr->header.n_rows);
PickleObject__write_bytes(buf, arr->data, arr->header.numel * sizeof(py_TValue));
pkl__emit_int(buf, arr->n_cols);
pkl__emit_int(buf, arr->n_rows);
PickleObject__write_bytes(buf, arr->data, arr->numel * sizeof(py_TValue));
}
pkl__store_memo(buf, obj->_obj);
return true;
@ -652,9 +651,9 @@ bool py_pickle_loads_body(const unsigned char* p, int memo_length, c11_smallmap_
int n_cols = pkl__read_int(&p);
int n_rows = pkl__read_int(&p);
c11_array2d* arr = py_newarray2d(py_pushtmp(), n_cols, n_rows);
int total_size = arr->header.numel * sizeof(py_TValue);
int total_size = arr->numel * sizeof(py_TValue);
memcpy(arr->data, p, total_size);
for(int i = 0; i < arr->header.numel; i++) {
for(int i = 0; i < arr->numel; i++) {
arr->data[i].type = pkl__fix_type(arr->data[i].type, type_mapping);
}
p += total_size;

View File

@ -61,11 +61,6 @@ bool py_checktype(py_Ref self, py_Type type) {
return TypeError("expected '%t', got '%t'", type, self->type);
}
bool py_checkinstance(py_Ref self, py_Type type) {
if(py_isinstance(self, type)) return true;
return TypeError("expected '%t' or its subclass, got '%t'", type, self->type);
}
bool py_isinstance(py_Ref obj, py_Type type) { return py_issubclass(obj->type, type); }
bool py_issubclass(py_Type derived, py_Type base) {

View File

@ -133,8 +133,6 @@ bool py_call(py_Ref f, int argc, py_Ref argv) {
#ifndef NDEBUG
bool py_callcfunc(py_CFunction f, int argc, py_Ref argv) {
py_StackRef p0 = py_peek(0);
// NOTE: sometimes users are using `py_retval()` to pass `argv`
// It will be reset to `nil` and cause an exception
py_newnil(py_retval());
bool ok = f(argc, argv);
if(!ok) {

View File

@ -191,6 +191,11 @@ static bool builtins_exit(int argc, py_Ref argv) {
}
exit(code);
return false;
// py_TValue sso_code;
// py_newint(&sso_code, code);
// bool ok = py_tpcall(tp_SystemExit, 1, &sso_code);
// if(!ok) return false;
// return py_raise(py_retval());
}
static bool builtins_input(int argc, py_Ref argv) {
@ -324,8 +329,9 @@ static bool builtins_round(int argc, py_Ref argv) {
static bool builtins_print(int argc, py_Ref argv) {
// print(*args, sep=' ', end='\n')
py_TValue* args = py_tuple_data(argv);
int length = py_tuple_len(argv);
py_TValue* args;
int length = pk_arrayview(argv, &args);
assert(length != -1);
c11_sv sep = py_tosv(py_arg(1));
c11_sv end = py_tosv(py_arg(2));
c11_sbuf buf;

View File

@ -1,13 +1,10 @@
from array2d import array2d
from linalg import vec2i
def exit_on_error():
raise KeyboardInterrupt
# test error args for __init__
try:
a = array2d(0, 0)
exit_on_error()
exit(0)
except ValueError:
pass
@ -16,7 +13,6 @@ a = array2d[int](2, 4, lambda pos: (pos.x, pos.y))
assert a.width == a.n_cols == 2
assert a.height == a.n_rows == 4
assert a.shape == vec2i(2, 4)
assert a.numel == 8
assert a.tolist() == [
[(0, 0), (1, 0)],
@ -43,7 +39,7 @@ assert a[0, 0] == (0, 0)
assert a[1, 3] == (1, 3)
try:
a[2, 0]
exit_on_error()
exit(1)
except IndexError:
pass
@ -55,7 +51,7 @@ a[1, 3] = 6
assert a[1, 3] == 6
try:
a[0, -1] = 7
exit_on_error()
exit(1)
except IndexError:
pass
@ -87,19 +83,21 @@ d = c.copy()
assert (d == c).all() and d is not c
# test fill_
d[:, :] = -3 # d.fill_(-3)
d.fill_(-3)
assert (d == array2d(2, 4, default=-3)).all()
# test apply
d.apply(lambda x: x + 3)
# test apply_
d.apply_(lambda x: x + 3)
assert (d == array2d(2, 4, default=0)).all()
# test copy_
a[:, :] = d
a.copy_(d)
assert (a == d).all() and a is not d
x = array2d(2, 4, default=0)
x[:, :] = d
x.copy_(d)
assert (x == d).all() and x is not d
x.copy_([1, 2, 3, 4, 5, 6, 7, 8])
assert x.tolist() == [[1, 2], [3, 4], [5, 6], [7, 8]]
# test alive_neighbors
a = array2d[int](3, 3, default=0)
@ -150,7 +148,7 @@ assert a.get_bounding_rect(0) == (0, 0, 5, 5)
try:
a.get_bounding_rect(2)
exit_on_error()
exit(1)
except ValueError:
pass
@ -167,10 +165,16 @@ assert a == array2d(3, 2, default=3)
try:
a[:, :] = array2d(1, 1)
exit_on_error()
exit(1)
except ValueError:
pass
try:
a[:, :] = ...
exit(1)
except TypeError:
pass
# test __iter__
a = array2d(3, 4, default=1)
for xy, val in a:

View File

@ -1,13 +0,0 @@
from array2d import chunked_array2d
from linalg import vec2i
a = chunked_array2d(4, default=0)
print(iter(a))
print(list(a))
a[vec2i.ONE] = 1
print(a.view().render())
print(list(a))