blueloveTH пре 2 година
родитељ
комит
d8d25894c7
11 измењених фајлова са 101 додато и 77 уклоњено
  1. 2 1
      include/pocketpy/bindings.h
  2. 1 0
      include/pocketpy/str.h
  3. 8 2
      include/pocketpy/vm.h
  4. 4 0
      include/typings/c.pyi
  5. 41 32
      python/pickle.py
  6. 27 0
      src/cffi.cpp
  7. 0 22
      src/linalg.cpp
  8. 2 3
      src/pocketpy.cpp
  9. 6 0
      src/str.cpp
  10. 10 0
      tests/80_c.py
  11. 0 17
      tests/80_linalg.py

+ 2 - 1
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<decltype(std::declval<wT>()._())>;         \
         static_assert(std::is_trivially_copyable<vT>::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>(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));                          \

+ 1 - 0
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<typename T>
     SStream& operator<<(T val){

+ 8 - 2
include/pocketpy/vm.h

@@ -449,7 +449,7 @@ public:
     template<int ARGC>
     PyObject* bind_method(PyObject*, Str, NativeFuncC);
     template<int ARGC>
-    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<int ARGC>
-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;
 }

+ 4 - 0
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: ...

+ 41 - 32
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)

+ 27 - 0
src/cffi.cpp

@@ -44,6 +44,33 @@ namespace pkpy{
             return vm->heap.gcnew<C99Struct>(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; i<self.size; i++) ss.write_hex((unsigned char)self.p[i]);
+            return VAR(ss.str());
+        });
+
+        // @staticmethod
+        vm->bind_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<s.size; i+=2){
+                char c = 0;
+                if(s[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;

+ 0 - 22
src/linalg.cpp

@@ -105,11 +105,6 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, PyVec2& currentVelocity, float
             return vm->heap.gcnew<PyVec2>(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<PyVec3>(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<PyVec4>(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]);

+ 2 - 3
src/pocketpy.cpp

@@ -988,9 +988,8 @@ void init_builtins(VM* _vm) {
         SStream ss;
         ss << "b'";
         for(int i=0; i<self.size(); i++){
-            ss << "\\x"; // << std::hex << std::setw(2) << std::setfill('0') << self[i];
-            ss << "0123456789ABCDEF"[self[i] >> 4];
-            ss << "0123456789ABCDEF"[self[i] & 0xf];
+            ss << "\\x";
+            ss.write_hex((unsigned char)self[i]);
         }
         ss << "'";
         return VAR(ss.str());

+ 6 - 0
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

+ 10 - 0
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()
+

+ 0 - 17
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)