From d8d25894c73c209701cc076d4b13af0dd11f4429 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Mon, 15 Jan 2024 14:40:24 +0800 Subject: [PATCH] ... --- include/pocketpy/bindings.h | 3 +- include/pocketpy/str.h | 1 + include/pocketpy/vm.h | 10 ++++- include/typings/c.pyi | 4 ++ python/pickle.py | 73 +++++++++++++++++++++---------------- src/cffi.cpp | 27 ++++++++++++++ src/linalg.cpp | 22 ----------- src/pocketpy.cpp | 5 +-- src/str.cpp | 6 +++ tests/80_c.py | 10 +++++ tests/80_linalg.py | 17 --------- 11 files changed, 101 insertions(+), 77 deletions(-) diff --git a/include/pocketpy/bindings.h b/include/pocketpy/bindings.h index 31a54a0d..1cc1dac0 100644 --- a/include/pocketpy/bindings.h +++ b/include/pocketpy/bindings.h @@ -138,13 +138,14 @@ void _bind(VM* vm, PyObject* obj, const char* sig, Ret(T::*func)(Params...)){ #define PY_STRUCT_LIKE(wT) \ using vT = std::remove_pointer_t()._())>; \ static_assert(std::is_trivially_copyable::value); \ + type->attr().set("__struct__", vm->True); \ vm->bind_func<1>(type, "from_struct", [](VM* vm, ArgsView args){ \ C99Struct& s = CAST(C99Struct&, args[0]); \ if(s.size != sizeof(vT)) vm->ValueError("size mismatch"); \ PyObject* obj = vm->heap.gcnew(wT::_type(vm)); \ memcpy(_CAST(wT&, obj)._(), s.p, sizeof(vT)); \ return obj; \ - }); \ + }, {}, BindType::STATICMETHOD); \ vm->bind_method<0>(type, "to_struct", [](VM* vm, ArgsView args){ \ wT& self = _CAST(wT&, args[0]); \ return VAR_T(C99Struct, self._(), sizeof(vT)); \ diff --git a/include/pocketpy/str.h b/include/pocketpy/str.h index b8c68e5b..b6258e87 100644 --- a/include/pocketpy/str.h +++ b/include/pocketpy/str.h @@ -141,6 +141,7 @@ struct SStream{ SStream& operator<<(std::string_view s); SStream& operator<<(char c); SStream& operator<<(StrName sn); + void write_hex(unsigned char c); template SStream& operator<<(T val){ diff --git a/include/pocketpy/vm.h b/include/pocketpy/vm.h index f780d505..128832ac 100644 --- a/include/pocketpy/vm.h +++ b/include/pocketpy/vm.h @@ -449,7 +449,7 @@ public: template PyObject* bind_method(PyObject*, Str, NativeFuncC); template - PyObject* bind_func(PyObject*, Str, NativeFuncC); + PyObject* bind_func(PyObject*, Str, NativeFuncC, UserData userdata={}, BindType bt=BindType::DEFAULT); void _error(PyObject*); PyObject* _run_top_frame(); void post_init(); @@ -624,8 +624,14 @@ PyObject* VM::bind_method(PyObject* obj, Str name, NativeFuncC fn) { } template -PyObject* VM::bind_func(PyObject* obj, Str name, NativeFuncC fn) { +PyObject* VM::bind_func(PyObject* obj, Str name, NativeFuncC fn, UserData userdata, BindType bt) { PyObject* nf = VAR(NativeFunc(fn, ARGC, false)); + PK_OBJ_GET(NativeFunc, nf).set_userdata(userdata); + switch(bt){ + case BindType::DEFAULT: break; + case BindType::STATICMETHOD: nf = VAR(StaticMethod(nf)); break; + case BindType::CLASSMETHOD: nf = VAR(ClassMethod(nf)); break; + } obj->attr().set(name, nf); return nf; } diff --git a/include/typings/c.pyi b/include/typings/c.pyi index 41628465..e23c200d 100644 --- a/include/typings/c.pyi +++ b/include/typings/c.pyi @@ -57,6 +57,10 @@ class struct: def __eq__(self, other: 'struct') -> bool: ... def __ne__(self, other: 'struct') -> bool: ... + def hex(self) -> str: ... + @staticmethod + def from_hex(s: str) -> 'struct': ... + def read_char(self, offset=0) -> int: ... def read_uchar(self, offset=0) -> int: ... def read_short(self, offset=0) -> int: ... diff --git a/python/pickle.py b/python/pickle.py index df8f7a6b..35976a2b 100644 --- a/python/pickle.py +++ b/python/pickle.py @@ -1,4 +1,5 @@ import json +from c import struct import builtins _BASIC_TYPES = [int, float, str, bool, type(None)] @@ -16,18 +17,20 @@ class _Pickler: self.raw_memo = {} # id -> int self.memo = [] # int -> object - def _type_id(self, o: type): - assert type(o) is type - name = o.__name__ - mod = o.__module__ + @staticmethod + def _type_id(t: type): + assert type(t) is type + name = t.__name__ + mod = t.__module__ if mod is not None: name = mod.__path__ + _MOD_T_SEP + name return name def wrap(self, o): - if type(o) in _BASIC_TYPES: + o_t = type(o) + if o_t in _BASIC_TYPES: return o - if type(o) is type: + if o_t is type: return ["type", self._type_id(o)] index = self.raw_memo.get(id(o), None) @@ -39,25 +42,29 @@ class _Pickler: self.memo.append(ret) self.raw_memo[id(o)] = index - if type(o) is tuple: + if o_t is tuple: ret.append("tuple") ret.append([self.wrap(i) for i in o]) return [index] - if type(o) is bytes: + if o_t is bytes: ret.append("bytes") ret.append([o[j] for j in range(len(o))]) return [index] - - if type(o) is list: + if o_t is list: ret.append("list") ret.append([self.wrap(i) for i in o]) return [index] - if type(o) is dict: + if o_t is dict: ret.append("dict") ret.append([[self.wrap(k), self.wrap(v)] for k,v in o.items()]) return [index] - _0 = self._type_id(type(o)) + _0 = self._type_id(o_t) + + if getattr(o_t, '__struct__', False): + ret.append(_0) + ret.append(o.to_struct().hex()) + return [index] if hasattr(o, "__getnewargs__"): _1 = o.__getnewargs__() # an iterable @@ -68,13 +75,11 @@ class _Pickler: if o.__dict__ is None: _2 = None else: - _2 = {} - for k,v in o.__dict__.items(): - _2[k] = self.wrap(v) + _2 = {k: self.wrap(v) for k,v in o.__dict__.items()} - ret.append(_0) - ret.append(_1) - ret.append(_2) + ret.append(_0) # type id + ret.append(_1) # newargs + ret.append(_2) # state return [index] def run_pipe(self): @@ -122,7 +127,6 @@ class _Unpickler: ret = bytes(o[1]) self.tag(index, ret) return ret - if o[0] == "list": ret = [] self.tag(index, ret) @@ -137,21 +141,26 @@ class _Unpickler: return ret # generic object - cls, newargs, state = o cls = _find_class(o[0]) - # create uninitialized instance - new_f = getattr(cls, "__new__") - if newargs is not None: - newargs = [self.unwrap(i) for i in newargs] - inst = new_f(cls, *newargs) + if getattr(cls, '__struct__', False): + inst = cls.from_struct(struct.from_hex(o[1])) + self.tag(index, inst) + return inst else: - inst = new_f(cls) - self.tag(index, inst) - # restore state - if state is not None: - for k,v in state.items(): - setattr(inst, k, self.unwrap(v)) - return inst + _, newargs, state = o + # create uninitialized instance + new_f = getattr(cls, "__new__") + if newargs is not None: + newargs = [self.unwrap(i) for i in newargs] + inst = new_f(cls, *newargs) + else: + inst = new_f(cls) + self.tag(index, inst) + # restore state + if state is not None: + for k,v in state.items(): + setattr(inst, k, self.unwrap(v)) + return inst def run_pipe(self): return self.unwrap(self.obj) diff --git a/src/cffi.cpp b/src/cffi.cpp index 124bfbe5..6cbc6470 100644 --- a/src/cffi.cpp +++ b/src/cffi.cpp @@ -44,6 +44,33 @@ namespace pkpy{ return vm->heap.gcnew(cls, size); }); + vm->bind_method<0>(type, "hex", [](VM* vm, ArgsView args){ + const C99Struct& self = _CAST(C99Struct&, args[0]); + SStream ss; + ss << "0x"; + for(int i=0; ibind_func<1>(type, "from_hex", [](VM* vm, ArgsView args){ + const Str& s = CAST(Str&, args[0]); + if(s.size<4 || s[0]!='0' || s[1]!='x' || s.size%2!=0) vm->ValueError("invalid hex string"); + C99Struct buffer(s.size/2-1, false); + for(int i=2; i='0' && s[i]<='9') c += s[i]-'0'; + else if(s[i]>='A' && s[i]<='F') c += s[i]-'A'+10; + else vm->ValueError(fmt("invalid hex char: '", s[i], "'")); + c <<= 4; + if(s[i+1]>='0' && s[i+1]<='9') c += s[i+1]-'0'; + else if(s[i+1]>='A' && s[i+1]<='F') c += s[i+1]-'A'+10; + else vm->ValueError(fmt("invalid hex char: '", s[i+1], "'")); + buffer.p[i/2-1] = c; + } + return VAR_T(C99Struct, std::move(buffer)); + }, {}, BindType::STATICMETHOD); + vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ C99Struct& self = _CAST(C99Struct&, obj); SStream ss; diff --git a/src/linalg.cpp b/src/linalg.cpp index 41c7732b..cd0476b5 100644 --- a/src/linalg.cpp +++ b/src/linalg.cpp @@ -105,11 +105,6 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, PyVec2& currentVelocity, float return vm->heap.gcnew(PK_OBJ_GET(Type, args[0]), Vec2(x, y)); }); - vm->bind_method<0>(type, "__getnewargs__", [](VM* vm, ArgsView args){ - PyVec2& self = _CAST(PyVec2&, args[0]); - return VAR(Tuple({ VAR(self.x), VAR(self.y) })); - }); - // @staticmethod vm->bind(type, "smooth_damp(current: vec2, target: vec2, current_velocity: vec2, smooth_time: float, max_speed: float, delta_time: float) -> vec2", [](VM* vm, ArgsView args){ Vec2 current = CAST(Vec2, args[0]); @@ -178,11 +173,6 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, PyVec2& currentVelocity, float return vm->heap.gcnew(PK_OBJ_GET(Type, args[0]), Vec3(x, y, z)); }); - vm->bind_method<0>(type, "__getnewargs__", [](VM* vm, ArgsView args){ - PyVec3& self = _CAST(PyVec3&, args[0]); - return VAR(Tuple({ VAR(self.x), VAR(self.y), VAR(self.z) })); - }); - vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ PyVec3& self = _CAST(PyVec3&, obj); std::stringstream ss; @@ -218,11 +208,6 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, PyVec2& currentVelocity, float return vm->heap.gcnew(PK_OBJ_GET(Type, args[0]), Vec4(x, y, z, w)); }); - vm->bind_method<0>(type, "__getnewargs__", [](VM* vm, ArgsView args){ - PyVec4& self = _CAST(PyVec4&, args[0]); - return VAR(Tuple({ VAR(self.x), VAR(self.y), VAR(self.z), VAR(self.w) })); - }); - vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ PyVec4& self = _CAST(PyVec4&, obj); std::stringstream ss; @@ -275,13 +260,6 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, PyVec2& currentVelocity, float return vm->None; }); - vm->bind_method<0>(type, "__getnewargs__", [](VM* vm, ArgsView args){ - PyMat3x3& self = _CAST(PyMat3x3&, args[0]); - Tuple t(9); - for(int i=0; i<9; i++) t[i] = VAR(self.v[i]); - return VAR(std::move(t)); - }); - vm->bind_method<1>(type, "assign", [](VM* vm, ArgsView args){ PyMat3x3& self = _CAST(PyMat3x3&, args[0]); const PyMat3x3& other = CAST(PyMat3x3&, args[1]); diff --git a/src/pocketpy.cpp b/src/pocketpy.cpp index c034dc7b..eead9ec2 100644 --- a/src/pocketpy.cpp +++ b/src/pocketpy.cpp @@ -988,9 +988,8 @@ void init_builtins(VM* _vm) { SStream ss; ss << "b'"; for(int i=0; i> 4]; - ss << "0123456789ABCDEF"[self[i] & 0xf]; + ss << "\\x"; + ss.write_hex((unsigned char)self[i]); } ss << "'"; return VAR(ss.str()); diff --git a/src/str.cpp b/src/str.cpp index 035f264d..9c979caf 100644 --- a/src/str.cpp +++ b/src/str.cpp @@ -488,4 +488,10 @@ int utf8len(unsigned char c, bool suppress){ std::reverse(begin, buffer.end()); return *this; } + + void SStream::write_hex(unsigned char c){ + *this << "0123456789ABCDEF"[c >> 4]; + *this << "0123456789ABCDEF"[c & 0xf]; + } + } // namespace pkpy \ No newline at end of file diff --git a/tests/80_c.py b/tests/80_c.py index 55d4d854..2143320d 100644 --- a/tests/80_c.py +++ b/tests/80_c.py @@ -62,3 +62,13 @@ assert p[4] == ord("o") assert p[5] == ord("!") assert p[6] == 0 assert p.read_string() == "Hello!" + +s = c.struct(67) +for i in range(67): + s.write_char(i, i) + +s_hex = s.hex() +s_r = c.struct.from_hex(s_hex) +assert (s == s_r and s is not s_r), (s_hex, s_r.hex()) +assert s_hex == s_r.hex() + diff --git a/tests/80_linalg.py b/tests/80_linalg.py index cf3caaf0..62317fb1 100644 --- a/tests/80_linalg.py +++ b/tests/80_linalg.py @@ -58,11 +58,6 @@ static_test_vec3_int = vec3(278, -13919730938747, 1364223456756456) assert str(static_test_vec3_float).startswith('vec3(') assert str(static_test_vec3_int).startswith('vec3(') -# test __getnewargs__ -element_name_list = ['x', 'y', 'z'] -element_value_list = [getattr(test_vec3, attr) for attr in element_name_list] -assert tuple(element_value_list) == test_vec3.__getnewargs__() - # test copy element_name_list = ['x', 'y', 'z'] element_value_list = [getattr(test_vec3, attr) for attr in element_name_list] @@ -81,13 +76,6 @@ static_test_vec4_int = vec4(278, -13919730938747, 1364223456756456, -37) assert str(static_test_vec4_float).startswith('vec4(') assert str(static_test_vec4_int).startswith('vec4(') -# test __getnewargs__ -element_name_list = ['x', 'y', 'z', 'w'] -element_value_list = [getattr(test_vec4, attr) for attr in element_name_list] -_0 = tuple(element_value_list) -_1 = test_vec4.__getnewargs__() -assert (_0 == _1), (_0, _1) - # test copy element_name_list = ['x', 'y', 'z', 'w'] element_value_list = [getattr(test_vec4, attr) for attr in element_name_list] @@ -341,11 +329,6 @@ test_mat_copy.determinant() assert str(static_test_mat_float) assert str(static_test_mat_int) -# test __getnewargs__ -test_mat_copy = test_mat.copy() -element_value_list = [getattr(test_mat, attr) for attr in element_name_list] -assert tuple(element_value_list) == test_mat.__getnewargs__() - # test __truediv__ test_mat_copy = test_mat.copy() result_mat = test_mat_copy.__truediv__(12.345)