mirror of
https://github.com/arendst/Tasmota.git
synced 2026-10-26 22:11:22 +00:00
947 lines
32 KiB
Python
947 lines
32 KiB
Python
"""
|
|
berry_port/be_int64lib.py — Port of default/be_int64_class.c
|
|
|
|
int64 class: 64-bit integer support for Berry's 32-bit architecture.
|
|
Uses Python's native arbitrary-precision integers masked to 64-bit signed range.
|
|
|
|
Original C code is included as comments.
|
|
"""
|
|
|
|
# /********************************************************************
|
|
# ** int64 - support 64 bits int on 32 bits architecture
|
|
# **
|
|
# ** Pure native Berry C API implementation - no dependency on berry_mapping.
|
|
# ** All functions use be_top/be_to* /be_push* stack operations directly.
|
|
# ********************************************************************/
|
|
|
|
# ============================================================================
|
|
# Imports — lazy to avoid circular dependencies
|
|
# ============================================================================
|
|
|
|
def _lazy_be_api():
|
|
import berry_port.be_api as m; return m
|
|
|
|
def _lazy_be_object():
|
|
import berry_port.be_object as m; return m
|
|
|
|
# ============================================================================
|
|
# Constants
|
|
# ============================================================================
|
|
|
|
INT64_MIN = -(2**63)
|
|
INT64_MAX = 2**63 - 1
|
|
INT32_MIN = -(2**31)
|
|
INT32_MAX = 2**31 - 1
|
|
|
|
# ============================================================================
|
|
# Helpers
|
|
# ============================================================================
|
|
|
|
def _mask64(val):
|
|
"""Mask a Python int to signed 64-bit range."""
|
|
val = val & 0xFFFFFFFFFFFFFFFF
|
|
if val >= 0x8000000000000000:
|
|
val -= 0x10000000000000000
|
|
return val
|
|
|
|
# /* Secure string to int64 conversion with validation */
|
|
# static int secure_str_to_int64(bvm *vm, const char* s, int64_t* result, void* allocated_ptr) {
|
|
# if (!s || *s == '\0') { *result = 0; return 0; }
|
|
# char* endptr;
|
|
# errno = 0;
|
|
# long long temp = strtoll(s, &endptr, 10);
|
|
# if (errno == ERANGE) {
|
|
# if (allocated_ptr) be_free(vm, allocated_ptr, sizeof(int64_t));
|
|
# be_raise(vm, "value_error", "integer string out of range");
|
|
# return -1;
|
|
# }
|
|
# while (*endptr == ' ' || *endptr == '\t' || *endptr == '\n' || *endptr == '\r') { endptr++; }
|
|
# if (*endptr != '\0') {
|
|
# if (allocated_ptr) be_free(vm, allocated_ptr, sizeof(int64_t));
|
|
# be_raise(vm, "value_error", "invalid integer string format");
|
|
# return -1;
|
|
# }
|
|
# *result = temp;
|
|
# return 0;
|
|
# }
|
|
def _secure_str_to_int64(vm, s):
|
|
"""Secure string to int64 conversion with validation.
|
|
|
|
Returns a Python int in [INT64_MIN, INT64_MAX].
|
|
Raises Berry 'value_error' on invalid format or out-of-range.
|
|
|
|
Mirrors C strtoll behavior:
|
|
- empty or whitespace-only string -> 0 (no error)
|
|
- leading/trailing whitespace trimmed
|
|
- trailing non-whitespace -> value_error
|
|
- out of 64-bit range -> value_error
|
|
"""
|
|
be_api = _lazy_be_api()
|
|
if s is None:
|
|
return 0
|
|
s = s.strip()
|
|
if s == "":
|
|
return 0
|
|
try:
|
|
result = int(s, 10)
|
|
except ValueError:
|
|
be_api.be_raise(vm, "value_error", "invalid integer string format")
|
|
return 0 # unreachable after raise
|
|
if result > INT64_MAX or result < INT64_MIN:
|
|
be_api.be_raise(vm, "value_error", "integer string out of range")
|
|
return 0 # unreachable after raise
|
|
return result
|
|
|
|
# ============================================================================
|
|
# Stack access helpers (ported from C self_get_p / arg_get_p / push_int64_instance)
|
|
# ============================================================================
|
|
|
|
# static int64_t* self_get_p(bvm *vm) {
|
|
# be_getmember(vm, 1, "_p");
|
|
# int64_t *p = (int64_t*)be_tocomptr(vm, -1);
|
|
# be_pop(vm, 1);
|
|
# return p;
|
|
# }
|
|
def self_get_p(vm):
|
|
"""Get the int64 value from self (stack position 1).
|
|
|
|
Returns a Python int in [INT64_MIN, INT64_MAX].
|
|
"""
|
|
be_api = _lazy_be_api()
|
|
be_api.be_getmember(vm, 1, "_p")
|
|
p = be_api.be_tocomptr(vm, -1)
|
|
be_api.be_pop(vm, 1)
|
|
return p # Python int or None
|
|
|
|
# /* Get the _p pointer from an int64 instance at stack position idx */
|
|
# static int64_t* arg_get_p(bvm *vm, int idx) {
|
|
# if (be_isnil(vm, idx)) { return NULL; }
|
|
# if (!be_isinstance(vm, idx)) { return NULL; }
|
|
# be_getmember(vm, idx, "_p");
|
|
# int64_t *p = (int64_t*)be_tocomptr(vm, -1);
|
|
# be_pop(vm, 1);
|
|
# return p;
|
|
# }
|
|
def arg_get_p(vm, idx):
|
|
"""Get the int64 value from an int64 instance at stack position idx.
|
|
|
|
Returns Python int if idx holds a valid int64 instance, else None.
|
|
"""
|
|
be_api = _lazy_be_api()
|
|
if be_api.be_isnil(vm, idx):
|
|
return None
|
|
if not be_api.be_isinstance(vm, idx):
|
|
return None
|
|
be_api.be_getmember(vm, idx, "_p")
|
|
p = be_api.be_tocomptr(vm, -1)
|
|
be_api.be_pop(vm, 1)
|
|
return p
|
|
|
|
# /*
|
|
# * Push a new int64 instance onto the stack with the given value.
|
|
# * Stack on return: new int64 instance at top.
|
|
# */
|
|
# static void push_int64_instance(bvm *vm, int64_t val) {
|
|
# be_getglobal(vm, "int64"); /* stack: class */
|
|
# be_call(vm, 0); /* stack: instance */
|
|
# int64_t *r64 = arg_get_p(vm, -1); /* reuse constructor-owned payload */
|
|
# if (r64 == NULL) { be_raise(vm, "memory_error", "cannot allocate buffer"); }
|
|
# *r64 = val;
|
|
# }
|
|
# Python note: values are immutable Python ints stored directly as comptr, so
|
|
# there is no payload to leak and nothing to fill in place; re-running init
|
|
# with the value as comptr is the equivalent of writing *r64.
|
|
def push_int64_instance(vm, val):
|
|
"""Push a new int64 instance with the given value onto the stack.
|
|
|
|
The value is masked to signed 64-bit range before storing.
|
|
No malloc needed in Python — the int is stored directly as comptr.
|
|
"""
|
|
be_api = _lazy_be_api()
|
|
val = _mask64(val)
|
|
be_api.be_getglobal(vm, "int64") # stack: class
|
|
be_api.be_call(vm, 0) # stack: instance
|
|
be_api.be_getmember(vm, -1, "init") # stack: instance, init
|
|
be_api.be_pushvalue(vm, -2) # stack: instance, init, instance
|
|
be_api.be_pushcomptr(vm, val) # stack: instance, init, instance, val
|
|
be_api.be_call(vm, 2) # stack: instance, ret
|
|
be_api.be_pop(vm, 1) # stack: instance
|
|
|
|
# ============================================================================
|
|
# Constructor / Destructor
|
|
# ============================================================================
|
|
|
|
# static int int64_init(bvm *vm) {
|
|
# int argc = be_top(vm);
|
|
# int64_t *i64 = NULL;
|
|
# if (argc > 1 && be_iscomptr(vm, 2)) {
|
|
# i64 = (int64_t*)be_tocomptr(vm, 2);
|
|
# }
|
|
# if (i64 == NULL) {
|
|
# i64 = (int64_t*)be_malloc(vm, sizeof(int64_t));
|
|
# *i64 = 0;
|
|
# }
|
|
# 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);
|
|
# if (secure_str_to_int64(vm, s, i64, i64) != 0) { return 0; }
|
|
# } 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 = (int64_t*)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);
|
|
# }
|
|
def int64_init(vm):
|
|
"""init(self [, value]) — Initialize int64 from int, real, string,
|
|
comptr, int64 instance, bool, or nil."""
|
|
be_api = _lazy_be_api()
|
|
argc = be_api.be_top(vm)
|
|
i64 = 0 # default value
|
|
|
|
# If a comptr is passed directly, use it (pre-set value from push_int64_instance)
|
|
if argc > 1 and be_api.be_iscomptr(vm, 2):
|
|
i64 = be_api.be_tocomptr(vm, 2)
|
|
elif argc > 1:
|
|
if be_api.be_isnil(vm, 2):
|
|
pass # keep default 0
|
|
elif be_api.be_isint(vm, 2):
|
|
i64 = _mask64(be_api.be_toint(vm, 2))
|
|
elif be_api.be_isreal(vm, 2):
|
|
i64 = _mask64(int(be_api.be_toreal(vm, 2)))
|
|
elif be_api.be_isstring(vm, 2):
|
|
i64 = _secure_str_to_int64(vm, be_api.be_tostring(vm, 2))
|
|
elif be_api.be_isbool(vm, 2):
|
|
i64 = 1 if be_api.be_tobool(vm, 2) else 0
|
|
elif be_api.be_isinstance(vm, 2):
|
|
be_api.be_getglobal(vm, "int64")
|
|
if be_api.be_isderived(vm, 2):
|
|
v64 = arg_get_p(vm, 2)
|
|
if v64 is not None:
|
|
i64 = v64
|
|
else:
|
|
be_api.be_raise(vm, "TypeError", "unsupported argument type")
|
|
else:
|
|
be_api.be_raise(vm, "TypeError", "unsupported argument type")
|
|
|
|
be_api.be_pushcomptr(vm, i64)
|
|
be_api.be_setmember(vm, 1, "_p")
|
|
return be_api.be_returnnilvalue(vm)
|
|
|
|
# static int int64_deinit(bvm *vm) {
|
|
# /* In C: frees the allocated int64_t */
|
|
# /* In Python: no-op (GC handles cleanup) */
|
|
# be_return_nil(vm);
|
|
# }
|
|
def int64_deinit(vm):
|
|
"""deinit(self) — No-op in Python (GC handles cleanup)."""
|
|
be_api = _lazy_be_api()
|
|
return be_api.be_returnnilvalue(vm)
|
|
|
|
# ============================================================================
|
|
# Conversion methods
|
|
# ============================================================================
|
|
|
|
# static int int64_tostring(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# static char s[24];
|
|
# int64_toa(*i64, (uint8_t*)s);
|
|
# be_pushstring(vm, s);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_tostring(vm):
|
|
"""tostring(self) -> string — Decimal string representation."""
|
|
be_api = _lazy_be_api()
|
|
val = self_get_p(vm)
|
|
be_api.be_pushstring(vm, str(val))
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# /* static fromstring(s) -> int64 */
|
|
# static int int64_fromstring(bvm *vm) {
|
|
# const char *s = be_tostring(vm, 1);
|
|
# int64_t val = 0;
|
|
# if (secure_str_to_int64(vm, s, &val, NULL) != 0) { return 0; }
|
|
# push_int64_instance(vm, val);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_fromstring(vm):
|
|
"""static fromstring(s) -> int64 — Parse decimal string to int64.
|
|
|
|
STATIC method: reads arg at stack[1] (no self).
|
|
"""
|
|
be_api = _lazy_be_api()
|
|
s = be_api.be_tostring(vm, 1)
|
|
val = _secure_str_to_int64(vm, s)
|
|
push_int64_instance(vm, val)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# /* isint(self) -> bool : is the value within int32 range? */
|
|
# static int int64_isint(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# be_pushbool(vm, *i64 >= INT32_MIN && *i64 <= INT32_MAX);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_isint(vm):
|
|
"""isint(self) -> bool — True if value fits in int32 range."""
|
|
be_api = _lazy_be_api()
|
|
val = self_get_p(vm)
|
|
be_api.be_pushbool(vm, INT32_MIN <= val <= INT32_MAX)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# /* toint(self) -> int : truncate to 32-bit int */
|
|
# static int int64_toint(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# be_pushint(vm, (int32_t)*i64);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_toint(vm):
|
|
"""toint(self) -> int — Truncate to 32-bit signed int."""
|
|
be_api = _lazy_be_api()
|
|
val = self_get_p(vm)
|
|
# Equivalent of C (int32_t) cast: mask to 32 bits, then sign-extend
|
|
val32 = ((val & 0xFFFFFFFF) ^ 0x80000000) - 0x80000000
|
|
be_api.be_pushint(vm, val32)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# /* tobool(self) -> bool */
|
|
# static int int64_tobool(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# be_pushbool(vm, *i64 != 0);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_tobool(vm):
|
|
"""tobool(self) -> bool — True if value != 0."""
|
|
be_api = _lazy_be_api()
|
|
val = self_get_p(vm)
|
|
be_api.be_pushbool(vm, val != 0)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# ============================================================================
|
|
# Static factory methods
|
|
# ============================================================================
|
|
|
|
# /* static fromu32(low [, high]) -> int64 */
|
|
# static int int64_fromu32(bvm *vm) {
|
|
# int argc = be_top(vm);
|
|
# uint32_t low = (uint32_t)be_toint(vm, 1);
|
|
# uint32_t high = (argc > 1) ? (uint32_t)be_toint(vm, 2) : 0;
|
|
# int64_t val = (int64_t)low | (((int64_t)high) << 32);
|
|
# push_int64_instance(vm, val);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_fromu32(vm):
|
|
"""static fromu32(low [, high]) -> int64 — Construct from unsigned 32-bit halves.
|
|
|
|
STATIC method: reads args at stack[1], stack[2] (no self).
|
|
low and high are treated as unsigned 32-bit values.
|
|
"""
|
|
be_api = _lazy_be_api()
|
|
argc = be_api.be_top(vm)
|
|
low = be_api.be_toint(vm, 1) & 0xFFFFFFFF
|
|
high = (be_api.be_toint(vm, 2) & 0xFFFFFFFF) if argc > 1 else 0
|
|
val = low | (high << 32)
|
|
push_int64_instance(vm, val)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# /* static fromfloat(f) -> int64 */
|
|
# static int int64_fromfloat(bvm *vm) {
|
|
# breal f = be_toreal(vm, 1);
|
|
# push_int64_instance(vm, (int64_t)f);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_fromfloat(vm):
|
|
"""static fromfloat(f) -> int64 — Construct from float.
|
|
|
|
STATIC method: reads arg at stack[1] (no self).
|
|
"""
|
|
be_api = _lazy_be_api()
|
|
f = be_api.be_toreal(vm, 1)
|
|
push_int64_instance(vm, _mask64(int(f)))
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# /* static toint64(v) -> int64 | nil */
|
|
# static int int64_toint64(bvm *vm) {
|
|
# if (be_top(vm) == 0 || be_isnil(vm, 1)) {
|
|
# be_return_nil(vm);
|
|
# }
|
|
# be_getglobal(vm, "int64");
|
|
# be_pushvalue(vm, 1);
|
|
# be_call(vm, 1);
|
|
# be_pop(vm, 1);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_toint64(vm):
|
|
"""static toint64(v) -> int64 | nil — Convert value to int64, nil passthrough.
|
|
|
|
STATIC method: reads arg at stack[1] (no self).
|
|
If no args or nil at stack[1], returns nil.
|
|
Otherwise creates a new int64 instance via int64(v).
|
|
"""
|
|
be_api = _lazy_be_api()
|
|
if be_api.be_top(vm) == 0 or be_api.be_isnil(vm, 1):
|
|
return be_api.be_returnnilvalue(vm)
|
|
# Push int64 class, push value, call int64(v)
|
|
be_api.be_getglobal(vm, "int64")
|
|
be_api.be_pushvalue(vm, 1)
|
|
be_api.be_call(vm, 1)
|
|
be_api.be_pop(vm, 1)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# ============================================================================
|
|
# Arithmetic operators
|
|
# ============================================================================
|
|
|
|
# static int int64_add(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# int64_t *j64 = arg_get_p(vm, 2);
|
|
# int64_t b = j64 ? *j64 : 0;
|
|
# if ((b > 0 && *i64 > INT64_MAX - b) || (b < 0 && *i64 < INT64_MIN - b)) {
|
|
# be_raise(vm, "overflow_error", "integer overflow in addition");
|
|
# }
|
|
# push_int64_instance(vm, *i64 + b);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_add(vm):
|
|
"""+(self, other_int64) -> int64 — Add two int64 values."""
|
|
be_api = _lazy_be_api()
|
|
a = self_get_p(vm)
|
|
b_ptr = arg_get_p(vm, 2)
|
|
b = b_ptr if b_ptr is not None else 0
|
|
# Overflow check (matching C implementation)
|
|
if (b > 0 and a > INT64_MAX - b) or (b < 0 and a < INT64_MIN - b):
|
|
be_api.be_raise(vm, "overflow_error", "integer overflow in addition")
|
|
push_int64_instance(vm, a + b)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# /* add(self, int32) -> int64 : add a Berry int directly */
|
|
# static int int64_add32(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# int64_t b = (int64_t)be_toint(vm, 2);
|
|
# if ((b > 0 && *i64 > INT64_MAX - b) || (b < 0 && *i64 < INT64_MIN - b)) {
|
|
# be_raise(vm, "overflow_error", "integer overflow in addition");
|
|
# }
|
|
# push_int64_instance(vm, *i64 + b);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_add32(vm):
|
|
"""add(self, int32) -> int64 — Add a Berry int32 directly."""
|
|
be_api = _lazy_be_api()
|
|
a = self_get_p(vm)
|
|
b = be_api.be_toint(vm, 2)
|
|
# Overflow check (matching C implementation)
|
|
if (b > 0 and a > INT64_MAX - b) or (b < 0 and a < INT64_MIN - b):
|
|
be_api.be_raise(vm, "overflow_error", "integer overflow in addition")
|
|
push_int64_instance(vm, a + b)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# /* -(self, other_int64) -> int64 */
|
|
# static int int64_sub(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# int64_t *j64 = arg_get_p(vm, 2);
|
|
# int64_t b = j64 ? *j64 : 0;
|
|
# if ((*i64 > 0 && b < *i64 - INT64_MAX) || (*i64 < 0 && b > *i64 - INT64_MIN)) {
|
|
# be_raise(vm, "overflow_error", "integer overflow in subtraction");
|
|
# }
|
|
# push_int64_instance(vm, *i64 - b);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_sub(vm):
|
|
"""-(self, other_int64) -> int64 — Subtract two int64 values."""
|
|
be_api = _lazy_be_api()
|
|
a = self_get_p(vm)
|
|
b_ptr = arg_get_p(vm, 2)
|
|
b = b_ptr if b_ptr is not None else 0
|
|
# Overflow check for subtraction (matching C implementation)
|
|
if (a > 0 and b < a - INT64_MAX) or (a < 0 and b > a - INT64_MIN):
|
|
be_api.be_raise(vm, "overflow_error", "integer overflow in subtraction")
|
|
push_int64_instance(vm, a - b)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# /* -*(self) -> int64 : unary negation */
|
|
# static int int64_neg(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# if (*i64 == INT64_MIN) {
|
|
# be_raise(vm, "overflow_error", "cannot negate INT64_MIN");
|
|
# }
|
|
# push_int64_instance(vm, -*i64);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_neg(vm):
|
|
"""-*(self) -> int64 — Unary negation."""
|
|
be_api = _lazy_be_api()
|
|
a = self_get_p(vm)
|
|
if a == INT64_MIN:
|
|
be_api.be_raise(vm, "overflow_error", "cannot negate INT64_MIN")
|
|
push_int64_instance(vm, -a)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# static int int64_mul(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# int64_t *j64 = arg_get_p(vm, 2);
|
|
# int64_t a = *i64;
|
|
# int64_t b = j64 ? *j64 : 0;
|
|
# if (a == 0 || b == 0) {
|
|
# push_int64_instance(vm, 0);
|
|
# be_return(vm);
|
|
# }
|
|
# if ((a == INT64_MIN && b == -1) || (a == -1 && b == INT64_MIN)) {
|
|
# be_raise(vm, "overflow_error", "integer overflow in multiplication");
|
|
# }
|
|
# if ((a > 0 && b > 0 && a > INT64_MAX / b) ||
|
|
# (a < 0 && b < 0 && a < INT64_MAX / b) ||
|
|
# (a > 0 && b < 0 && b < INT64_MIN / a) ||
|
|
# (a < 0 && b > 0 && a < INT64_MIN / b)) {
|
|
# be_raise(vm, "overflow_error", "integer overflow in multiplication");
|
|
# }
|
|
# push_int64_instance(vm, a * b);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_mul(vm):
|
|
"""*(self, other_int64) -> int64 — Multiply two int64 values."""
|
|
be_api = _lazy_be_api()
|
|
a = self_get_p(vm)
|
|
b_ptr = arg_get_p(vm, 2)
|
|
b = b_ptr if b_ptr is not None else 0
|
|
# Short-circuit: if either is 0, result is 0
|
|
if a == 0 or b == 0:
|
|
push_int64_instance(vm, 0)
|
|
return be_api.be_returnvalue(vm)
|
|
# Overflow check: INT64_MIN * -1 (or vice versa)
|
|
if (a == INT64_MIN and b == -1) or (a == -1 and b == INT64_MIN):
|
|
be_api.be_raise(vm, "overflow_error", "integer overflow in multiplication")
|
|
# General overflow check: Python has arbitrary precision, so we can
|
|
# simply compute the product and check if it fits in int64 range.
|
|
result = a * b
|
|
if result > INT64_MAX or result < INT64_MIN:
|
|
be_api.be_raise(vm, "overflow_error", "integer overflow in multiplication")
|
|
push_int64_instance(vm, result)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# /* /(self, other_int64) -> int64 */
|
|
# static int int64_div(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# int64_t *j64 = arg_get_p(vm, 2);
|
|
# if (j64 == NULL || *j64 == 0) {
|
|
# be_raise(vm, "divzero_error", "division by zero");
|
|
# }
|
|
# if (*i64 == INT64_MIN && *j64 == -1) {
|
|
# be_raise(vm, "overflow_error", "division overflow: INT64_MIN / -1");
|
|
# }
|
|
# push_int64_instance(vm, *i64 / *j64);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_div(vm):
|
|
"""/(self, other_int64) -> int64 — Divide two int64 values (truncation toward zero)."""
|
|
be_api = _lazy_be_api()
|
|
a = self_get_p(vm)
|
|
b_ptr = arg_get_p(vm, 2)
|
|
# Division by zero check (null or zero)
|
|
if b_ptr is None or b_ptr == 0:
|
|
be_api.be_raise(vm, "divzero_error", "division by zero")
|
|
b = b_ptr
|
|
# Overflow check: INT64_MIN / -1
|
|
if a == INT64_MIN and b == -1:
|
|
be_api.be_raise(vm, "overflow_error", "division overflow: INT64_MIN / -1")
|
|
# C-style truncation toward zero (Python // floors toward negative infinity)
|
|
# Use: sign * (abs(a) // abs(b))
|
|
if (a < 0) != (b < 0):
|
|
result = -(abs(a) // abs(b))
|
|
else:
|
|
result = abs(a) // abs(b)
|
|
push_int64_instance(vm, result)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# /* %(self, other_int64) -> int64 */
|
|
# static int int64_mod(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# int64_t *j64 = arg_get_p(vm, 2);
|
|
# if (j64 == NULL || *j64 == 0) {
|
|
# be_raise(vm, "divzero_error", "modulo by zero");
|
|
# }
|
|
# push_int64_instance(vm, *i64 % *j64);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_mod(vm):
|
|
"""%(self, other_int64) -> int64 — Modulo (C-style truncation toward zero)."""
|
|
be_api = _lazy_be_api()
|
|
a = self_get_p(vm)
|
|
b_ptr = arg_get_p(vm, 2)
|
|
# Modulo by zero check
|
|
if b_ptr is None or b_ptr == 0:
|
|
be_api.be_raise(vm, "divzero_error", "modulo by zero")
|
|
b = b_ptr
|
|
# C-style modulo: a - trunc(a/b) * b
|
|
# trunc(a/b) = sign * (abs(a) // abs(b))
|
|
if (a < 0) != (b < 0):
|
|
trunc_div = -(abs(a) // abs(b))
|
|
else:
|
|
trunc_div = abs(a) // abs(b)
|
|
result = a - trunc_div * b
|
|
push_int64_instance(vm, result)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# ============================================================================
|
|
# Shift operators
|
|
# ============================================================================
|
|
|
|
# /* <<(self, int32) -> int64 */
|
|
# static int int64_shiftleft(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# int32_t j32 = be_toint(vm, 2) & 63;
|
|
# push_int64_instance(vm, *i64 << j32);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_shiftleft(vm):
|
|
"""<<(self, int32) -> int64 — Left shift, amount masked to 0-63."""
|
|
be_api = _lazy_be_api()
|
|
a = self_get_p(vm)
|
|
shift = be_api.be_toint(vm, 2) & 63
|
|
push_int64_instance(vm, a << shift)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# /* >>(self, int32) -> int64 */
|
|
# static int int64_shiftright(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# int32_t j32 = be_toint(vm, 2) & 63;
|
|
# push_int64_instance(vm, *i64 >> j32);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_shiftright(vm):
|
|
""">>(self, int32) -> int64 — Arithmetic right shift, amount masked to 0-63."""
|
|
be_api = _lazy_be_api()
|
|
a = self_get_p(vm)
|
|
shift = be_api.be_toint(vm, 2) & 63
|
|
push_int64_instance(vm, a >> shift)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# ============================================================================
|
|
# Comparison operators
|
|
# ============================================================================
|
|
|
|
import struct
|
|
|
|
# static int int64_compare(bvm *vm, int64_t *i64, int idx, bbool *valid) {
|
|
# *valid = btrue;
|
|
# int64_t *j64 = arg_get_p(vm, idx);
|
|
# if (j64) {
|
|
# if (*i64 < *j64) return -1;
|
|
# if (*i64 > *j64) return 1;
|
|
# return 0;
|
|
# }
|
|
# if (be_isint(vm, idx) || be_isnil(vm, idx)) {
|
|
# int64_t b = be_isint(vm, idx) ? (int64_t)be_toint(vm, idx) : 0;
|
|
# if (*i64 < b) return -1;
|
|
# if (*i64 > b) return 1;
|
|
# return 0;
|
|
# }
|
|
# if (be_isbytes(vm, idx)) {
|
|
# size_t len = 0;
|
|
# const void *ptr = be_tobytes(vm, idx, &len);
|
|
# if (len == sizeof(int64_t) && memcmp(i64, ptr, sizeof(int64_t)) == 0) {
|
|
# return 0;
|
|
# }
|
|
# *valid = btrue;
|
|
# return 1;
|
|
# }
|
|
# *valid = bfalse;
|
|
# return 1;
|
|
# }
|
|
def _int64_compare(vm, val, idx):
|
|
"""Compare int64 value against the argument at stack position idx.
|
|
|
|
Returns (cmp, valid) where cmp is -1/0/1 and valid is bool.
|
|
"""
|
|
be_api = _lazy_be_api()
|
|
|
|
# Check if other is an int64 instance
|
|
j64 = arg_get_p(vm, idx)
|
|
if j64 is not None:
|
|
if val < j64:
|
|
return (-1, True)
|
|
if val > j64:
|
|
return (1, True)
|
|
return (0, True)
|
|
|
|
# Check if other is a plain int or nil (nil treated as 0)
|
|
if be_api.be_isint(vm, idx) or be_api.be_isnil(vm, idx):
|
|
b = be_api.be_toint(vm, idx) if be_api.be_isint(vm, idx) else 0
|
|
if val < b:
|
|
return (-1, True)
|
|
if val > b:
|
|
return (1, True)
|
|
return (0, True)
|
|
|
|
# Check if other is bytes — compare raw 8-byte LE representation
|
|
from berry_port.be_byteslib import be_isbytes, be_tobytes
|
|
if be_isbytes(vm, idx):
|
|
bufptr, length = be_tobytes(vm, idx)
|
|
if length == 8 and bufptr is not None:
|
|
self_bytes = struct.pack('<q', val)
|
|
other_bytes = bytes(bufptr[:8])
|
|
if self_bytes == other_bytes:
|
|
return (0, True)
|
|
return (1, True)
|
|
|
|
# Unknown type — not valid for comparison
|
|
return (1, False)
|
|
|
|
# static int int64_equals(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# bbool valid;
|
|
# int cmp = int64_compare(vm, i64, 2, &valid);
|
|
# be_pushbool(vm, valid && cmp == 0);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_equals(vm):
|
|
"""==(self, other) -> bool — Equality comparison."""
|
|
be_api = _lazy_be_api()
|
|
val = self_get_p(vm)
|
|
cmp, valid = _int64_compare(vm, val, 2)
|
|
be_api.be_pushbool(vm, valid and cmp == 0)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# static int int64_nequals(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# bbool valid;
|
|
# int cmp = int64_compare(vm, i64, 2, &valid);
|
|
# be_pushbool(vm, !valid || cmp != 0);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_nequals(vm):
|
|
"""!=(self, other) -> bool — Inequality comparison."""
|
|
be_api = _lazy_be_api()
|
|
val = self_get_p(vm)
|
|
cmp, valid = _int64_compare(vm, val, 2)
|
|
be_api.be_pushbool(vm, not valid or cmp != 0)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# static int int64_gt(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# int64_t *j64 = arg_get_p(vm, 2);
|
|
# int64_t b = j64 ? *j64 : 0;
|
|
# be_pushbool(vm, *i64 > b);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_gt(vm):
|
|
""">(self, other) -> bool — Greater than."""
|
|
be_api = _lazy_be_api()
|
|
a = self_get_p(vm)
|
|
b_ptr = arg_get_p(vm, 2)
|
|
b = b_ptr if b_ptr is not None else 0
|
|
be_api.be_pushbool(vm, a > b)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# static int int64_gte(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# int64_t *j64 = arg_get_p(vm, 2);
|
|
# int64_t b = j64 ? *j64 : 0;
|
|
# be_pushbool(vm, *i64 >= b);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_gte(vm):
|
|
""">=(self, other) -> bool — Greater than or equal."""
|
|
be_api = _lazy_be_api()
|
|
a = self_get_p(vm)
|
|
b_ptr = arg_get_p(vm, 2)
|
|
b = b_ptr if b_ptr is not None else 0
|
|
be_api.be_pushbool(vm, a >= b)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# static int int64_lt(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# int64_t *j64 = arg_get_p(vm, 2);
|
|
# int64_t b = j64 ? *j64 : 0;
|
|
# be_pushbool(vm, *i64 < b);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_lt(vm):
|
|
"""<(self, other) -> bool — Less than."""
|
|
be_api = _lazy_be_api()
|
|
a = self_get_p(vm)
|
|
b_ptr = arg_get_p(vm, 2)
|
|
b = b_ptr if b_ptr is not None else 0
|
|
be_api.be_pushbool(vm, a < b)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# static int int64_lte(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# int64_t *j64 = arg_get_p(vm, 2);
|
|
# int64_t b = j64 ? *j64 : 0;
|
|
# be_pushbool(vm, *i64 <= b);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_lte(vm):
|
|
"""<=(self, other) -> bool — Less than or equal."""
|
|
be_api = _lazy_be_api()
|
|
a = self_get_p(vm)
|
|
b_ptr = arg_get_p(vm, 2)
|
|
b = b_ptr if b_ptr is not None else 0
|
|
be_api.be_pushbool(vm, a <= b)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# ============================================================================
|
|
# Bytes conversion
|
|
# ============================================================================
|
|
|
|
# /* tobytes(self) -> bytes */
|
|
# static int int64_tobytes(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# be_pushbytes(vm, i64, sizeof(int64_t));
|
|
# be_return(vm);
|
|
# }
|
|
def int64_tobytes(vm):
|
|
"""tobytes(self) -> bytes — 8-byte little-endian representation."""
|
|
be_api = _lazy_be_api()
|
|
val = self_get_p(vm)
|
|
data = struct.pack('<q', val)
|
|
from berry_port.be_byteslib import be_pushbytes
|
|
be_pushbytes(vm, data, len(data))
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# /* static frombytes(b [, idx]) -> int64 */
|
|
# static int int64_frombytes(bvm *vm) {
|
|
# int argc = be_top(vm);
|
|
# size_t len = 0;
|
|
# const uint8_t *ptr = (const uint8_t*)be_tobytes(vm, 1, &len);
|
|
# int32_t idx = (argc > 1) ? be_toint(vm, 2) : 0;
|
|
#
|
|
# if (idx < 0) { idx = (int32_t)len + idx; }
|
|
# if (idx < 0) { idx = 0; }
|
|
# if (idx >= (int32_t)len) { idx = (int32_t)len; }
|
|
#
|
|
# uint32_t usable_len = len - idx;
|
|
# if (usable_len > sizeof(int64_t)) { usable_len = sizeof(int64_t); }
|
|
# int64_t val = 0;
|
|
# memmove(&val, ptr + idx, usable_len);
|
|
# push_int64_instance(vm, val);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_frombytes(vm):
|
|
"""static frombytes(b [, idx]) -> int64 — Read int64 from bytes at offset.
|
|
|
|
STATIC method: reads args at stack[1] (bytes), stack[2] (optional offset).
|
|
Supports negative indexing. Zero-pads if fewer than 8 bytes available.
|
|
"""
|
|
be_api = _lazy_be_api()
|
|
from berry_port.be_byteslib import be_tobytes
|
|
argc = be_api.be_top(vm)
|
|
bufptr, length = be_tobytes(vm, 1)
|
|
idx = be_api.be_toint(vm, 2) if argc > 1 else 0
|
|
|
|
# Negative indexing
|
|
if idx < 0:
|
|
idx = length + idx
|
|
if idx < 0:
|
|
idx = 0
|
|
if idx >= length:
|
|
idx = length
|
|
|
|
usable_len = length - idx
|
|
if usable_len > 8:
|
|
usable_len = 8
|
|
|
|
# Extract usable bytes and zero-pad to 8 bytes
|
|
if bufptr is not None and usable_len > 0:
|
|
raw = bytes(bufptr[idx:idx + usable_len])
|
|
else:
|
|
raw = b''
|
|
padded = raw + b'\x00' * (8 - len(raw))
|
|
|
|
val = struct.unpack('<q', padded)[0]
|
|
push_int64_instance(vm, val)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# ============================================================================
|
|
# 32-bit accessors
|
|
# ============================================================================
|
|
|
|
# /* low32(self) -> int */
|
|
# static int int64_low32(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# be_pushint(vm, (int32_t)*i64);
|
|
# be_return(vm);
|
|
# }
|
|
def int64_low32(vm):
|
|
"""low32(self) -> int — Lower 32 bits as signed int32."""
|
|
be_api = _lazy_be_api()
|
|
val = self_get_p(vm)
|
|
# Equivalent of C (int32_t) cast: mask to 32 bits, then sign-extend
|
|
val32 = ((val & 0xFFFFFFFF) ^ 0x80000000) - 0x80000000
|
|
be_api.be_pushint(vm, val32)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# /* high32(self) -> int */
|
|
# static int int64_high32(bvm *vm) {
|
|
# int64_t *i64 = self_get_p(vm);
|
|
# be_pushint(vm, (int32_t)(*i64 >> 32));
|
|
# be_return(vm);
|
|
# }
|
|
def int64_high32(vm):
|
|
"""high32(self) -> int — Upper 32 bits as signed int32 (arithmetic shift right by 32)."""
|
|
be_api = _lazy_be_api()
|
|
val = self_get_p(vm)
|
|
# Arithmetic right shift by 32, then cast to signed int32
|
|
shifted = val >> 32
|
|
val32 = ((shifted & 0xFFFFFFFF) ^ 0x80000000) - 0x80000000
|
|
be_api.be_pushint(vm, val32)
|
|
return be_api.be_returnvalue(vm)
|
|
|
|
# ============================================================================
|
|
# Registration
|
|
# ============================================================================
|
|
|
|
def be_load_int64lib(vm):
|
|
be_api = _lazy_be_api()
|
|
members = [
|
|
("_p", None),
|
|
("init", int64_init),
|
|
("deinit", int64_deinit),
|
|
("fromu32", int64_fromu32, True), # static
|
|
("fromfloat", int64_fromfloat, True), # static
|
|
("toint64", int64_toint64, True), # static
|
|
("tostring", int64_tostring),
|
|
("fromstring", int64_fromstring, True), # static
|
|
("isint", int64_isint),
|
|
("toint", int64_toint),
|
|
("tobool", int64_tobool),
|
|
("add", int64_add32),
|
|
("+", int64_add),
|
|
("-", int64_sub),
|
|
("*", int64_mul),
|
|
("/", int64_div),
|
|
("%", int64_mod),
|
|
("-*", int64_neg),
|
|
("==", int64_equals),
|
|
("!=", int64_nequals),
|
|
(">", int64_gt),
|
|
(">=", int64_gte),
|
|
("<", int64_lt),
|
|
("<=", int64_lte),
|
|
("<<", int64_shiftleft),
|
|
(">>", int64_shiftright),
|
|
("tobytes", int64_tobytes),
|
|
("frombytes", int64_frombytes, True), # static
|
|
("low32", int64_low32),
|
|
("high32", int64_high32),
|
|
]
|
|
be_api.be_regclass(vm, "int64", members)
|