From 5a42d35f9db9ad2d0ead91afa1dd25c19d73d472 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Wed, 27 Sep 2023 05:31:01 +0800 Subject: [PATCH] ... --- include/pocketpy/cffi.h | 20 ------ include/pocketpy/linalg.h | 27 ++++--- include/typings/c.pyi | 54 +++++++------- python/c.py | 27 +++++++ python/collections.py | 4 +- src/cffi.cpp | 145 ++++++++++++++------------------------ src/pocketpy.cpp | 12 ++++ tests/80_c.py | 11 +-- tests/80_linalg.py | 5 +- tests/99_cffi_2.py | 36 +++++----- 10 files changed, 156 insertions(+), 185 deletions(-) create mode 100644 python/c.py diff --git a/include/pocketpy/cffi.h b/include/pocketpy/cffi.h index 64496546..dd5c098e 100644 --- a/include/pocketpy/cffi.h +++ b/include/pocketpy/cffi.h @@ -29,8 +29,6 @@ namespace pkpy { #define VAR_T(T, ...) vm->heap.gcnew(T::_type(vm), __VA_ARGS__) -int c99_sizeof(VM*, const Str&); - inline PyObject* py_var(VM* vm, void* p); inline PyObject* py_var(VM* vm, char* p); @@ -80,13 +78,6 @@ struct C99Struct{ } } - template - C99Struct(std::monostate _, const T& data): C99Struct(sizeof(T)){ - static_assert(is_pod::value); - static_assert(!std::is_pointer_v); - memcpy(p, &data, this->size); - } - C99Struct(void* p, int size): C99Struct(size){ if(p != nullptr) memcpy(this->p, p, size); } @@ -120,17 +111,6 @@ inline void add_refl_type(std::string_view name, size_t size){ _refl_types[name] = std::move(type); } -struct C99ReflType final: ReflType{ - PY_CLASS(C99ReflType, c, _refl) - - C99ReflType(const ReflType& type){ - this->name = type.name; - this->size = type.size; - } - - static void _register(VM* vm, PyObject* mod, PyObject* type); -}; - static_assert(sizeof(Py_) <= 64); static_assert(sizeof(Py_) <= 64); diff --git a/include/pocketpy/linalg.h b/include/pocketpy/linalg.h index 6c288362..57710b98 100644 --- a/include/pocketpy/linalg.h +++ b/include/pocketpy/linalg.h @@ -4,14 +4,13 @@ namespace pkpy{ -static constexpr float kEpsilon = 1e-4f; -inline static bool isclose(float a, float b){ return fabsf(a - b) < kEpsilon; } +inline bool isclose(float a, float b){ return fabsf(a - b) < 1e-4f; } struct Vec2{ float x, y; Vec2() : x(0.0f), y(0.0f) {} Vec2(float x, float y) : x(x), y(y) {} - Vec2(const Vec2& v) : x(v.x), y(v.y) {} + Vec2(const Vec2& v) = default; Vec2 operator+(const Vec2& v) const { return Vec2(x + v.x, y + v.y); } Vec2& operator+=(const Vec2& v) { x += v.x; y += v.y; return *this; } @@ -35,7 +34,7 @@ struct Vec3{ float x, y, z; Vec3() : x(0.0f), y(0.0f), z(0.0f) {} Vec3(float x, float y, float z) : x(x), y(y), z(z) {} - Vec3(const Vec3& v) : x(v.x), y(v.y), z(v.z) {} + Vec3(const Vec3& v) = default; Vec3 operator+(const Vec3& v) const { return Vec3(x + v.x, y + v.y, z + v.z); } Vec3& operator+=(const Vec3& v) { x += v.x; y += v.y; z += v.z; return *this; } @@ -59,7 +58,7 @@ struct Vec4{ float x, y, z, w; Vec4() : x(0.0f), y(0.0f), z(0.0f), w(0.0f) {} Vec4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {} - Vec4(const Vec4& v) : x(v.x), y(v.y), z(v.z), w(v.w) {} + Vec4(const Vec4& v) = default; Vec4 operator+(const Vec4& v) const { return Vec4(x + v.x, y + v.y, z + v.z, w + v.w); } Vec4& operator+=(const Vec4& v) { x += v.x; y += v.y; z += v.z; w += v.w; return *this; } @@ -96,6 +95,8 @@ struct Mat3x3{ : _11(_11), _12(_12), _13(_13) , _21(_21), _22(_22), _23(_23) , _31(_31), _32(_32), _33(_33) {} + + Mat3x3(const Mat3x3& other) = default; void set_zeros(){ for (int i=0; i<9; ++i) v[i] = 0.0f; } void set_ones(){ for (int i=0; i<9; ++i) v[i] = 1.0f; } @@ -208,7 +209,7 @@ struct Mat3x3{ bool inverse(Mat3x3& ret) const{ float det = determinant(); - if (fabsf(det) < kEpsilon) return false; + if (isclose(det, 0)) return false; float inv_det = 1.0f / det; ret._11 = (_22 * _33 - _23 * _32) * inv_det; ret._12 = (_13 * _32 - _12 * _33) * inv_det; @@ -233,7 +234,7 @@ struct Mat3x3{ bool is_affine() const{ float det = _11 * _22 - _12 * _21; - if(fabsf(det) < kEpsilon) return false; + if(!isclose(det, 0)) return false; return _31 == 0.0f && _32 == 0.0f && _33 == 1.0f; } @@ -274,7 +275,7 @@ struct PyVec2: Vec2 { PyVec2() : Vec2() {} PyVec2(const Vec2& v) : Vec2(v) {} - PyVec2(const PyVec2& v) : Vec2(v) {} + PyVec2(const PyVec2& v) = default; static void _register(VM* vm, PyObject* mod, PyObject* type); }; @@ -284,7 +285,7 @@ struct PyVec3: Vec3 { PyVec3() : Vec3() {} PyVec3(const Vec3& v) : Vec3(v) {} - PyVec3(const PyVec3& v) : Vec3(v) {} + PyVec3(const PyVec3& v) = default; static void _register(VM* vm, PyObject* mod, PyObject* type); }; @@ -294,7 +295,7 @@ struct PyVec4: Vec4{ PyVec4(): Vec4(){} PyVec4(const Vec4& v): Vec4(v){} - PyVec4(const PyVec4& v): Vec4(v){} + PyVec4(const PyVec4& v) = default; static void _register(VM* vm, PyObject* mod, PyObject* type); }; @@ -304,7 +305,7 @@ struct PyMat3x3: Mat3x3{ PyMat3x3(): Mat3x3(){} PyMat3x3(const Mat3x3& other): Mat3x3(other){} - PyMat3x3(const PyMat3x3& other): Mat3x3(other){} + PyMat3x3(const PyMat3x3& other) = default; static void _register(VM* vm, PyObject* mod, PyObject* type); }; @@ -340,5 +341,9 @@ inline void add_module_linalg(VM* vm){ } static_assert(sizeof(Py_) <= 64); +static_assert(std::is_trivially_copyable::value); +static_assert(std::is_trivially_copyable::value); +static_assert(std::is_trivially_copyable::value); +static_assert(std::is_trivially_copyable::value); } // namespace pkpy \ No newline at end of file diff --git a/include/typings/c.pyi b/include/typings/c.pyi index 1a8bac4d..d3365adc 100644 --- a/include/typings/c.pyi +++ b/include/typings/c.pyi @@ -3,15 +3,9 @@ from typing import overload def malloc(size: int) -> 'void_p': ... def free(ptr: 'void_p') -> None: ... def sizeof(type: str) -> int: ... -def refl(name: str) -> '_refl': ... def memset(ptr: 'void_p', value: int, size: int) -> None: ... def memcpy(dst: 'void_p', src: 'void_p', size: int) -> None: ... -class _refl: - def __call__(self) -> 'struct': ... - def name(self) -> str: ... - def size(self) -> int: ... - class void_p: def __add__(self, i: int) -> 'void_p': ... def __sub__(self, i: int) -> 'void_p': ... @@ -37,7 +31,7 @@ class void_p: def read_bool(self) -> bool: ... def read_void_p(self) -> 'void_p': ... def read_bytes(self, size: int) -> bytes: ... - def read_struct(self, type: str) -> 'struct': ... + def read_struct(self, size: int) -> 'struct': ... def write_char(self, value: int) -> None: ... def write_uchar(self, value: int) -> None: ... @@ -60,11 +54,7 @@ class struct: @overload def __init__(self, size: int): ... @overload - def __init__(self, p: 'void_p', size: int): ... - @overload - def __init__(self, s: str): ... - @overload - def __init__(self, b: bytes): ... + def __init__(self, buffer: bytes): ... def addr(self) -> 'void_p': ... def copy(self) -> 'struct': ... @@ -72,9 +62,6 @@ class struct: def __eq__(self, other: 'struct') -> bool: ... def __ne__(self, other: 'struct') -> bool: ... - def to_string(self) -> str: ... - def to_bytes(self) -> bytes: ... - def read_char(self, offset=0) -> int: ... def read_uchar(self, offset=0) -> int: ... def read_short(self, offset=0) -> int: ... @@ -105,19 +92,19 @@ class struct: def write_bool(self, value: bool, offset=0) -> None: ... def write_void_p(self, value: 'void_p', offset=0) -> None: ... -char_ = refl("char") -uchar_ = refl("uchar") -short_ = refl("short") -ushort_ = refl("ushort") -int_ = refl("int") -uint_ = refl("uint") -long_ = refl("long") -ulong_ = refl("ulong") -longlong_ = refl("longlong") -ulonglong_ = refl("ulonglong") -float_ = refl("float") -double_ = refl("double") -bool_ = refl("bool") +def char_(val: int) -> struct: ... +def uchar_(val: int) -> struct: ... +def short_(val: int) -> struct: ... +def ushort_(val: int) -> struct: ... +def int_(val: int) -> struct: ... +def uint_(val: int) -> struct: ... +def long_(val: int) -> struct: ... +def ulong_(val: int) -> struct: ... +def longlong_(val: int) -> struct: ... +def ulonglong_(val: int) -> struct: ... +def float_(val: float) -> struct: ... +def double_(val: float) -> struct: ... +def bool_(val: bool) -> struct: ... char_p = void_p uchar_p = void_p @@ -131,4 +118,13 @@ longlong_p = void_p ulonglong_p = void_p float_p = void_p double_p = void_p -bool_p = void_p \ No newline at end of file +bool_p = void_p + +class array(struct): + count: int + item_size: int + + def __new__(cls, count: int, item_size: int): ... + def __getitem__(self, index: int) -> struct: ... + def __setitem__(self, index: int, value: struct) -> None: ... + def __len__(self) -> int: ... diff --git a/python/c.py b/python/c.py new file mode 100644 index 00000000..4e5ad1b9 --- /dev/null +++ b/python/c.py @@ -0,0 +1,27 @@ +class array(struct): + item_count: int + item_size: int + + def __new__(cls, item_count: int, item_size: int = 1): + obj = struct.__new__(cls, item_count * item_size) + obj._enable_instance_dict() + obj.item_count = item_count + obj.item_size = item_size + return obj + + def __getitem__(self, index: int) -> struct: + if index < 0 or index >= self.item_count: + raise IndexError("array index out of range") + p = self.addr() + self.item_size * index + return p.read_struct(self.item_size) + + def __setitem__(self, index: int, value: struct) -> None: + if index < 0 or index >= self.item_count: + raise IndexError("array index out of range") + if value.size() != self.item_size: + raise ValueError(f"array item size mismatch: {value.size()} != {self.item_size}") + p = self.addr() + self.item_size * index + p.write_struct(value) + + def __len__(self) -> int: + return self.item_count diff --git a/python/collections.py b/python/collections.py index 00a9e739..75d99567 100644 --- a/python/collections.py +++ b/python/collections.py @@ -156,5 +156,5 @@ class defaultdict: def items(self): return self._a.items() - def pop(self, key): - return self._a.pop(key) + def pop(self, *args): + return self._a.pop(*args) diff --git a/src/cffi.cpp b/src/cffi.cpp index 33bb790b..5369a44f 100644 --- a/src/cffi.cpp +++ b/src/cffi.cpp @@ -100,33 +100,33 @@ namespace pkpy{ } void C99Struct::_register(VM* vm, PyObject* mod, PyObject* type){ - vm->bind_constructor<-1>(type, [](VM* vm, ArgsView args){ + vm->bind_constructor<2>(type, [](VM* vm, ArgsView args){ Type cls = PK_OBJ_GET(Type, args[0]); - if(args.size() == 1+1){ - if(is_int(args[1])){ - int size = _CAST(int, args[1]); - return vm->heap.gcnew(cls, size); - } - if(is_non_tagged_type(args[1], vm->tp_str)){ - const Str& s = _CAST(Str&, args[1]); - return vm->heap.gcnew(cls, (void*)s.data, s.size); - } - if(is_non_tagged_type(args[1], vm->tp_bytes)){ - const Bytes& b = _CAST(Bytes&, args[1]); - return vm->heap.gcnew(cls, (void*)b.data(), b.size()); - } - vm->TypeError("expected int, str or bytes"); - return vm->None; + if(is_int(args[1])){ + int size = _CAST(int, args[1]); + return vm->heap.gcnew(cls, size); } - if(args.size() == 1+2){ - void* p = CAST(void*, args[1]); - int size = CAST(int, args[2]); - return vm->heap.gcnew(cls, p, size); + if(is_non_tagged_type(args[1], vm->tp_bytes)){ + const Bytes& b = _CAST(Bytes&, args[1]); + return vm->heap.gcnew(cls, (void*)b.data(), b.size()); } - vm->TypeError("expected 1 or 2 arguments"); + vm->TypeError("expected int or bytes"); return vm->None; }); + vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ + C99Struct& self = _CAST(C99Struct&, obj); + std::stringstream ss; + ss << ""; + return VAR(ss.str()); + }); + + vm->bind__hash__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ + C99Struct& self = _CAST(C99Struct&, obj); + std::string_view view((char*)self.p, self.size); + return (i64)std::hash()(view); + }); + vm->bind_method<0>(type, "addr", [](VM* vm, ArgsView args){ C99Struct& self = _CAST(C99Struct&, args[0]); return VAR_T(VoidP, self.p); @@ -142,18 +142,6 @@ namespace pkpy{ return VAR_T(C99Struct, self); }); - vm->bind_method<0>(type, "to_string", [](VM* vm, ArgsView args){ - C99Struct& self = _CAST(C99Struct&, args[0]); - return VAR(Str(self.p, self.size)); - }); - - vm->bind_method<0>(type, "to_bytes", [](VM* vm, ArgsView args){ - C99Struct& self = _CAST(C99Struct&, args[0]); - std::vector buffer(self.size); - memcpy(buffer.data(), self.p, self.size); - return VAR(Bytes(std::move(buffer))); - }); - vm->bind__eq__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){ C99Struct& self = _CAST(C99Struct&, lhs); if(!is_non_tagged_type(rhs, C99Struct::_type(vm))) return vm->NotImplemented; @@ -198,8 +186,7 @@ namespace pkpy{ vm->bind_method<1>(type, "read_struct", [](VM* vm, ArgsView args){ VoidP& self = _CAST(VoidP&, args[0]); - const Str& type = CAST(Str&, args[1]); - int size = c99_sizeof(vm, type); + int size = CAST(int, args[1]); return VAR_T(C99Struct, self.ptr, size); }); @@ -211,30 +198,6 @@ namespace pkpy{ }); } - void C99ReflType::_register(VM* vm, PyObject* mod, PyObject* type){ - vm->bind_notimplemented_constructor(type); - - vm->bind_method<0>(type, "__call__", [](VM* vm, ArgsView args){ - C99ReflType& self = _CAST(C99ReflType&, args[0]); - return VAR_T(C99Struct, nullptr, self.size); - }); - - vm->bind_method<0>(type, "__repr__", [](VM* vm, ArgsView args){ - C99ReflType& self = _CAST(C99ReflType&, args[0]); - return VAR(""); - }); - - vm->bind_method<0>(type, "name", [](VM* vm, ArgsView args){ - C99ReflType& self = _CAST(C99ReflType&, args[0]); - return VAR(self.name); - }); - - vm->bind_method<0>(type, "size", [](VM* vm, ArgsView args){ - C99ReflType& self = _CAST(C99ReflType&, args[0]); - return VAR(self.size); - }); - } - void add_module_c(VM* vm){ PyObject* mod = vm->new_module("c"); @@ -267,50 +230,44 @@ void add_module_c(VM* vm){ vm->bind_func<1>(mod, "sizeof", [](VM* vm, ArgsView args){ const Str& type = CAST(Str&, args[0]); - i64 size = c99_sizeof(vm, type); - return VAR(size); - }); - - vm->bind_func<1>(mod, "refl", [](VM* vm, ArgsView args){ - const Str& key = CAST(Str&, args[0]); - auto it = _refl_types.find(key.sv()); - if(it == _refl_types.end()) vm->ValueError("reflection type not found"); - const ReflType& rt = it->second; - return VAR_T(C99ReflType, rt); + auto it = _refl_types.find(type.sv()); + if(it != _refl_types.end()) return VAR(it->second.size); + vm->ValueError("not a valid c99 type"); + return vm->None; }); VoidP::register_class(vm, mod); C99Struct::register_class(vm, mod); - C99ReflType::register_class(vm, mod); mod->attr().set("NULL", VAR_T(VoidP, nullptr)); - add_refl_type("char", sizeof(char)); - add_refl_type("uchar", sizeof(unsigned char)); - add_refl_type("short", sizeof(short)); - add_refl_type("ushort", sizeof(unsigned short)); - add_refl_type("int", sizeof(int)); - add_refl_type("uint", sizeof(unsigned int)); - add_refl_type("long", sizeof(long)); - add_refl_type("ulong", sizeof(unsigned long)); - add_refl_type("longlong", sizeof(long long)); - add_refl_type("ulonglong", sizeof(unsigned long long)); - add_refl_type("float", sizeof(float)); - add_refl_type("double", sizeof(double)); - add_refl_type("bool", sizeof(bool)); add_refl_type("void_p", sizeof(void*)); - PyObject* void_p_t = mod->attr("void_p"); - for(const char* t: {"char", "uchar", "short", "ushort", "int", "uint", "long", "ulong", "longlong", "ulonglong", "float", "double", "bool"}){ - mod->attr().set(Str(t) + "_", VAR_T(C99ReflType, _refl_types[t])); - mod->attr().set(Str(t) + "_p", void_p_t); - } -} -int c99_sizeof(VM* vm, const Str& type){ - auto it = _refl_types.find(type.sv()); - if(it != _refl_types.end()) return it->second.size; - vm->ValueError("not a valid c99 type"); - return 0; +#define BIND_PRIMITIVE(T, name) \ + vm->bind_func<1>(mod, name "_", [](VM* vm, ArgsView args){ \ + T val = CAST(T, args[0]); \ + return VAR_T(C99Struct, &val, sizeof(T)); \ + }); \ + add_refl_type(name, sizeof(T)); \ + mod->attr().set(name "_p", void_p_t); \ + + BIND_PRIMITIVE(char, "char") + BIND_PRIMITIVE(unsigned char, "uchar") + BIND_PRIMITIVE(short, "short") + BIND_PRIMITIVE(unsigned short, "ushort") + BIND_PRIMITIVE(int, "int") + BIND_PRIMITIVE(unsigned int, "uint") + BIND_PRIMITIVE(long, "long") + BIND_PRIMITIVE(unsigned long, "ulong") + BIND_PRIMITIVE(long long, "longlong") + BIND_PRIMITIVE(unsigned long long, "ulonglong") + BIND_PRIMITIVE(float, "float") + BIND_PRIMITIVE(double, "double") + BIND_PRIMITIVE(bool, "bool") + + // add array type + CodeObject_ code = vm->compile(kPythonLibs["c"], "c.py", EXEC_MODE); + vm->_exec(code, mod); } } // namespace pkpy \ No newline at end of file diff --git a/src/pocketpy.cpp b/src/pocketpy.cpp index c92cef1e..762b887a 100644 --- a/src/pocketpy.cpp +++ b/src/pocketpy.cpp @@ -352,6 +352,18 @@ void init_builtins(VM* _vm) { return vm->heap.gcnew(t); }); + _vm->bind_method<0>("object", "_enable_instance_dict", [](VM* vm, ArgsView args){ + PyObject* self = args[0]; + if(is_tagged(self)){ + vm->TypeError("object: tagged object cannot enable instance dict"); + } + if(self->is_attr_valid()){ + vm->TypeError("object: instance dict is already enabled"); + } + self->enable_instance_dict(); + return vm->None; + }); + _vm->bind_constructor<2>("type", PK_LAMBDA(vm->_t(args[1]))); _vm->bind_constructor<-1>("range", [](VM* vm, ArgsView args) { diff --git a/tests/80_c.py b/tests/80_c.py index 45bfc96e..1ddae36b 100644 --- a/tests/80_c.py +++ b/tests/80_c.py @@ -1,23 +1,18 @@ import c -c_int = c.refl("int") -assert c_int.size() == c.sizeof("int") array = c.malloc(c.sizeof("int") * 10) for i in range(10): off = c.sizeof("int") * i (array+off).write_int(i) -x = c_int() -x.addr().write_int(0) +x = c.int_(0) for i in range(10): off = c.sizeof("int") * i i = (array+off).read_int() - x.addr().write_int( - x.addr().read_int() + i - ) + x.write_int(x.read_int() + i) -assert x.addr().read_int() == (0+9)*10//2 +assert x.read_int() == (0+9)*10//2 c.memset(array, 0, c.sizeof("int") * 10) diff --git a/tests/80_linalg.py b/tests/80_linalg.py index 22b44c74..afc880f7 100644 --- a/tests/80_linalg.py +++ b/tests/80_linalg.py @@ -332,7 +332,10 @@ for i in range(3): for j in range(3): list_mat[i][j] = test_mat[i, j] determinant = list_mat[0][0]*(list_mat[1][1]*list_mat[2][2] - list_mat[1][2]*list_mat[2][1]) - list_mat[0][1]*(list_mat[1][0]*list_mat[2][2] - list_mat[1][2]*list_mat[2][0]) + list_mat[0][2]*(list_mat[1][0]*list_mat[2][1] - list_mat[1][1]*list_mat[2][0]) -assert round(determinant, 3) == round(test_mat_copy.determinant(), 3) +_0 = determinant +_1 = test_mat_copy.determinant() +_0, _1 = round(_0, 2), round(_1, 2) +assert (_0 == _1), f'{_0} != {_1}' diff --git a/tests/99_cffi_2.py b/tests/99_cffi_2.py index 562f46a7..09834cd4 100644 --- a/tests/99_cffi_2.py +++ b/tests/99_cffi_2.py @@ -1,14 +1,5 @@ import c -c_int = c.refl("int") -assert c_int.name() == "int" -assert c_int.__repr__() == '' -# ------------------------------------------------ -c_int_1 = c.refl("int") -c_struct_1 = c_int_1() -assert (c_int_1() == c_int_1()) -assert (c_struct_1 == c_struct_1) == True -# ------------------------------------------------ assert c.void_p.from_hex('0x2568b60').hex() == '0x2568b60' # ------------------------------------------------ @@ -49,17 +40,15 @@ c_void_1.read_bytes(5) c_void_1.write_bytes(c_void_1.read_bytes(5)) # ------------------------------------------------ c_void_1 = c.malloc(32) -my_struct2 = c.struct(c_void_1, 32) - -data_str = "Hello, World!" -my_struct3 = c.struct(data_str) +my_struct2 = c_void_1.read_struct(32) +assert my_struct2.size() == 32 data_bytes = bytes([1,2,3]) my_struct4 = c.struct(data_bytes) try: c.struct(True) - raise Exception('c.struct 的构造方法未能触发 TypeError("expected int, str or bytes")') + raise Exception('c.struct 的构造方法未能触发 TypeError("expected int or bytes")') except TypeError: pass @@ -75,18 +64,25 @@ assert my_struct1.size() == 16 # 对 c.struct 的 copy 方法的测试不完全 assert my_struct1.copy().size() == 16 -data_str = "Hello, World!" -my_struct3 = c.struct(data_str) -assert my_struct3.to_string() == data_str - data_bytes = bytes([1,2,3]) my_struct4 = c.struct(data_bytes) -assert my_struct4.to_bytes() == data_bytes +assert my_struct4.addr().read_bytes( + my_struct4.size() +) == data_bytes # ------------------------------------------------ # 此处测试并不完全 c_void_1 = c.malloc(16) my_struct1 = c.struct(16) -c_void_1.read_struct('long') c_void_1.write_struct(my_struct1) +assert c_void_1.read_struct(16) == my_struct1 + +from c import array, int_ +a = array(10, item_size=4) +assert a.item_count == 10 +assert a.item_size == 4 + +_ = hash(a) +a[4] = int_(123) +assert a[4] == int_(123) \ No newline at end of file