From 3d6325473ec39f132a3fb4785218c13ed4591194 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Thu, 13 Feb 2025 14:56:10 +0800 Subject: [PATCH] backup --- .github/workflows/main.yml | 207 +++++++++++++++ .github/workflows/pybind11.yml | 76 ++++++ .github/workflows/website.yml | 42 +++ .github/workflows/workflows.zip | Bin 2732 -> 0 bytes include/pocketpy/interpreter/array2d.h | 16 +- include/pocketpy/pocketpy.h | 6 +- include/typings/array2d.pyi | 2 +- src/modules/array2d.c | 340 ++++++++++++------------- src/modules/pickle.c | 13 +- src/public/cast.c | 5 + 10 files changed, 502 insertions(+), 205 deletions(-) create mode 100644 .github/workflows/main.yml create mode 100644 .github/workflows/pybind11.yml create mode 100644 .github/workflows/website.yml delete mode 100644 .github/workflows/workflows.zip diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..c6f931bc --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,207 @@ +name: build + +on: + push: + paths-ignore: + - 'docs/**' + - 'web/**' + - '**.md' + pull_request: + paths-ignore: + - 'docs/**' + - 'web/**' + - '**.md' +jobs: + build_win32_amalgamated: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: ilammy/msvc-dev-cmd@v1 + - name: Compile + shell: powershell + run: | + python amalgamate.py + cd amalgamated + cl.exe /std:c11 /utf-8 /Ox /I. pocketpy.c main.c /link /out:pkpy.exe + build_win32: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: ilammy/msvc-dev-cmd@v1 + - name: Compile + shell: bash + run: | + mkdir -p output/x86_64 + python cmake_build.py + cp main.exe output/x86_64 + cp pocketpy.dll output/x86_64 + - uses: actions/upload-artifact@v4 + with: + name: windows + path: output + - name: Unit Test + run: python scripts/run_tests.py + - name: Benchmark + run: python scripts/run_tests.py benchmark + build_linux: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Setup Clang + uses: egor-tensin/setup-clang@v1 + with: + version: 15 + platform: x64 + - name: Install dependencies + run: sudo apt-get install -y libclang-rt-15-dev + - name: Unit Test with Coverage + run: bash run_tests.sh + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + directory: .coverage + if: github.ref == 'refs/heads/main' + - name: Compile and Test + run: | + mkdir -p output/x86_64 + python cmake_build.py + python scripts/run_tests.py + cp main output/x86_64 + cp libpocketpy.so output/x86_64 + env: + CC: clang + - uses: actions/upload-artifact@v4 + with: + name: linux + path: output + - name: Benchmark + run: python scripts/run_tests.py benchmark + build_linux_x86: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Setup Alpine Linux for aarch64 + uses: jirutka/setup-alpine@v1 + with: + arch: x86 + packages: gcc g++ make cmake libc-dev linux-headers python3 + - name: Build and Test + run: | + uname -m + python cmake_build.py + python scripts/run_tests.py + python scripts/run_tests.py benchmark + shell: alpine.sh --root {0} + build_darwin: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Compile and Test + run: | + python cmake_build.py + python scripts/run_tests.py + - name: Benchmark + run: python scripts/run_tests.py benchmark + - name: Test Amalgamated Build + run: python amalgamate.py + build_android: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: r23 + local-cache: false + add-to-path: false + - name: Compile Shared Library + run: | + bash build_android.sh arm64-v8a + bash build_android.sh armeabi-v7a + bash build_android.sh x86_64 + + mkdir -p output/arm64-v8a + mkdir -p output/armeabi-v7a + mkdir -p output/x86_64 + + cp build/android/arm64-v8a/libpocketpy.so output/arm64-v8a + cp build/android/armeabi-v7a/libpocketpy.so output/armeabi-v7a + cp build/android/x86_64/libpocketpy.so output/x86_64 + env: + ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} + - uses: actions/upload-artifact@v4 + with: + name: android + path: output + build_ios: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Compile Frameworks + run: | + git clone https://github.com/leetal/ios-cmake --depth 1 ~/ios-cmake + bash build_ios.sh + mkdir -p output + cp -r build/pocketpy.xcframework output/pocketpy.xcframework + - uses: actions/upload-artifact@v4 + with: + name: ios + path: output + + merge: + runs-on: ubuntu-latest + needs: [ build_win32, build_linux, build_darwin, build_android, build_ios ] + steps: + - name: "Create output directory" + run: "mkdir $GITHUB_WORKSPACE/output" + + - name: "Merge win32" + uses: actions/download-artifact@v4.1.7 + with: + name: windows + path: $GITHUB_WORKSPACE/output/windows + + - name: "Merge linux" + uses: actions/download-artifact@v4.1.7 + with: + name: linux + path: $GITHUB_WORKSPACE/output/linux + + # - name: "Merge darwin" + # uses: actions/download-artifact@v4.1.7 + # with: + # name: macos + # path: $GITHUB_WORKSPACE/output/macos + + - name: "Merge android" + uses: actions/download-artifact@v4.1.7 + with: + name: android + path: $GITHUB_WORKSPACE/output/android + + - name: "Merge ios" + uses: actions/download-artifact@v4.1.7 + with: + name: ios + path: $GITHUB_WORKSPACE/output/ios + + - name: "Upload merged artifact" + uses: actions/upload-artifact@v4.3.3 + with: + name: all-in-one + path: $GITHUB_WORKSPACE/output diff --git a/.github/workflows/pybind11.yml b/.github/workflows/pybind11.yml new file mode 100644 index 00000000..7578b030 --- /dev/null +++ b/.github/workflows/pybind11.yml @@ -0,0 +1,76 @@ +name: PKBIND Build and Test + +on: + push: + paths-ignore: + - "docs/**" + - "web/**" + - "**.md" + pull_request: + paths-ignore: + - "docs/**" + - "web/**" + - "**.md" + +jobs: + build_linux: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up GCC + run: | + sudo apt-get update + sudo apt-get install -y gcc g++ + + - name: Set up CMake + uses: jwlawson/actions-setup-cmake@v1.10 + + - name: Test + run: | + cd include/pybind11/tests + cmake -B build + cmake --build build --config Release --parallel + ./build/PKBIND_TEST + + build_win: + runs-on: windows-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up MSVC + uses: ilammy/msvc-dev-cmd@v1 + + - name: Set up CMake + uses: jwlawson/actions-setup-cmake@v1.10 + + - name: Test + run: | + cd include\pybind11\tests + cmake -B build + cmake --build build --config Release --parallel + build\Release\PKBIND_TEST.exe + + build_mac: + runs-on: macos-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Clang + run: | + brew install llvm + echo 'export PATH="/usr/local/opt/llvm/bin:$PATH"' >> ~/.zshrc + source ~/.zshrc + + - name: Set up CMake + uses: jwlawson/actions-setup-cmake@v1.10 + + - name: Test + run: | + cd include/pybind11/tests + cmake -B build -DENABLE_TEST=ON + cmake --build build --config Release --parallel + ./build/PKBIND_TEST diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml new file mode 100644 index 00000000..a2add796 --- /dev/null +++ b/.github/workflows/website.yml @@ -0,0 +1,42 @@ +name: website + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: write + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + ################################################### + - uses: actions/setup-node@v3.1.1 + - name: Retype build + run: | + python scripts/gen_docs.py + cd docs + npm install retypeapp -g + retype build + ################################################### + - name: Setup emsdk + uses: mymindstorm/setup-emsdk@v12 + with: + version: latest + actions-cache-folder: 'emsdk-cache' + - name: Compile + run: | + bash build_web.sh + mv web docs/.retype/static + ################################################### + - uses: crazy-max/ghaction-github-pages@v3 + with: + target_branch: gh-pages + build_dir: docs/.retype + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + if: github.ref == 'refs/heads/main' diff --git a/.github/workflows/workflows.zip b/.github/workflows/workflows.zip deleted file mode 100644 index 2b6554362dced3e1fd79f397fb15d1ead29d83d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2732 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 B*?s^3 diff --git a/include/pocketpy/interpreter/array2d.h b/include/pocketpy/interpreter/array2d.h index 5a6bc9f9..fe19ca05 100644 --- a/include/pocketpy/interpreter/array2d.h +++ b/include/pocketpy/interpreter/array2d.h @@ -1,10 +1,8 @@ #pragma once #include "pocketpy/pocketpy.h" - -#include "pocketpy/common/utils.h" -#include "pocketpy/common/sstream.h" -#include "pocketpy/interpreter/vm.h" +#include "pocketpy/common/smallmap.h" +#include "pocketpy/objects/base.h" typedef struct c11_array2d_like { int n_cols; @@ -53,13 +51,3 @@ typedef struct c11_chunked_array2d { py_TValue default_T; py_TValue context_builder; } c11_chunked_array2d; - -void c11_chunked_array2d__dtor(c11_chunked_array2d* self); - -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__del(c11_chunked_array2d* self, int col, int row); - -void pk__register_chunked_array2d(py_Ref mod); - -/* array2d_view */ \ No newline at end of file diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index 05d51733..d8a70302 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -302,10 +302,14 @@ 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. +/// Check if the object is an instance of the given type exactly. /// 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) diff --git a/include/typings/array2d.pyi b/include/typings/array2d.pyi index 5b7117ec..48d2cb62 100644 --- a/include/typings/array2d.pyi +++ b/include/typings/array2d.pyi @@ -36,6 +36,7 @@ class array2d_like[T]: 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 @@ -98,7 +99,6 @@ class array2d[T](array2d_like[T]): @staticmethod def fromlist(data: list[list[T]]) -> array2d[T]: ... - def tolist(self) -> list[list[T]]: ... class chunked_array2d[T, TContext]: diff --git a/src/modules/array2d.c b/src/modules/array2d.c index b590a988..3d4e2810 100644 --- a/src/modules/array2d.c +++ b/src/modules/array2d.c @@ -1,4 +1,5 @@ #include "pocketpy/interpreter/array2d.h" +#include "pocketpy/interpreter/vm.h" static bool c11_array2d_like_is_valid(c11_array2d_like* self, unsigned int col, unsigned int row) { return col < self->n_cols && row < self->n_rows; @@ -199,6 +200,21 @@ static bool array2d_like_copy(int argc, py_Ref argv) { return true; } +static bool array2d_like_tolist(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + c11_array2d_like* self = py_touserdata(argv); + py_newlistn(py_retval(), self->n_rows); + for(int j = 0; j < self->n_rows; j++) { + py_Ref row_j = py_list_getitem(py_retval(), j); + py_newlistn(row_j, self->n_cols); + for(int i = 0; i < self->n_cols; i++) { + py_Ref item = self->f_get(self, i, j); + py_list_setitem(row_j, i, item); + } + } + return true; +} + static bool _check_same_shape(int colA, int rowA, int colB, int rowB) { if(colA != colB || rowA != rowB) { const char* fmt = "expected the same shape: (%d, %d) != (%d, %d)"; @@ -533,16 +549,16 @@ static bool array2d_like_count_neighbors(int argc, py_Ref argv) { int n_offsets; if(strcmp(neighborhood, "Moore") == 0) { offsets = Moore; - n_offsets = 8; + n_offsets = c11__count_array(Moore); } else if(strcmp(neighborhood, "von Neumann") == 0) { offsets = von_Neumann; - n_offsets = 4; + n_offsets = c11__count_array(von_Neumann); } else { return ValueError("neighborhood must be 'Moore' or 'von Neumann'"); } for(int j = 0; j < self->n_rows; j++) { for(int i = 0; i < self->n_cols; i++) { - int count = 0; + py_i64 count = 0; for(int k = 0; k < n_offsets; k++) { int x = i + offsets[k].x; int y = j + offsets[k].y; @@ -561,7 +577,51 @@ static bool array2d_like_count_neighbors(int argc, py_Ref argv) { return true; } -static void pk__register_array2d_like(py_Ref mod) { +// convolve(self: array2d_like[int], kernel: array2d_like[int], padding: int) -> array2d[int] +static bool array2d_like_convolve(int argc, py_Ref argv) { + PY_CHECK_ARGC(3); + if(!py_checkinstance(&argv[1], tp_array2d_like)) return false; + PY_CHECK_ARG_TYPE(2, tp_int); + c11_array2d_like* self = py_touserdata(&argv[0]); + c11_array2d_like* kernel = py_touserdata(&argv[1]); + int padding = py_toint(py_arg(2)); + if(kernel->n_cols != kernel->n_rows) return ValueError("kernel must be square"); + int ksize = kernel->n_cols; + if(ksize % 2 == 0) return ValueError("kernel size must be odd"); + int ksize_half = ksize / 2; + c11_array2d* res = py_newarray2d(py_pushtmp(), self->n_cols, self->n_rows); + for(int j = 0; j < self->n_rows; j++) { + for(int i = 0; i < self->n_cols; i++) { + py_i64 sum = 0; + for(int jj = 0; jj < ksize; jj++) { + for(int ii = 0; ii < ksize; ii++) { + int x = i + ii - ksize_half; + int y = j + jj - ksize_half; + py_i64 _0, _1; + if(x < 0 || x >= self->n_cols || y < 0 || y >= self->n_rows) { + _0 = padding; + } else { + py_Ref item = self->f_get(self, x, y); + if(!py_checkint(item)) return false; + _0 = py_toint(item); + } + py_Ref kitem = kernel->f_get(kernel, ii, jj); + if(!py_checkint(kitem)) return false; + _1 = py_toint(kitem); + sum += _0 * _1; + } + } + py_newint(c11_array2d__get(res, i, j), sum); + } + } + py_assign(py_retval(), py_peek(-1)); + py_pop(); + return true; +} + +#undef HANDLE_SLICE + +static py_Type register_array2d_like(py_Ref mod) { py_Type type = py_newtype("array2d_like", tp_object, mod, NULL); py_bindproperty(type, "n_cols", array2d_like_n_cols, NULL); @@ -582,6 +642,7 @@ static void pk__register_array2d_like(py_Ref mod) { py_bindmethod(type, "map", array2d_like_map); py_bindmethod(type, "apply", array2d_like_apply); py_bindmethod(type, "copy", array2d_like_copy); + py_bindmethod(type, "tolist", array2d_like_tolist); py_bindmagic(type, __eq__, array2d_like__eq__); py_bindmagic(type, __ne__, array2d_like__ne__); @@ -594,6 +655,42 @@ static void pk__register_array2d_like(py_Ref mod) { py_bindmethod(type, "count", array2d_like_count); py_bindmethod(type, "get_bounding_rect", array2d_like_get_bounding_rect); py_bindmethod(type, "count_neighbors", array2d_like_count_neighbors); + py_bindmethod(type, "convolve", array2d_like_convolve); + + const char* scc = + "\ndef get_connected_components(self, value: T, neighborhood: Neighborhood) -> tuple[array2d[int], int]:\n from collections import deque\n from linalg import vec2i\n\n DIRS = [vec2i.LEFT, vec2i.RIGHT, vec2i.UP, vec2i.DOWN]\n assert neighborhood in ['Moore', 'von Neumann']\n\n if neighborhood == 'Moore':\n DIRS.extend([\n vec2i.LEFT+vec2i.UP,\n vec2i.RIGHT+vec2i.UP,\n vec2i.LEFT+vec2i.DOWN,\n vec2i.RIGHT+vec2i.DOWN\n ])\n\n visited = array2d[int](self.width, self.height, default=0)\n queue = deque()\n count = 0\n for y in range(self.height):\n for x in range(self.width):\n if visited[x, y] or self[x, y] != value:\n continue\n count += 1\n queue.append((x, y))\n visited[x, y] = count\n while queue:\n cx, cy = queue.popleft()\n for dx, dy in DIRS:\n nx, ny = cx+dx, cy+dy\n if self.is_valid(nx, ny) and not visited[nx, ny] and self[nx, ny] == value:\n queue.append((nx, ny))\n visited[nx, ny] = count\n return visited, count\n\narray2d_like.get_connected_components = get_connected_components\ndel get_connected_components\n"; + if(!py_exec(scc, "array2d.py", EXEC_MODE, mod)) { + py_printexc(); + c11__abort("failed to execute array2d.py"); + } + + return type; +} + +static bool array2d_like_iterator__next__(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + c11_array2d_like_iterator* self = py_touserdata(argv); + if(self->j >= self->array->n_rows) return StopIteration(); + py_newtuple(py_retval(), 2); + py_TValue* data = py_tuple_data(py_retval()); + py_newvec2i(&data[0], + (c11_vec2i){ + {self->i, self->j} + }); + py_assign(&data[1], self->array->f_get(self->array, self->i, self->j)); + self->i++; + if(self->i >= self->array->n_cols) { + self->i = 0; + self->j++; + } + return true; +} + +static py_Type register_array2d_like_iterator(py_Ref mod) { + py_Type type = py_newtype("array2d_like_iterator", tp_object, mod, NULL); + py_bindmagic(type, __iter__, pk_wrapper__self); + py_bindmagic(type, __next__, array2d_like_iterator__next__); + return type; } static bool array2d__new__(int argc, py_Ref argv) { @@ -629,36 +726,6 @@ static bool array2d__new__(int argc, py_Ref argv) { return true; } -static bool _array2d_check_all_type(c11_array2d* self, py_Type type) { - for(int i = 0; i < self->numel; i++) { - py_Type item_type = self->data[i].type; - if(item_type != type) { - const char* fmt = "expected array2d[%t], got %t"; - return TypeError(fmt, type, item_type); - } - } - return true; -} - -static bool array2d_like_iterator__next__(int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - c11_array2d_like_iterator* self = py_touserdata(argv); - if(self->j >= self->array->n_rows) return StopIteration(); - py_newtuple(py_retval(), 2); - py_TValue* data = py_tuple_data(py_retval()); - py_newvec2i(&data[0], - (c11_vec2i){ - {self->i, self->j} - }); - py_assign(&data[1], self->array->f_get(self->array, self->i, self->j)); - self->i++; - if(self->i >= self->array->n_cols) { - self->i = 0; - self->j++; - } - return true; -} - // fromlist(data: list[list[T]]) -> array2d[T] static bool array2d_fromlist_STATIC(int argc, py_Ref argv) { PY_CHECK_ARGC(1); @@ -687,131 +754,27 @@ static bool array2d_fromlist_STATIC(int argc, py_Ref argv) { return true; } -// tolist(self) -> list[list[T]] -static bool array2d_tolist(int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - c11_array2d* self = py_touserdata(argv); - py_newlistn(py_retval(), self->n_rows); - for(int j = 0; j < self->n_rows; j++) { - py_Ref row_j = py_list_getitem(py_retval(), j); - py_newlistn(row_j, self->n_cols); - for(int i = 0; i < self->n_cols; i++) { - py_list_setitem(row_j, i, c11_array2d__get(self, i, j)); - } - } - return true; -} - -// convolve(self: array2d[int], kernel: array2d[int], padding: int) -> array2d[int] -static bool array2d_convolve(int argc, py_Ref argv) { - PY_CHECK_ARGC(3); - PY_CHECK_ARG_TYPE(1, tp_array2d); - PY_CHECK_ARG_TYPE(2, tp_int); - c11_array2d_like* self = py_touserdata(argv); - c11_array2d_like* kernel = py_touserdata(py_arg(1)); - int padding = py_toint(py_arg(2)); - if(kernel->n_cols != kernel->n_rows) { return ValueError("kernel must be square"); } - int ksize = kernel->n_cols; - if(ksize % 2 == 0) return ValueError("kernel size must be odd"); - int ksize_half = ksize / 2; - if(!_array2d_check_all_type(self, tp_int)) return false; - if(!_array2d_check_all_type(kernel, tp_int)) return false; - c11_array2d* res = py_newarray2d(py_pushtmp(), self->n_cols, self->n_rows); - for(int j = 0; j < self->n_rows; j++) { - for(int i = 0; i < self->n_cols; i++) { - py_i64 sum = 0; - for(int jj = 0; jj < ksize; jj++) { - for(int ii = 0; ii < ksize; ii++) { - int x = i + ii - ksize_half; - int y = j + jj - ksize_half; - py_i64 _0, _1; - if(x < 0 || x >= self->n_cols || y < 0 || y >= self->n_rows) { - _0 = padding; - } else { - _0 = py_toint(c11_array2d__get(self, x, y)); - } - _1 = py_toint(c11_array2d__get(kernel, ii, jj)); - sum += _0 * _1; - } - } - py_newint(c11_array2d__get(res, i, j), sum); - } - } - py_assign(py_retval(), py_peek(-1)); - py_pop(); - return true; -} - -void pk__add_module_array2d() { - py_GlobalRef mod = py_newmodule("array2d"); - - py_Type array2d_like = pk_newtype("array2d_like", tp_object, mod, NULL, false, true); - py_Type array2d_like_iterator = - pk_newtype("array2d_like_iterator", tp_object, mod, NULL, false, true); - assert(array2d_like == tp_array2d_like); - assert(array2d_like_iterator == tp_array2d_like_iterator); - py_setdict(mod, py_name("array2d_like"), py_tpobject(array2d_like)); - py_setdict(mod, py_name("array2d_like_iterator"), py_tpobject(array2d_like_iterator)); - - py_Type array2d = py_newtype("array2d", tp_array2d_like, mod, NULL); - py_Type chunked_array2d = py_newtype("chunked_array2d", tp_array2d_like, mod, NULL); - - py_setdict(py_tpobject(array2d_like), __hash__, py_None()); - py_setdict(py_tpobject(array2d_like_iterator), __hash__, py_None()); - py_setdict(py_tpobject(array2d), __hash__, py_None()); - py_setdict(py_tpobject(chunked_array2d), __hash__, py_None()); - - py_bindmagic(array2d_like_iterator, __iter__, pk_wrapper__self); - py_bindmagic(array2d_like_iterator, __next__, array2d_like_iterator__next__); - - py_bind(py_tpobject(array2d), +static py_Type register_array2d(py_Ref mod){ + py_Type type = py_newtype("array2d", tp_array2d_like, mod, NULL); + py_bind(py_tpobject(type), "__new__(cls, n_cols: int, n_rows: int, default=None)", array2d__new__); - - py_bindmagic(array2d, __eq__, array2d__eq__); - py_bindmagic(array2d, __ne__, array2d__ne__); - py_bindmagic(array2d, __repr__, array2d__repr__); - py_bindmagic(array2d, __iter__, array2d__iter__); - - py_bindmagic(array2d, __getitem__, array2d__getitem__); - py_bindmagic(array2d, __setitem__, array2d__setitem__); - - py_bindmethod(array2d, "is_valid", array2d_is_valid); - py_bindmethod(array2d, "get", array2d_get); - - py_bindmethod(array2d, "map", array2d_map); - py_bindmethod(array2d, "copy", array2d_copy); - - py_bindmethod(array2d, "fill_", array2d_fill_); - py_bindmethod(array2d, "apply_", array2d_apply_); - py_bindmethod(array2d, "copy_", array2d_copy_); - - py_bindmethod(array2d, "render", array2d_render); - - py_bindmethod(array2d, "all", array2d_all); - py_bindmethod(array2d, "any", array2d_any); - - py_bindstaticmethod(array2d, "fromlist", array2d_fromlist_STATIC); - py_bindmethod(array2d, "tolist", array2d_tolist); - - py_bindmethod(array2d, "count", array2d_count); - py_bindmethod(array2d, "get_bounding_rect", array2d_get_bounding_rect); - py_bindmethod(array2d, "count_neighbors", array2d_count_neighbors); - py_bindmethod(array2d, "convolve", array2d_convolve); - - const char* scc = - "\ndef get_connected_components(self, value: T, neighborhood: Neighborhood) -> tuple[array2d[int], int]:\n from collections import deque\n from linalg import vec2i\n\n DIRS = [vec2i.LEFT, vec2i.RIGHT, vec2i.UP, vec2i.DOWN]\n assert neighborhood in ['Moore', 'von Neumann']\n\n if neighborhood == 'Moore':\n DIRS.extend([\n vec2i.LEFT+vec2i.UP,\n vec2i.RIGHT+vec2i.UP,\n vec2i.LEFT+vec2i.DOWN,\n vec2i.RIGHT+vec2i.DOWN\n ])\n\n visited = array2d[int](self.width, self.height, default=0)\n queue = deque()\n count = 0\n for y in range(self.height):\n for x in range(self.width):\n if visited[x, y] or self[x, y] != value:\n continue\n count += 1\n queue.append((x, y))\n visited[x, y] = count\n while queue:\n cx, cy = queue.popleft()\n for dx, dy in DIRS:\n nx, ny = cx+dx, cy+dy\n if self.is_valid(nx, ny) and not visited[nx, ny] and self[nx, ny] == value:\n queue.append((nx, ny))\n visited[nx, ny] = count\n return visited, count\n\narray2d.get_connected_components = get_connected_components\ndel get_connected_components\n"; - - if(!py_exec(scc, "array2d.py", EXEC_MODE, mod)) { - py_printexc(); - c11__abort("failed to execute array2d.py"); - } - - pk__register_chunked_array2d(mod); + py_bindstaticmethod(type, "fromlist", array2d_fromlist_STATIC); + return type; } -#undef INC_COUNT -#undef HANDLE_SLICE +static bool array2d_view_origin(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + c11_array2d_view* self = py_touserdata(argv); + py_newvec2i(py_retval(), self->origin); + return true; +} + +static py_Type register_array2d_view(py_Ref mod) { + py_Type type = py_newtype("array2d_view", tp_array2d_like, mod, NULL); + py_bindproperty(type, "origin", array2d_view_origin, NULL); + return type; +} /* chunked_array2d */ #define SMALLMAP_T__SOURCE @@ -845,7 +808,7 @@ static py_TValue* c11_chunked_array2d__new_chunk(c11_chunked_array2d* self, c11_ return data; } -void c11_chunked_array2d__world_to_chunk(c11_chunked_array2d* self, +static void c11_chunked_array2d__world_to_chunk(c11_chunked_array2d* self, int col, int row, c11_vec2i* chunk_pos, @@ -885,6 +848,32 @@ static py_TValue* c11_chunked_array2d__parse_col_row(c11_chunked_array2d* self, return data + 1; // skip context } +static 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 NULL; + py_Ref retval = &data[local_pos.y * self->chunk_size + local_pos.x]; + if(py_isnil(retval)) return NULL; + return retval; +} + +static 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; + } + data[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[local_pos.y * self->chunk_size + local_pos.x] = *py_NIL(); +} + static bool chunked_array2d__new__(int argc, py_Ref argv) { py_Type cls = py_totype(argv); py_newobject(py_retval(), cls, 0, sizeof(c11_chunked_array2d)); @@ -1035,32 +1024,6 @@ void c11_chunked_array2d__dtor(c11_chunked_array2d* self) { c11_chunked_array2d_chunks__dtor(&self->chunks); } -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 NULL; - py_Ref retval = &data[local_pos.y * self->chunk_size + local_pos.x]; - if(py_isnil(retval)) return NULL; - return retval; -} - -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; - } - 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); @@ -1074,7 +1037,7 @@ static void c11_chunked_array2d__mark(void* ud) { } } -void pk__register_chunked_array2d(py_Ref mod) { +static void 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); @@ -1095,4 +1058,15 @@ void pk__register_chunked_array2d(py_Ref mod) { 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); +} + + +void pk__add_module_array2d() { + py_GlobalRef mod = py_newmodule("array2d"); + + register_array2d_like(mod); + register_array2d_like_iterator(mod); + register_array2d(mod); + register_array2d_view(mod); + register_chunked_array2d(mod); } \ No newline at end of file diff --git a/src/modules/pickle.c b/src/modules/pickle.c index c19fabfa..b5afae1e 100644 --- a/src/modules/pickle.c +++ b/src/modules/pickle.c @@ -4,6 +4,7 @@ #include "pocketpy/common/utils.h" #include "pocketpy/common/sstream.h" +#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/array2d.h" #include @@ -324,16 +325,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->numel; i++) { + for(int i = 0; i < arr->header.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->n_cols); - pkl__emit_int(buf, arr->n_rows); - PickleObject__write_bytes(buf, arr->data, arr->numel * sizeof(py_TValue)); + 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__store_memo(buf, obj->_obj); return true; @@ -651,9 +652,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->numel * sizeof(py_TValue); + int total_size = arr->header.numel * sizeof(py_TValue); memcpy(arr->data, p, total_size); - for(int i = 0; i < arr->numel; i++) { + for(int i = 0; i < arr->header.numel; i++) { arr->data[i].type = pkl__fix_type(arr->data[i].type, type_mapping); } p += total_size; diff --git a/src/public/cast.c b/src/public/cast.c index 6cda6c66..ea5169ca 100644 --- a/src/public/cast.c +++ b/src/public/cast.c @@ -61,6 +61,11 @@ 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) {