diff --git a/include/pocketpy/cffi.h b/include/pocketpy/cffi.h index 58531dd8..b8267baa 100644 --- a/include/pocketpy/cffi.h +++ b/include/pocketpy/cffi.h @@ -48,9 +48,9 @@ struct VoidP{ bool operator>=(const VoidP& other) const { return ptr >= other.ptr; } Str hex() const{ - std::stringstream ss; // hex - ss << std::hex << reinterpret_cast(ptr); - return "0x" + ss.str(); + SStream ss; + ss.write_hex(ptr); + return ss.str(); } static void _register(VM* vm, PyObject* mod, PyObject* type); diff --git a/include/pocketpy/common.h b/include/pocketpy/common.h index 8b370bc4..1a0ab2d1 100644 --- a/include/pocketpy/common.h +++ b/include/pocketpy/common.h @@ -3,13 +3,11 @@ #include #include -#include #include #include #include #include #include -#include #include #include #include diff --git a/include/pocketpy/str.h b/include/pocketpy/str.h index b6258e87..4cfc7e2f 100644 --- a/include/pocketpy/str.h +++ b/include/pocketpy/str.h @@ -127,37 +127,31 @@ struct SStream{ PK_ALWAYS_PASS_BY_POINTER(SStream) // pod_vector is allocated by pool64 so the buffer can be moved into Str without a copy pod_vector buffer; + int _precision = -1; + bool empty() const { return buffer.empty(); } + void setprecision(int precision) { _precision = precision; } SStream(){} SStream(int guess_size){ buffer.reserve(guess_size); } Str str(); - SStream& operator<<(const Str& s); - SStream& operator<<(const char* s); - SStream& operator<<(i64 val); - SStream& operator<<(const std::string& s); - SStream& operator<<(std::string_view s); - SStream& operator<<(char c); - SStream& operator<<(StrName sn); - void write_hex(unsigned char c); + SStream& operator<<(const Str&); + SStream& operator<<(const char*); + SStream& operator<<(int); + SStream& operator<<(unsigned int); + SStream& operator<<(unsigned long); + SStream& operator<<(i64); + SStream& operator<<(f64); + SStream& operator<<(const std::string&); + SStream& operator<<(std::string_view); + SStream& operator<<(char); + SStream& operator<<(StrName); - template - SStream& operator<<(T val){ - if constexpr(std::is_floating_point_v){ - if(std::isinf(val) || std::isnan(val)){ - return (*this) << std::to_string(val); - } - std::stringstream ss; // float - ss << std::setprecision(std::numeric_limits::max_digits10-1) << val; - std::string s = ss.str(); - if(std::all_of(s.begin()+1, s.end(), isdigit)) s += ".0"; - return (*this) << s; - } - (*this) << std::to_string(val); - return *this; - } + void write_hex(unsigned char); + void write_hex(void*); + void write_hex(i64); }; template diff --git a/src/cffi.cpp b/src/cffi.cpp index 44f26637..8eae5714 100644 --- a/src/cffi.cpp +++ b/src/cffi.cpp @@ -60,10 +60,12 @@ namespace pkpy{ 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 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 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] = c; } diff --git a/src/linalg.cpp b/src/linalg.cpp index 30e3b074..0852af2e 100644 --- a/src/linalg.cpp +++ b/src/linalg.cpp @@ -117,9 +117,9 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, PyVec2& currentVelocity, float }, {}, BindType::STATICMETHOD); vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ - PyVec2& self = _CAST(PyVec2&, obj); - std::stringstream ss; - ss << std::fixed << std::setprecision(3); + Vec2 self = _CAST(PyVec2&, obj); + SStream ss; + ss.setprecision(3); ss << "vec2(" << self.x << ", " << self.y << ")"; return VAR(ss.str()); }); @@ -165,9 +165,9 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, PyVec2& currentVelocity, float }); vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ - PyVec3& self = _CAST(PyVec3&, obj); - std::stringstream ss; - ss << std::fixed << std::setprecision(3); + Vec3 self = _CAST(PyVec3&, obj); + SStream ss; + ss.setprecision(3); ss << "vec3(" << self.x << ", " << self.y << ", " << self.z << ")"; return VAR(ss.str()); }); @@ -202,9 +202,9 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, PyVec2& currentVelocity, float }); vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ - PyVec4& self = _CAST(PyVec4&, obj); - std::stringstream ss; - ss << std::fixed << std::setprecision(3); + Vec4 self = _CAST(PyVec4&, obj); + SStream ss; + ss.setprecision(3); ss << "vec4(" << self.x << ", " << self.y << ", " << self.z << ", " << self.w << ")"; return VAR(ss.str()); }); @@ -261,9 +261,9 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, PyVec2& currentVelocity, float }); vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ - PyMat3x3& self = _CAST(PyMat3x3&, obj); - std::stringstream ss; - ss << std::fixed << std::setprecision(3); + const PyMat3x3& self = _CAST(PyMat3x3&, obj); + SStream ss; + ss.setprecision(3); ss << "mat3x3([" << self._11 << ", " << self._12 << ", " << self._13 << ",\n"; ss << " " << self._21 << ", " << self._22 << ", " << self._23 << ",\n"; ss << " " << self._31 << ", " << self._32 << ", " << self._33 << "])"; diff --git a/src/pocketpy.cpp b/src/pocketpy.cpp index 2b2a413b..135d4b32 100644 --- a/src/pocketpy.cpp +++ b/src/pocketpy.cpp @@ -254,9 +254,9 @@ void init_builtins(VM* _vm) { }); _vm->bind_func<1>(_vm->builtins, "hex", [](VM* vm, ArgsView args) { - std::stringstream ss; // hex - ss << std::hex << CAST(i64, args[0]); - return VAR("0x" + ss.str()); + SStream ss; + ss.write_hex(CAST(i64, args[0])); + return VAR(ss.str()); }); _vm->bind_func<1>(_vm->builtins, "iter", [](VM* vm, ArgsView args) { @@ -300,9 +300,10 @@ void init_builtins(VM* _vm) { // tp_object _vm->bind__repr__(VM::tp_object, [](VM* vm, PyObject* obj) { if(is_tagged(obj)) PK_FATAL_ERROR(); - std::stringstream ss; // hex - ss << "<" << _type_name(vm, vm->_tp(obj)) << " object at 0x"; - ss << std::hex << reinterpret_cast(obj) << ">"; + SStream ss; + ss << "<" << _type_name(vm, vm->_tp(obj)) << " object at "; + ss.write_hex(obj); + ss << ">"; return VAR(ss.str()); }); diff --git a/src/str.cpp b/src/str.cpp index 9c979caf..6b657016 100644 --- a/src/str.cpp +++ b/src/str.cpp @@ -469,6 +469,19 @@ int utf8len(unsigned char c, bool suppress){ return *this << sn.sv(); } + SStream& SStream::operator<<(unsigned int val){ + return (*this) << static_cast(val); + } + + SStream& SStream::operator<<(unsigned long val){ + // unsigned long could be out of range of `i64`, use `std::to_string` instead + return (*this) << std::to_string(val); + } + + SStream& SStream::operator<<(int val){ + return (*this) << static_cast(val); + } + SStream& SStream::operator<<(i64 val){ // str(-2**64).__len__() == 21 buffer.reserve(buffer.size() + 24); @@ -489,9 +502,53 @@ int utf8len(unsigned char c, bool suppress){ return *this; } + SStream& SStream::operator<<(f64 val){ + if(std::isinf(val) || std::isnan(val)){ + return (*this) << std::to_string(val); + } + char b[32]; + if(_precision == -1){ + int prec = std::numeric_limits::max_digits10-1; + sprintf(b, "%.*g", prec, val); + }else{ + int prec = _precision; + sprintf(b, "%.*f", prec, val); + } + (*this) << b; + if(std::all_of(b+1, b+strlen(b), isdigit)){ + (*this) << ".0"; + } + return *this; + } + void SStream::write_hex(unsigned char c){ *this << "0123456789ABCDEF"[c >> 4]; *this << "0123456789ABCDEF"[c & 0xf]; } + void SStream::write_hex(void* p){ + (*this) << "0x"; + uintptr_t p_t = reinterpret_cast(p); + for(int i=sizeof(void*)-1; i>=0; i--){ + unsigned char cpnt = (p_t >> (i * 8)) & 0xff; + write_hex(cpnt); + } + } + + void SStream::write_hex(i64 val){ + if(val < 0){ + (*this) << "-"; + val = -val; + } + (*this) << "0x"; + if(val == 0){ + (*this) << "0"; + return; + } + for(int i=56; i>=0; i-=8){ + unsigned char cpnt = (val >> i) & 0xff; + if(cpnt != 0) write_hex(cpnt); + } + } + } // namespace pkpy \ No newline at end of file diff --git a/src/vm.cpp b/src/vm.cpp index 73f5f230..547b5692 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -1,5 +1,4 @@ #include "pocketpy/vm.h" -#include "pocketpy/config.h" namespace pkpy{ @@ -507,8 +506,9 @@ PyObject* VM::_format_string(Str spec, PyObject* obj){ if(type == 'f'){ f64 val = CAST(f64, obj); if(precision < 0) precision = 6; - std::stringstream ss; // float - ss << std::fixed << std::setprecision(precision) << val; + SStream ss; + ss.setprecision(precision); + ss << val; ret = ss.str(); }else if(type == 'd'){ ret = std::to_string(CAST(i64, obj));