This commit is contained in:
blueloveTH 2024-01-15 14:40:24 +08:00
parent fb3ffaa020
commit d8d25894c7
11 changed files with 101 additions and 77 deletions

View File

@ -138,13 +138,14 @@ void _bind(VM* vm, PyObject* obj, const char* sig, Ret(T::*func)(Params...)){
#define PY_STRUCT_LIKE(wT) \ #define PY_STRUCT_LIKE(wT) \
using vT = std::remove_pointer_t<decltype(std::declval<wT>()._())>; \ using vT = std::remove_pointer_t<decltype(std::declval<wT>()._())>; \
static_assert(std::is_trivially_copyable<vT>::value); \ 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){ \ vm->bind_func<1>(type, "from_struct", [](VM* vm, ArgsView args){ \
C99Struct& s = CAST(C99Struct&, args[0]); \ C99Struct& s = CAST(C99Struct&, args[0]); \
if(s.size != sizeof(vT)) vm->ValueError("size mismatch"); \ if(s.size != sizeof(vT)) vm->ValueError("size mismatch"); \
PyObject* obj = vm->heap.gcnew<wT>(wT::_type(vm)); \ PyObject* obj = vm->heap.gcnew<wT>(wT::_type(vm)); \
memcpy(_CAST(wT&, obj)._(), s.p, sizeof(vT)); \ memcpy(_CAST(wT&, obj)._(), s.p, sizeof(vT)); \
return obj; \ return obj; \
}); \ }, {}, BindType::STATICMETHOD); \
vm->bind_method<0>(type, "to_struct", [](VM* vm, ArgsView args){ \ vm->bind_method<0>(type, "to_struct", [](VM* vm, ArgsView args){ \
wT& self = _CAST(wT&, args[0]); \ wT& self = _CAST(wT&, args[0]); \
return VAR_T(C99Struct, self._(), sizeof(vT)); \ return VAR_T(C99Struct, self._(), sizeof(vT)); \

View File

@ -141,6 +141,7 @@ struct SStream{
SStream& operator<<(std::string_view s); SStream& operator<<(std::string_view s);
SStream& operator<<(char c); SStream& operator<<(char c);
SStream& operator<<(StrName sn); SStream& operator<<(StrName sn);
void write_hex(unsigned char c);
template<typename T> template<typename T>
SStream& operator<<(T val){ SStream& operator<<(T val){

View File

@ -449,7 +449,7 @@ public:
template<int ARGC> template<int ARGC>
PyObject* bind_method(PyObject*, Str, NativeFuncC); PyObject* bind_method(PyObject*, Str, NativeFuncC);
template<int ARGC> template<int ARGC>
PyObject* bind_func(PyObject*, Str, NativeFuncC); PyObject* bind_func(PyObject*, Str, NativeFuncC, UserData userdata={}, BindType bt=BindType::DEFAULT);
void _error(PyObject*); void _error(PyObject*);
PyObject* _run_top_frame(); PyObject* _run_top_frame();
void post_init(); void post_init();
@ -624,8 +624,14 @@ PyObject* VM::bind_method(PyObject* obj, Str name, NativeFuncC fn) {
} }
template<int ARGC> 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)); 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); obj->attr().set(name, nf);
return nf; return nf;
} }

View File

@ -57,6 +57,10 @@ class struct:
def __eq__(self, other: 'struct') -> bool: ... def __eq__(self, other: 'struct') -> bool: ...
def __ne__(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_char(self, offset=0) -> int: ...
def read_uchar(self, offset=0) -> int: ... def read_uchar(self, offset=0) -> int: ...
def read_short(self, offset=0) -> int: ... def read_short(self, offset=0) -> int: ...

View File

@ -1,4 +1,5 @@
import json import json
from c import struct
import builtins import builtins
_BASIC_TYPES = [int, float, str, bool, type(None)] _BASIC_TYPES = [int, float, str, bool, type(None)]
@ -16,18 +17,20 @@ class _Pickler:
self.raw_memo = {} # id -> int self.raw_memo = {} # id -> int
self.memo = [] # int -> object self.memo = [] # int -> object
def _type_id(self, o: type): @staticmethod
assert type(o) is type def _type_id(t: type):
name = o.__name__ assert type(t) is type
mod = o.__module__ name = t.__name__
mod = t.__module__
if mod is not None: if mod is not None:
name = mod.__path__ + _MOD_T_SEP + name name = mod.__path__ + _MOD_T_SEP + name
return name return name
def wrap(self, o): def wrap(self, o):
if type(o) in _BASIC_TYPES: o_t = type(o)
if o_t in _BASIC_TYPES:
return o return o
if type(o) is type: if o_t is type:
return ["type", self._type_id(o)] return ["type", self._type_id(o)]
index = self.raw_memo.get(id(o), None) index = self.raw_memo.get(id(o), None)
@ -39,25 +42,29 @@ class _Pickler:
self.memo.append(ret) self.memo.append(ret)
self.raw_memo[id(o)] = index self.raw_memo[id(o)] = index
if type(o) is tuple: if o_t is tuple:
ret.append("tuple") ret.append("tuple")
ret.append([self.wrap(i) for i in o]) ret.append([self.wrap(i) for i in o])
return [index] return [index]
if type(o) is bytes: if o_t is bytes:
ret.append("bytes") ret.append("bytes")
ret.append([o[j] for j in range(len(o))]) ret.append([o[j] for j in range(len(o))])
return [index] return [index]
if o_t is list:
if type(o) is list:
ret.append("list") ret.append("list")
ret.append([self.wrap(i) for i in o]) ret.append([self.wrap(i) for i in o])
return [index] return [index]
if type(o) is dict: if o_t is dict:
ret.append("dict") ret.append("dict")
ret.append([[self.wrap(k), self.wrap(v)] for k,v in o.items()]) ret.append([[self.wrap(k), self.wrap(v)] for k,v in o.items()])
return [index] 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__"): if hasattr(o, "__getnewargs__"):
_1 = o.__getnewargs__() # an iterable _1 = o.__getnewargs__() # an iterable
@ -68,13 +75,11 @@ class _Pickler:
if o.__dict__ is None: if o.__dict__ is None:
_2 = None _2 = None
else: else:
_2 = {} _2 = {k: self.wrap(v) for k,v in o.__dict__.items()}
for k,v in o.__dict__.items():
_2[k] = self.wrap(v)
ret.append(_0) ret.append(_0) # type id
ret.append(_1) ret.append(_1) # newargs
ret.append(_2) ret.append(_2) # state
return [index] return [index]
def run_pipe(self): def run_pipe(self):
@ -122,7 +127,6 @@ class _Unpickler:
ret = bytes(o[1]) ret = bytes(o[1])
self.tag(index, ret) self.tag(index, ret)
return ret return ret
if o[0] == "list": if o[0] == "list":
ret = [] ret = []
self.tag(index, ret) self.tag(index, ret)
@ -137,21 +141,26 @@ class _Unpickler:
return ret return ret
# generic object # generic object
cls, newargs, state = o
cls = _find_class(o[0]) cls = _find_class(o[0])
# create uninitialized instance if getattr(cls, '__struct__', False):
new_f = getattr(cls, "__new__") inst = cls.from_struct(struct.from_hex(o[1]))
if newargs is not None: self.tag(index, inst)
newargs = [self.unwrap(i) for i in newargs] return inst
inst = new_f(cls, *newargs)
else: else:
inst = new_f(cls) _, newargs, state = o
self.tag(index, inst) # create uninitialized instance
# restore state new_f = getattr(cls, "__new__")
if state is not None: if newargs is not None:
for k,v in state.items(): newargs = [self.unwrap(i) for i in newargs]
setattr(inst, k, self.unwrap(v)) inst = new_f(cls, *newargs)
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
def run_pipe(self): def run_pipe(self):
return self.unwrap(self.obj) return self.unwrap(self.obj)

View File

@ -44,6 +44,33 @@ namespace pkpy{
return vm->heap.gcnew<C99Struct>(cls, size); 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){ vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
C99Struct& self = _CAST(C99Struct&, obj); C99Struct& self = _CAST(C99Struct&, obj);
SStream ss; SStream ss;

View File

@ -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)); 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 // @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){ 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]); 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)); 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){ vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
PyVec3& self = _CAST(PyVec3&, obj); PyVec3& self = _CAST(PyVec3&, obj);
std::stringstream ss; 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)); 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){ vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
PyVec4& self = _CAST(PyVec4&, obj); PyVec4& self = _CAST(PyVec4&, obj);
std::stringstream ss; std::stringstream ss;
@ -275,13 +260,6 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, PyVec2& currentVelocity, float
return vm->None; 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){ vm->bind_method<1>(type, "assign", [](VM* vm, ArgsView args){
PyMat3x3& self = _CAST(PyMat3x3&, args[0]); PyMat3x3& self = _CAST(PyMat3x3&, args[0]);
const PyMat3x3& other = CAST(PyMat3x3&, args[1]); const PyMat3x3& other = CAST(PyMat3x3&, args[1]);

View File

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

View File

@ -488,4 +488,10 @@ int utf8len(unsigned char c, bool suppress){
std::reverse(begin, buffer.end()); std::reverse(begin, buffer.end());
return *this; return *this;
} }
void SStream::write_hex(unsigned char c){
*this << "0123456789ABCDEF"[c >> 4];
*this << "0123456789ABCDEF"[c & 0xf];
}
} // namespace pkpy } // namespace pkpy

View File

@ -62,3 +62,13 @@ assert p[4] == ord("o")
assert p[5] == ord("!") assert p[5] == ord("!")
assert p[6] == 0 assert p[6] == 0
assert p.read_string() == "Hello!" 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()

View File

@ -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_float).startswith('vec3(')
assert str(static_test_vec3_int).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 # test copy
element_name_list = ['x', 'y', 'z'] element_name_list = ['x', 'y', 'z']
element_value_list = [getattr(test_vec3, attr) for attr in element_name_list] 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_float).startswith('vec4(')
assert str(static_test_vec4_int).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 # test copy
element_name_list = ['x', 'y', 'z', 'w'] element_name_list = ['x', 'y', 'z', 'w']
element_value_list = [getattr(test_vec4, attr) for attr in element_name_list] 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_float)
assert str(static_test_mat_int) 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 __truediv__
test_mat_copy = test_mat.copy() test_mat_copy = test_mat.copy()
result_mat = test_mat_copy.__truediv__(12.345) result_mat = test_mat_copy.__truediv__(12.345)