diff --git a/.gitmodules b/.gitmodules index 302f5629..cd448cf9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "3rd/dmath/dmath"] path = 3rd/dmath/dmath url = https://github.com/pocketpy/dmath +[submodule "3rd/periphery/c-periphery"] + path = 3rd/periphery/c-periphery + url = https://github.com/vsergeev/c-periphery.git diff --git a/3rd/periphery/c-periphery b/3rd/periphery/c-periphery new file mode 160000 index 00000000..76c4edd4 --- /dev/null +++ b/3rd/periphery/c-periphery @@ -0,0 +1 @@ +Subproject commit 76c4edd4b5c43a597fd37c618ed32dbc2a27ec40 diff --git a/include/pocketpy/interpreter/modules.h b/include/pocketpy/interpreter/modules.h index 80134e80..2c73d6bb 100644 --- a/include/pocketpy/interpreter/modules.h +++ b/include/pocketpy/interpreter/modules.h @@ -19,6 +19,7 @@ void pk__add_module_base64(); void pk__add_module_importlib(); void pk__add_module_unicodedata(); +void pk__add_module_stdc(); void pk__add_module_vmath(); void pk__add_module_array2d(); void pk__add_module_colorcvt(); diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index 1c3eaf00..6eac3fcf 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -876,6 +876,16 @@ enum py_PredefinedType { tp_ImportError, tp_AssertionError, tp_KeyError, + /* stdc */ + tp_stdc_Memory, + tp_stdc_Char, tp_stdc_UChar, + tp_stdc_Short, tp_stdc_UShort, + tp_stdc_Int, tp_stdc_UInt, + tp_stdc_Long, tp_stdc_ULong, + tp_stdc_LongLong, tp_stdc_ULongLong, + tp_stdc_Float, tp_stdc_Double, + tp_stdc_Pointer, + tp_stdc_Bool, /* vmath */ tp_vec2, tp_vec3, diff --git a/include/typings/picoterm_io.pyi b/include/typings/picoterm_io.pyi new file mode 100644 index 00000000..3e5976d5 --- /dev/null +++ b/include/typings/picoterm_io.pyi @@ -0,0 +1,21 @@ +from typing import Literal +from vmath import vec2, color32 +from cute_png import Image + +def read_axis(self) -> vec2: ... + +def read_button(self, index: Literal[0, 1, 2, 3, 4, 5], ttl_ms: int = 10) -> bool: ... +def read_all_buttons(self, out_list: list[bool], ttl_ms: int = 10) -> None: ... +def clear_button(self, index: Literal[0, 1, 2, 3, 4, 5]) -> None: ... +def clear_all_buttons(self) -> None: ... + +def read_buzzer_freq(self) -> int: ... +def read_buzzer_volume(self) -> float: ... +def write_buzzer_freq(self, freq: int) -> None: ... +def write_buzzer_volume(self, volume: float) -> None: ... + +def write_fb0(self, image: Image) -> None: ... + +def read_led(self, index: Literal[0, 1, 2, 3]) -> color32: ... +def write_led(self, index: int, color: color32) -> None: ... +def write_leds(self, _0: color32 | None, _1: color32 | None, _2: color32 | None, _3: color32 | None) -> None: ... diff --git a/include/typings/stdc.pyi b/include/typings/stdc.pyi new file mode 100644 index 00000000..eaac5561 --- /dev/null +++ b/include/typings/stdc.pyi @@ -0,0 +1,61 @@ +from typing import overload, Self + +intptr = int + +def malloc(size: int) -> intptr: ... +def free(ptr: intptr) -> None: ... + +def memcpy(dst: intptr, src: intptr, n: int) -> None: ... +def memset(s: intptr, c: int, n: int) -> None: ... +def memcmp(s1: intptr, s2: intptr, n: int) -> int: ... + +def addressof(obj: Memory) -> intptr: ... +def sizeof(obj: type[Memory]) -> int: ... + +def read_cstr(p: intptr) -> str: ... +def read_bytes(p: intptr, n: int) -> bytes: ... +def write_cstr(p: intptr, data: str) -> None: ... +def write_bytes(p: intptr, data: bytes) -> None: ... + +class Memory: ... + +class _BuiltinMemory[T](Memory): + value: T + + def __new__(cls, value: T | None = None) -> None: ... + @staticmethod + def read(p: intptr, offset: int) -> T: ... + @staticmethod + def write(p: intptr, offset: int, value: T) -> None: ... + @staticmethod + def array(length: int) -> Self: ... + + @property + def size(self) -> int: ... + + def __getitem__(self, index: int) -> T: ... + def __setitem__(self, index: int, value: T) -> None: ... + +class Char(_BuiltinMemory[int]): ... +class UChar(_BuiltinMemory[int]): ... +class Short(_BuiltinMemory[int]): ... +class UShort(_BuiltinMemory[int]): ... +class Int(_BuiltinMemory[int]): ... +class UInt(_BuiltinMemory[int]): ... +class Long(_BuiltinMemory[int]): ... +class ULong(_BuiltinMemory[int]): ... +class LongLong(_BuiltinMemory[int]): ... + +class Float(_BuiltinMemory[float]): ... +class Double(_BuiltinMemory[float]): ... +class Pointer(_BuiltinMemory[intptr]): ... +class Bool(_BuiltinMemory[bool]): ... + +INT8: _BuiltinMemory[int] +UINT8: _BuiltinMemory[int] +INT16: _BuiltinMemory[int] +UINT16: _BuiltinMemory[int] +INT32: _BuiltinMemory[int] +UINT32: _BuiltinMemory[int] +INT64: _BuiltinMemory[int] +UINT64: _BuiltinMemory[int] diff --git a/scripts/c_bind/c_bind/function.py b/scripts/c_bind/c_bind/function.py index 3d982bfc..fe84ad58 100644 --- a/scripts/c_bind/c_bind/function.py +++ b/scripts/c_bind/c_bind/function.py @@ -2,6 +2,12 @@ from .writer import Writer from .converters import get_converter from .schema import Function +from keyword import iskeyword + +def sanitize_name(name: str): + if iskeyword(name): + return name + '_' + return name def gen_function(w: Writer, pyi_w: Writer, function: Function): name = function.name @@ -35,10 +41,11 @@ def gen_function(w: Writer, pyi_w: Writer, function: Function): # pyi py_args = [] # arg_names = [f'_{i}' for i in range(len(args_cvt))] - arg_names = [arg.name for arg in function.params] + arg_names = [sanitize_name(arg.name) for arg in function.params] for i in range(len(args_cvt)): py_args.append(f'{arg_names[i]}: {args_cvt[i].py_T}') + py_args.append('/') pyi_w.write(f'def {name}({", ".join(py_args)}) -> {ret_cvt.py_T}:') if function.desc: pyi_w.write(f' """Wraps `{function.signature()}`\n\n {function.desc}"""') diff --git a/scripts/c_bind/c_bind/library.py b/scripts/c_bind/c_bind/library.py index 49fff49f..cf69b4ed 100644 --- a/scripts/c_bind/c_bind/library.py +++ b/scripts/c_bind/c_bind/library.py @@ -191,8 +191,8 @@ class Library: self.functions.append(Function( name=function.name, params=[FunctionParam( - type=param, - name=f'_{i}' + type=param[0], + name=param[1] or f'_{i}' ) for i, param in enumerate(function.args)], ret_type=function.ret )) diff --git a/scripts/c_bind/c_bind/meta/parser.py b/scripts/c_bind/c_bind/meta/parser.py index 64cb8ca1..9839ae55 100644 --- a/scripts/c_bind/c_bind/meta/parser.py +++ b/scripts/c_bind/c_bind/meta/parser.py @@ -129,7 +129,7 @@ class Header: else: T, name = self.build_param(param) if T != 'void': - func.args.append(T) + func.args.append((T, name)) self.functions.append(func) def build(self, ast: c_ast.FileAST): diff --git a/scripts/c_bind/c_bind/meta/schema.py b/scripts/c_bind/c_bind/meta/schema.py index 9450fb0a..52ba4cc6 100644 --- a/scripts/c_bind/c_bind/meta/schema.py +++ b/scripts/c_bind/c_bind/meta/schema.py @@ -32,7 +32,7 @@ class Enum: class Function: def __init__(self, name: str, ret: str): self.name = name - self.args = [] # type: list[str] + self.args = [] # type: list[tuple[str, str]] self.ret = ret def __repr__(self): diff --git a/scripts/c_bind/gen_periphery.py b/scripts/c_bind/gen_periphery.py new file mode 100644 index 00000000..fefaaf45 --- /dev/null +++ b/scripts/c_bind/gen_periphery.py @@ -0,0 +1,26 @@ +import pcpp +import pycparser +from c_bind import Library, set_vmath_converter, set_enum_converters +from c_bind.meta import Header +import os + +file_dir = os.path.dirname(os.path.abspath(__file__)) + +path = '3rd/periphery/c-periphery/src/gpio.h' +code = pcpp.CmdPreprocessor([None, path, '-o', 'tmp.h', '-I', os.path.join(file_dir, 'libc_include')]) + +ast = pycparser.parse_file('tmp.h') +os.remove('tmp.h') + +header = Header() +header.build(ast) + +lib = Library.from_header('periphery', header) + +set_enum_converters([enum.name for enum in lib.enums]) + +lib.build( + includes=['c-periphery/gpio.h'], + glue_dir='3rd/periphery/src', + stub_dir='include/typings' +) diff --git a/scripts/c_bind/requirements.txt b/scripts/c_bind/requirements.txt new file mode 100644 index 00000000..b4d0b3c7 --- /dev/null +++ b/scripts/c_bind/requirements.txt @@ -0,0 +1,2 @@ +pcpp +pycparser diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 8cae7635..98842cbb 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -237,6 +237,7 @@ void VM__ctor(VM* self) { py_newnotimplemented(py_emplacedict(self->builtins, py_name("NotImplemented"))); + pk__add_module_stdc(); pk__add_module_vmath(); pk__add_module_array2d(); pk__add_module_colorcvt(); diff --git a/src/modules/stdc.c b/src/modules/stdc.c new file mode 100644 index 00000000..35f20ef7 --- /dev/null +++ b/src/modules/stdc.c @@ -0,0 +1,275 @@ +#include "pocketpy/pocketpy.h" +#include "pocketpy/interpreter/vm.h" +#include + +#define DEF_BUILTIN_MEMORY_T(Char_, char_, tp_int_, py_newint_, py_toint_, py_i64_) \ + static bool stdc_##Char_##__new__(int argc, py_Ref argv) { \ + char_* ud = py_newobject(py_retval(), tp_stdc_##Char_, 0, sizeof(char_)); \ + if(argc == 2) { \ + PY_CHECK_ARG_TYPE(1, tp_int_); \ + *ud = (char_)py_toint_(&argv[1]); \ + } else if(argc > 2) { \ + return TypeError("expected 1 or 2 arguments, got %d", argc); \ + } \ + return true; \ + } \ + static bool stdc_##Char_##__get_value(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(1); \ + char_* ud = py_touserdata(argv); \ + py_newint_(py_retval(), (py_i64_)(*ud)); \ + return true; \ + } \ + static bool stdc_##Char_##__set_value(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(2); \ + char_* ud = py_touserdata(argv); \ + PY_CHECK_ARG_TYPE(1, tp_int_); \ + *ud = (char_)py_toint_(&argv[1]); \ + py_newnone(py_retval()); \ + return true; \ + } \ + static bool stdc_##Char_##__read_STATIC(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(2); \ + PY_CHECK_ARG_TYPE(0, tp_int); \ + PY_CHECK_ARG_TYPE(1, tp_int); \ + char_* p = (char_*)(intptr_t)py_toint(&argv[0]); \ + int offset = py_toint(&argv[1]); \ + py_newint_(py_retval(), (py_i64_)(p[offset])); \ + return true; \ + } \ + static bool stdc_##Char_##__write_STATIC(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(3); \ + PY_CHECK_ARG_TYPE(0, tp_int); \ + PY_CHECK_ARG_TYPE(1, tp_int); \ + PY_CHECK_ARG_TYPE(2, tp_int_); \ + char_* p = (char_*)(intptr_t)py_toint(&argv[0]); \ + int offset = py_toint(&argv[1]); \ + p[offset] = (char_)py_toint_(&argv[2]); \ + py_newnone(py_retval()); \ + return true; \ + } \ + static bool stdc_##Char_##__array_STATIC(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(1); \ + PY_CHECK_ARG_TYPE(0, tp_int); \ + int length = py_toint(argv); \ + int size = sizeof(char_) * length; \ + py_newobject(py_retval(), tp_stdc_##Char_, 0, size); \ + return true; \ + } \ + static bool stdc_##Char_##__getitem__(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(2); \ + char_* ud = py_touserdata(argv); \ + PY_CHECK_ARG_TYPE(1, tp_int); \ + int index = py_toint(&argv[1]); \ + py_newint_(py_retval(), (py_i64_)(ud[index])); \ + return true; \ + } \ + static bool stdc_##Char_##__setitem__(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(3); \ + char_* ud = py_touserdata(argv); \ + PY_CHECK_ARG_TYPE(1, tp_int); \ + PY_CHECK_ARG_TYPE(2, tp_int_); \ + int index = py_toint(&argv[1]); \ + ud[index] = (char_)py_toint_(&argv[2]); \ + py_newnone(py_retval()); \ + return true; \ + } \ + static void pk__bind_stdc_##Char_(py_Ref mod) { \ + py_Type type = py_newtype(#Char_, tp_stdc_Memory, mod, NULL); \ + py_tpsetfinal(type); \ + assert(type == tp_stdc_##Char_); \ + py_bindmagic(type, __new__, stdc_##Char_##__new__); \ + py_bindmagic(type, __getitem__, stdc_##Char_##__getitem__); \ + py_bindmagic(type, __setitem__, stdc_##Char_##__setitem__); \ + py_bindproperty(type, "value", stdc_##Char_##__get_value, stdc_##Char_##__set_value); \ + py_bindstaticmethod(type, "read", stdc_##Char_##__read_STATIC); \ + py_bindstaticmethod(type, "write", stdc_##Char_##__write_STATIC); \ + py_bindstaticmethod(type, "array", stdc_##Char_##__array_STATIC); \ + py_newint(py_emplacedict(py_tpobject(type), py_name("size")), sizeof(char_)); \ + } + +DEF_BUILTIN_MEMORY_T(Char, char, tp_int, py_newint, py_toint, py_i64) +DEF_BUILTIN_MEMORY_T(UChar, unsigned char, tp_int, py_newint, py_toint, py_i64) +DEF_BUILTIN_MEMORY_T(Short, short, tp_int, py_newint, py_toint, py_i64) +DEF_BUILTIN_MEMORY_T(UShort, unsigned short, tp_int, py_newint, py_toint, py_i64) +DEF_BUILTIN_MEMORY_T(Int, int, tp_int, py_newint, py_toint, py_i64) +DEF_BUILTIN_MEMORY_T(UInt, unsigned int, tp_int, py_newint, py_toint, py_i64) +DEF_BUILTIN_MEMORY_T(Long, long, tp_int, py_newint, py_toint, py_i64) +DEF_BUILTIN_MEMORY_T(ULong, unsigned long, tp_int, py_newint, py_toint, py_i64) +DEF_BUILTIN_MEMORY_T(LongLong, long long, tp_int, py_newint, py_toint, py_i64) +DEF_BUILTIN_MEMORY_T(ULongLong, unsigned long long, tp_int, py_newint, py_toint, py_i64) +DEF_BUILTIN_MEMORY_T(Float, float, tp_float, py_newfloat, py_tofloat, float) +DEF_BUILTIN_MEMORY_T(Double, double, tp_float, py_newfloat, py_tofloat, double) +DEF_BUILTIN_MEMORY_T(Pointer, void*, tp_int, py_newint, py_toint, py_i64) +DEF_BUILTIN_MEMORY_T(Bool, bool, tp_bool, py_newbool, py_tobool, bool) + +#undef DEF_BUILTIN_MEMORY_T + +static bool stdc_malloc(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + PY_CHECK_ARG_TYPE(0, tp_int); + py_i64 size = py_toint(&argv[0]); + void* p = py_malloc(size); + py_newint(py_retval(), (py_i64)(intptr_t)p); + return true; +} + +static bool stdc_free(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + PY_CHECK_ARG_TYPE(0, tp_int); + void* p = (void*)(intptr_t)py_toint(&argv[0]); + py_free(p); + py_newnone(py_retval()); + return true; +} + +static bool stdc_memcpy(int argc, py_Ref argv) { + PY_CHECK_ARGC(3); + PY_CHECK_ARG_TYPE(0, tp_int); + PY_CHECK_ARG_TYPE(1, tp_int); + PY_CHECK_ARG_TYPE(2, tp_int); + void* dst = (void*)(intptr_t)py_toint(&argv[0]); + void* src = (void*)(intptr_t)py_toint(&argv[1]); + py_i64 n = py_toint(&argv[2]); + memcpy(dst, src, (size_t)n); + py_newnone(py_retval()); + return true; +} + +static bool stdc_memset(int argc, py_Ref argv) { + PY_CHECK_ARGC(3); + PY_CHECK_ARG_TYPE(0, tp_int); + PY_CHECK_ARG_TYPE(1, tp_int); + PY_CHECK_ARG_TYPE(2, tp_int); + void* dst = (void*)(intptr_t)py_toint(&argv[0]); + int value = (int)py_toint(&argv[1]); + py_i64 n = py_toint(&argv[2]); + memset(dst, value, (size_t)n); + py_newnone(py_retval()); + return true; +} + +static bool stdc_memcmp(int argc, py_Ref argv) { + PY_CHECK_ARGC(3); + PY_CHECK_ARG_TYPE(0, tp_int); + PY_CHECK_ARG_TYPE(1, tp_int); + PY_CHECK_ARG_TYPE(2, tp_int); + void* p1 = (void*)(intptr_t)py_toint(&argv[0]); + void* p2 = (void*)(intptr_t)py_toint(&argv[1]); + py_i64 n = py_toint(&argv[2]); + int res = memcmp(p1, p2, (size_t)n); + py_newint(py_retval(), (py_i64)res); + return true; +} + +static bool stdc_addressof(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + if(!py_checkinstance(argv, tp_stdc_Memory)) return false; + void* ud = py_touserdata(argv); + py_newint(py_retval(), (py_i64)(intptr_t)ud); + return true; +} + +static bool stdc_sizeof(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + PY_CHECK_ARG_TYPE(0, tp_type); + py_Type type = py_totype(&argv[0]); + if(!py_issubclass(type, tp_stdc_Memory)) { + return TypeError("expected a type derived from stdc.Memory"); + } + py_assign(py_retval(), py_getdict(py_tpobject(type), py_name("size"))); + return true; +} + +static bool stdc_read_cstr(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + PY_CHECK_ARG_TYPE(0, tp_int); + char* p = (char*)(intptr_t)py_toint(&argv[0]); + py_newstr(py_retval(), p); + return true; +} + +static bool stdc_write_cstr(int argc, py_Ref argv) { + PY_CHECK_ARGC(2); + PY_CHECK_ARG_TYPE(0, tp_int); + PY_CHECK_ARG_TYPE(1, tp_str); + char* p = (char*)(intptr_t)py_toint(&argv[0]); + c11_sv sv = py_tosv(&argv[1]); + memcpy(p, sv.data, sv.size); + p[sv.size] = '\0'; + py_newnone(py_retval()); + return true; +} + +static bool stdc_read_bytes(int argc, py_Ref argv) { + PY_CHECK_ARGC(2); + PY_CHECK_ARG_TYPE(0, tp_int); + PY_CHECK_ARG_TYPE(1, tp_int); + unsigned char* p = (unsigned char*)(intptr_t)py_toint(&argv[0]); + int size = py_toint(&argv[1]); + unsigned char* dst = py_newbytes(py_retval(), size); + memcpy(dst, p, size); + return true; +} + +static bool stdc_write_bytes(int argc, py_Ref argv) { + PY_CHECK_ARGC(2); + PY_CHECK_ARG_TYPE(0, tp_int); + PY_CHECK_ARG_TYPE(1, tp_bytes); + unsigned char* p = (unsigned char*)(intptr_t)py_toint(&argv[0]); + int size; + unsigned char* src = py_tobytes(&argv[1], &size); + memcpy(p, src, size); + py_newnone(py_retval()); + return true; +} + +void pk__add_module_stdc() { + py_Ref mod = py_newmodule("stdc"); + + py_bindfunc(mod, "malloc", stdc_malloc); + py_bindfunc(mod, "free", stdc_free); + py_bindfunc(mod, "memcpy", stdc_memcpy); + py_bindfunc(mod, "memset", stdc_memset); + py_bindfunc(mod, "memcmp", stdc_memcmp); + + py_bindfunc(mod, "addressof", stdc_addressof); + py_bindfunc(mod, "sizeof", stdc_sizeof); + + py_bindfunc(mod, "read_cstr", stdc_read_cstr); + py_bindfunc(mod, "write_cstr", stdc_write_cstr); + py_bindfunc(mod, "read_bytes", stdc_read_bytes); + py_bindfunc(mod, "write_bytes", stdc_write_bytes); + + py_Type Memory = py_newtype("Memory", tp_object, mod, NULL); + assert(Memory == tp_stdc_Memory); + + pk__bind_stdc_Char(mod); + pk__bind_stdc_UChar(mod); + pk__bind_stdc_Short(mod); + pk__bind_stdc_UShort(mod); + pk__bind_stdc_Int(mod); + pk__bind_stdc_UInt(mod); + pk__bind_stdc_Long(mod); + pk__bind_stdc_ULong(mod); + pk__bind_stdc_LongLong(mod); + pk__bind_stdc_ULongLong(mod); + + for(int size = 1; size <= 8; size *= 2) { + for(py_Type t = tp_stdc_Char; t <= tp_stdc_ULongLong; t += 2) { + py_Ref size_var = py_getdict(py_tpobject(t), py_name("size")); + if(py_toint(size_var) == size) { + char buf[16]; + snprintf(buf, sizeof(buf), "Int%d", size * 8); + py_setdict(mod, py_name(buf), py_tpobject(t)); + snprintf(buf, sizeof(buf), "UInt%d", size * 8); + py_setdict(mod, py_name(buf), py_tpobject(t + 1)); + break; + } + } + } + + pk__bind_stdc_Float(mod); + pk__bind_stdc_Double(mod); + pk__bind_stdc_Pointer(mod); + pk__bind_stdc_Bool(mod); +} \ No newline at end of file diff --git a/tests/793_stdc.py b/tests/793_stdc.py new file mode 100644 index 00000000..4c5b7c71 --- /dev/null +++ b/tests/793_stdc.py @@ -0,0 +1,62 @@ +from stdc import * + +assert sizeof(Int8) == sizeof(UInt8) == 1 +assert sizeof(Int16) == sizeof(UInt16) == 2 +assert sizeof(Int32) == sizeof(UInt32) == 4 +assert sizeof(Int64) == sizeof(UInt64) == 8 + +assert sizeof(Float) == 4 +assert sizeof(Double) == 8 + +assert sizeof(Bool) == 1 +assert sizeof(Pointer) in (4, 8) + +x = Int32(42) +assert x.value == 42 +x.value = 100 +assert x.value == 100 + +Int32.read(addressof(x), 0) == 100 +Int32.write(addressof(x), 0, 200) +assert x.value == 200 + +# test array +arr = Int32.array(3) +arr[0] = 10 +arr[1] = 20 +arr[2] = 30 +assert arr[0] == 10 +assert arr[1] == 20 +assert arr[2] == 30 + +# test malloc, memset, memcpy +p = malloc(3 * sizeof(Int32)) +memset(p, 0, 3 * sizeof(Int32)) +memcpy(p, addressof(arr), 3 * sizeof(Int32)) +for i in range(3): + assert arr[i] == Int32.read(p, i) + +assert memcmp(p, addressof(arr), 3 * sizeof(Int32)) == 0 + +# test free +free(p) + +# test float +y = Double.array(3) +y[0] = 1.1 +y[1] = 2.2 +y[2] = 3.3 +assert Double.read(addressof(y), 0) == 1.1 +assert Double.read(addressof(y), 1) == 2.2 +assert Double.read(addressof(y), 2) == 3.3 + +# test read_cstr and write_cstr +a = Char.array(20) +write_cstr(addressof(a), "hello") +assert read_cstr(addressof(a)) == "hello" + +a[3] = 0 +assert read_cstr(addressof(a)) == "hel" + +# test read_bytes and write_bytes +assert read_bytes(addressof(a), 5) == b'hel\x00o'