From 3f56ab68ad2804992470d6c3027b4f1e0fbb78cb Mon Sep 17 00:00:00 2001 From: s-hadinger <49731213+s-hadinger@users.noreply.github.com> Date: Thu, 19 Sep 2024 21:35:52 +0200 Subject: [PATCH] Berry improve `int64` constructor (#22172) --- CHANGELOG.md | 1 + lib/libesp32/berry/src/be_byteslib.c | 2 +- lib/libesp32/berry/src/be_vm.h | 2 +- lib/libesp32/berry/tests/int64.be | 136 ++++++++++++++++++ lib/libesp32/berry_int64/src/be_int64_class.c | 135 ++++++++++------- lib/libesp32/berry_int64/tests/int64.be | 34 ++++- .../berry_mapping/src/be_class_wrapper.c | 12 +- 7 files changed, 262 insertions(+), 60 deletions(-) create mode 100644 lib/libesp32/berry/tests/int64.be diff --git a/CHANGELOG.md b/CHANGELOG.md index e20113aad..7406073d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to this project will be documented in this file. - Berry virtual Energy driver (#22134) - Support for RX8010 RTC as used in IOTTIMER (#21376) - ESP8266 experimental support for second I2C bus +- Berry improve `int64` constructor ### Breaking Changed diff --git a/lib/libesp32/berry/src/be_byteslib.c b/lib/libesp32/berry/src/be_byteslib.c index e2f2257e0..39a8ccbdb 100644 --- a/lib/libesp32/berry/src/be_byteslib.c +++ b/lib/libesp32/berry/src/be_byteslib.c @@ -547,7 +547,7 @@ void m_write_attributes(bvm *vm, int rel_idx, const buf_impl * attr) } // buf_impl * bytes_realloc(bvm *vm, buf_impl *oldbuf, int32_t size) -void bytes_realloc(bvm *vm, buf_impl * attr, int32_t size) +void bytes_realloc(bvm *vm, buf_impl * attr, size_t size) { m_assert_not_readlonly(vm, attr); if (!attr->fixed && size < 4) { size = 4; } diff --git a/lib/libesp32/berry/src/be_vm.h b/lib/libesp32/berry/src/be_vm.h index c88609bbd..5953a4625 100644 --- a/lib/libesp32/berry/src/be_vm.h +++ b/lib/libesp32/berry/src/be_vm.h @@ -110,7 +110,7 @@ struct bvm { struct bgc gc; bctypefunc ctypefunc; /* handler to ctype_func */ bbyte compopt; /* compilation options */ - int32_t bytesmaxsize; /* max allowed size for bytes() object, default 32kb but can be increased */ + size_t bytesmaxsize; /* max allowed size for bytes() object, default 32kb but can be increased */ bobshook obshook; bmicrosfnct microsfnct; /* fucntion to get time as a microsecond resolution */ #if BE_USE_PERF_COUNTERS diff --git a/lib/libesp32/berry/tests/int64.be b/lib/libesp32/berry/tests/int64.be new file mode 100644 index 000000000..695149445 --- /dev/null +++ b/lib/libesp32/berry/tests/int64.be @@ -0,0 +1,136 @@ +# tests for int64 support (for int32 internal int representation) + +assert(int(int64()) == 0) +assert(str(int64()) == "0") +assert(int(int64(10)) == 10) +assert(str(int64(10)) == "10") +assert(int(int64(-5)) == -5) +assert(str(int64(-5)) == "-5") + +# extended constructor +assert(int64(35).tostring() == "35") +assert(int64(3.5).tostring() == "3") +assert(int64("35").tostring() == "35") +assert(int64("20000000000").tostring() == "20000000000") +assert(int64(true).tostring() == "1") +assert(int64(false).tostring() == "0") +assert(int64(int64(42)).tostring() == "42") +# invalid +assert(int64("").tostring() == "0") +assert(int64(nil).tostring() == "0") + +# testing large numbers +assert(str(int64.fromu32(0xFFFFFFFF, 0x7FFFFFFF)) == "9223372036854775807") # max positive number +assert(str(int64.fromu32(0x00000000, 0x80000000)) == "-9223372036854775808") +assert(str(int64.fromu32(10,10)) == "42949672970") + +# addition +assert(str(int64(10) + int64(20)) == "30") +assert(str(int64(10) + int64(-20)) == "-10") +assert(str(int64() + int64()) == "0") +assert(str(int64(10) + 0) == "10") # special case, do not crash if null + +# substraction +assert(str(int64(10) - int64(20)) == "-10") +assert(str(int64(10) - int64(-20)) == "30") +assert(str(int64() - int64()) == "0") +assert(str(int64(10) - 0) == "10") # special case, do not crash if null + +# neg +assert(str(-int64(10)) == "-10") +assert(str(-int64(-10)) == "10") +assert(str(-int64()) == "0") + +# multiplication +assert(str(int64(10) * int64(20)) == "200") +assert(str(int64(10) * int64(-20)) == "-200") +assert(str(int64() * int64()) == "0") +assert(str(int64(10) * 0) == "0") # special case, do not crash if null + +# division +assert(str(int64(100) / int64(20)) == "5") +assert(str(int64(100) / int64(-20)) == "-5") + +# modulus +assert(str(int64(102) % int64(10)) == "2") + +# equals +assert((int64(10) == int64(10)) == true) +assert((int64(10) == int64(20)) == false) +assert((int64(10) == int64()) == false) +assert((int64(0) == int64()) == true) +assert((int64(0) == 0) == true) +assert((int64(10) == 0) == false) + +# unequals +assert((int64(10) != int64(10)) == false) +assert((int64(10) != int64(20)) == true) +assert((int64(10) != int64()) == true) +assert((int64(0) != int64()) == false) +assert((int64(0) != 0) == false) +assert((int64(10) != 0) == true) + +# > +assert((int64(10) > int64(10)) == false) +assert((int64(10) > int64(20)) == false) +assert((int64(20) > int64(10)) == true) +assert((int64(10) > 0) == true) +# >= +assert((int64(10) >= int64(10)) == true) +assert((int64(10) >= int64(20)) == false) +assert((int64(20) >= int64(10)) == true) +assert((int64(10) >= 0) == true) + +# < +assert((int64(10) < int64(10)) == false) +assert((int64(10) < int64(20)) == true) +assert((int64(20) < int64(10)) == false) +assert((int64(10) < 0) == false) +# <= +assert((int64(10) <= int64(10)) == true) +assert((int64(10) <= int64(20)) == true) +assert((int64(20) <= int64(10)) == false) +assert((int64(10) <= 0) == false) + +# tobytes +assert(int64().tobytes() == bytes("0000000000000000")) +assert(int64(10).tobytes() == bytes("0A00000000000000")) +a = int64.fromu32(0xFFFFFFFF, 0x7FFFFFFF) # max positive number +assert(a.tobytes() == bytes("FFFFFFFFFFFFFF7F")) +a = int64.fromu32(0x00000000, 0x80000000) +assert(a.tobytes() == bytes("0000000000000080")) +assert(int64(-1).tobytes() == bytes("FFFFFFFFFFFFFFFF")) + +# frombytes +assert(int64.frombytes(bytes("0A00000000000000"), 0) == bytes("0A00000000000000")) # with implicit index 0 +assert(int64.frombytes(bytes("0A00000000000000")) == bytes("0A00000000000000")) +assert(int64.frombytes(bytes("0A00000000000000"), 1) == bytes("0000000000000000")) # index 1 and incomplete (7 bytes) + +assert(int64.frombytes(bytes("00FFFFFFFFFFFFFFFF"), 1) == bytes("FFFFFFFFFFFFFFFF")) # index 1 and incomplete (7 bytes) +assert(int64.frombytes(bytes("00FFFFFFFFFFFFFFFF"), -2) == bytes("FFFF000000000000")) # from end +assert(int64.frombytes(bytes("")) == bytes("0000000000000000")) # empty +assert(int64.frombytes(bytes(""),4) == bytes("0000000000000000")) # empty with wrong index + +# fromu32 +assert(int64.fromu32(0).tobytes() == bytes("0000000000000000")) +assert(int64.fromu32(0xFFFFFFFF).tobytes() == bytes("FFFFFFFF00000000")) +assert(int64.fromu32(0xFFFFFFFF, 1).tobytes() == bytes("FFFFFFFF01000000")) +assert(int64.fromu32(-1, 1).tobytes() == bytes("FFFFFFFF01000000")) +assert(int64.fromu32(-1, -1).tobytes() == bytes("FFFFFFFFFFFFFFFF")) + +# fromfloat +assert(int64.fromfloat(3.5).tostring() == "3") +assert(int64.fromfloat(-3.5).tostring() == "-3") +assert(int64.fromfloat(2e10).tostring() == "20000000000") # 20000000000 does not fit in 32 bits + +# toint64, supports int, float, bool, string, int64 +assert(int64.toint64(35).tostring() == "35") +assert(int64.toint64(3.5).tostring() == "3") +assert(int64.toint64("35").tostring() == "35") +assert(int64.toint64("20000000000").tostring() == "20000000000") +assert(int64.toint64(true).tostring() == "1") +assert(int64.toint64(false).tostring() == "0") +assert(int64.toint64(int64(42)).tostring() == "42") +# invalid +assert(int64.toint64("").tostring() == "0") +assert(int64.toint64(nil) == nil) diff --git a/lib/libesp32/berry_int64/src/be_int64_class.c b/lib/libesp32/berry_int64/src/be_int64_class.c index 16f8dbbba..91a0a7f2f 100644 --- a/lib/libesp32/berry_int64/src/be_int64_class.c +++ b/lib/libesp32/berry_int64/src/be_int64_class.c @@ -32,23 +32,70 @@ static void int64_toa(int64_t num, uint8_t* str) { } } -void* int64_init(bvm *vm, int32_t val) { - int64_t *i64 = (int64_t*)be_malloc(vm, sizeof(int64_t)); - *i64 = (int64_t) val; - // serial_debug("int64_init p=%p\n", i64); - return i64; +/* constructor*/ +static int int64_init(bvm *vm) { + int32_t argc = be_top(vm); // Get the number of arguments + int64_t *i64 = NULL; + /* did we receive a pre-allocated pointer */ + if (argc > 1 && be_iscomptr(vm, 2)) { + i64 = be_tocomptr(vm, 2); + } + /* or allocated */ + if (i64 == NULL) { + i64 = (int64_t*)be_malloc(vm, sizeof(int64_t)); + if (i64 == NULL) { be_raise(vm, "memory_error", "cannot allocate buffer"); } + *i64 = 0; // default to zero + } + bbool invalid_arg = bfalse; + if (argc > 1) { + if (be_iscomptr(vm, 2) || be_isnil(vm, 2)) { + /* keep value */ + } else if (be_isint(vm, 2)) { + *i64 = be_toint(vm, 2); + } else if (be_isreal(vm, 2)) { + *i64 = (int64_t)be_toreal(vm, 2); + } else if (be_isstring(vm, 2)) { + const char* s = be_tostring(vm, 2); + *i64 = atoll(s); + } else if (be_isbool(vm, 2)) { + *i64 = be_tobool(vm, 2) ? 1 : 0; + } else if (be_isinstance(vm, 2)) { + be_getglobal(vm, "int64"); + if (be_isderived(vm, 2)) { + be_getmember(vm, 2, "_p"); + int64_t *v64 = be_tocomptr(vm, -1); + if (v64 != NULL) { + *i64 = *v64; + } + } else { + invalid_arg = btrue; + } + } else { + invalid_arg = btrue; + } + } + if (invalid_arg) { + be_free(vm, i64, sizeof(int64_t)); + be_raise(vm, "TypeError", "unsupported argument type"); + } + be_pushcomptr(vm, i64); + be_setmember(vm, 1, "_p"); + be_return_nil(vm); } -BE_FUNC_CTYPE_DECLARE(int64_init, "+_p", "@[i]") -void int64_deinit(bvm *vm, int64_t *i64) { - // serial_debug("int64_deinit p=%p\n", i64); - be_free(vm, i64, sizeof(int64_t)); +/* destructor */ +static int int64_deinit(bvm *vm) { + be_getmember(vm, 1, "_p"); + int64_t *i64 = be_tocomptr(vm, -1); + if (i64 != NULL) { + be_free(vm, i64, sizeof(int64_t)); + } + be_return_nil(vm); } -BE_FUNC_CTYPE_DECLARE(int64_deinit, "", "@.") char* int64_tostring(int64_t *i64) { static char s[24]; /* enough to hold max value */ - int64_toa(*i64, s); + int64_toa(*i64, (uint8_t*)s); return s; } BE_FUNC_CTYPE_DECLARE(int64_tostring, "s", ".") @@ -80,6 +127,13 @@ int64_t* int64_fromu32(bvm *vm, uint32_t low, uint32_t high) { } BE_FUNC_CTYPE_DECLARE(int64_fromu32, "int64", "@i[i]") +int64_t* int64_fromfloat(bvm *vm, float f) { + int64_t* r64 = (int64_t*)be_malloc(vm, sizeof(int64_t)); + *r64 = (int64_t)f; + return r64; +} +BE_FUNC_CTYPE_DECLARE(int64_fromfloat, "int64", "@f") + int64_t* int64_add(bvm *vm, int64_t *i64, int64_t *j64) { int64_t* r64 = (int64_t*)be_malloc(vm, sizeof(int64_t)); // it's possible that arg j64 is nullptr, since class type does allow NULLPTR to come through. @@ -203,7 +257,7 @@ int64_t* int64_frombytes(bvm *vm, uint8_t* ptr, size_t len, int32_t idx) { int64_t* r64 = (int64_t*)be_malloc(vm, sizeof(int64_t)); if (idx < 0) { idx = len + idx; } // support negative index, counting from the end if (idx < 0) { idx = 0; } // sanity check - if (idx > len) { idx = len; } + if (idx > (int32_t)len) { idx = len; } uint32_t usable_len = len - idx; if (usable_len > sizeof(int64_t)) { usable_len = sizeof(int64_t); } *r64 = 0; // start with 0 @@ -224,10 +278,9 @@ BE_FUNC_CTYPE_DECLARE(int64_high32, "i", "(int64)") /* -def toint64(i) - if (type(i) == 'int') return int64.fromu32(i) end - if (type(i) == 'instance') && isinstance(i, int64) return i end - return nil +def toint64(v) + if (v == nil) return nil end + return int64(v) end */ @@ -237,7 +290,7 @@ end ********************************************************************/ be_local_closure(toint64, /* name */ be_nested_proto( - 4, /* nstack */ + 3, /* nstack */ 1, /* argc */ 0, /* varg */ 0, /* has upvals */ @@ -245,38 +298,21 @@ be_local_closure(toint64, /* name */ 0, /* has sup protos */ NULL, /* no sub protos */ 1, /* has constants */ - ( &(const bvalue[ 4]) { /* constants */ - /* K0 */ be_nested_str(int), - /* K1 */ be_nested_str(int64), - /* K2 */ be_nested_str(fromu32), - /* K3 */ be_nested_str(instance), + ( &(const bvalue[ 1]) { /* constants */ + /* K0 */ be_nested_str(int64), }), - &be_const_str_to64, + &be_const_str_toint64, &be_const_str_solidified, - ( &(const binstruction[23]) { /* code */ - 0x60040004, // 0000 GETGBL R1 G4 - 0x5C080000, // 0001 MOVE R2 R0 - 0x7C040200, // 0002 CALL R1 1 - 0x1C040300, // 0003 EQ R1 R1 K0 - 0x78060004, // 0004 JMPF R1 #000A - 0xB8060200, // 0005 GETNGBL R1 K1 - 0x8C040302, // 0006 GETMET R1 R1 K2 - 0x5C0C0000, // 0007 MOVE R3 R0 - 0x7C040400, // 0008 CALL R1 2 - 0x80040200, // 0009 RET 1 R1 - 0x60040004, // 000A GETGBL R1 G4 - 0x5C080000, // 000B MOVE R2 R0 - 0x7C040200, // 000C CALL R1 1 - 0x1C040303, // 000D EQ R1 R1 K3 - 0x78060005, // 000E JMPF R1 #0015 - 0x6004000F, // 000F GETGBL R1 G15 - 0x5C080000, // 0010 MOVE R2 R0 - 0xB80E0200, // 0011 GETNGBL R3 K1 - 0x7C040400, // 0012 CALL R1 2 - 0x78060000, // 0013 JMPF R1 #0015 - 0x80040000, // 0014 RET 1 R0 - 0x4C040000, // 0015 LDNIL R1 - 0x80040200, // 0016 RET 1 R1 + ( &(const binstruction[ 9]) { /* code */ + 0x4C040000, // 0000 LDNIL R1 + 0x1C040001, // 0001 EQ R1 R0 R1 + 0x78060001, // 0002 JMPF R1 #0005 + 0x4C040000, // 0003 LDNIL R1 + 0x80040200, // 0004 RET 1 R1 + 0xB8060000, // 0005 GETNGBL R1 K0 + 0x5C080000, // 0006 MOVE R2 R0 + 0x7C040200, // 0007 CALL R1 1 + 0x80040200, // 0008 RET 1 R1 }) ) ); @@ -288,9 +324,10 @@ be_local_closure(toint64, /* name */ /* @const_object_info_begin class be_class_int64 (scope: global, name: int64) { _p, var - init, ctype_func(int64_init) - deinit, ctype_func(int64_deinit) + init, func(int64_init) + deinit, func(int64_deinit) fromu32, static_ctype_func(int64_fromu32) + fromfloat, static_ctype_func(int64_fromfloat) toint64, static_closure(toint64_closure) tostring, ctype_func(int64_tostring) diff --git a/lib/libesp32/berry_int64/tests/int64.be b/lib/libesp32/berry_int64/tests/int64.be index 311b0f72c..695149445 100644 --- a/lib/libesp32/berry_int64/tests/int64.be +++ b/lib/libesp32/berry_int64/tests/int64.be @@ -7,6 +7,18 @@ assert(str(int64(10)) == "10") assert(int(int64(-5)) == -5) assert(str(int64(-5)) == "-5") +# extended constructor +assert(int64(35).tostring() == "35") +assert(int64(3.5).tostring() == "3") +assert(int64("35").tostring() == "35") +assert(int64("20000000000").tostring() == "20000000000") +assert(int64(true).tostring() == "1") +assert(int64(false).tostring() == "0") +assert(int64(int64(42)).tostring() == "42") +# invalid +assert(int64("").tostring() == "0") +assert(int64(nil).tostring() == "0") + # testing large numbers assert(str(int64.fromu32(0xFFFFFFFF, 0x7FFFFFFF)) == "9223372036854775807") # max positive number assert(str(int64.fromu32(0x00000000, 0x80000000)) == "-9223372036854775808") @@ -83,10 +95,9 @@ assert((int64(10) <= 0) == false) # tobytes assert(int64().tobytes() == bytes("0000000000000000")) assert(int64(10).tobytes() == bytes("0A00000000000000")) -a = int64() -a.set(0x7FFFFFFF,0xFFFFFFFF) # max positive number +a = int64.fromu32(0xFFFFFFFF, 0x7FFFFFFF) # max positive number assert(a.tobytes() == bytes("FFFFFFFFFFFFFF7F")) -a.set(0x80000000,0x00000000) +a = int64.fromu32(0x00000000, 0x80000000) assert(a.tobytes() == bytes("0000000000000080")) assert(int64(-1).tobytes() == bytes("FFFFFFFFFFFFFFFF")) @@ -106,3 +117,20 @@ assert(int64.fromu32(0xFFFFFFFF).tobytes() == bytes("FFFFFFFF00000000")) assert(int64.fromu32(0xFFFFFFFF, 1).tobytes() == bytes("FFFFFFFF01000000")) assert(int64.fromu32(-1, 1).tobytes() == bytes("FFFFFFFF01000000")) assert(int64.fromu32(-1, -1).tobytes() == bytes("FFFFFFFFFFFFFFFF")) + +# fromfloat +assert(int64.fromfloat(3.5).tostring() == "3") +assert(int64.fromfloat(-3.5).tostring() == "-3") +assert(int64.fromfloat(2e10).tostring() == "20000000000") # 20000000000 does not fit in 32 bits + +# toint64, supports int, float, bool, string, int64 +assert(int64.toint64(35).tostring() == "35") +assert(int64.toint64(3.5).tostring() == "3") +assert(int64.toint64("35").tostring() == "35") +assert(int64.toint64("20000000000").tostring() == "20000000000") +assert(int64.toint64(true).tostring() == "1") +assert(int64.toint64(false).tostring() == "0") +assert(int64.toint64(int64(42)).tostring() == "42") +# invalid +assert(int64.toint64("").tostring() == "0") +assert(int64.toint64(nil) == nil) diff --git a/lib/libesp32/berry_mapping/src/be_class_wrapper.c b/lib/libesp32/berry_mapping/src/be_class_wrapper.c index 0e06ec1de..40d442ae4 100644 --- a/lib/libesp32/berry_mapping/src/be_class_wrapper.c +++ b/lib/libesp32/berry_mapping/src/be_class_wrapper.c @@ -34,15 +34,15 @@ typedef intptr_t (*fn_any_callable)(intptr_t p0, intptr_t p1, intptr_t p2, intpt * On ESP32, int=32bits, real=float (32bits) \*********************************************************************************************/ static intptr_t realasint(breal v) { - intptr_t i; - i = *((intptr_t*) &v); - return i; + union { breal f; bint i; } u; + u.f = v; + return (intptr_t)u.i; } static breal intasreal(intptr_t v) { - breal r; - r = *((breal*) &v); - return r; + union { breal f; bint i; } u; + u.i = (bint)v; + return (breal)u.f; } /*********************************************************************************************\