Files
Tasmota/lib/libesp32/berry/berry_port/be_int64lib.py
T

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)