Files

614 lines
19 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# String copy function
@life(noescape(dest))
def strcpy(dest: str, src: str) -> str:
if dest == None or src == None: return None
original_dest: str = dest
for s in src:
dest[0] = s
dest += 1
dest[0] = 0
return original_dest
# String concatenate function
@life(noescape(dest))
def strcat(dest: str, src: str) -> str:
if dest == None or src == None: return dest
original_dest: str = dest
# Find end of dest
while dest[0] != '\0':
dest += 1
# Copy src
for s in src:
dest[0] = s
dest += 1
dest[0] = 0
return original_dest
# String copy function with limited length
@life(noescape(dest))
def strncpy(dest: str, src: str, n: sizet) -> str:
if dest == None or src == None or n == 0: return dest
original_dest: str = dest
i: sizet = 0
for s in src:
if i >= n: break
dest[0] = s
dest += 1
i += 1
# Fill remaining space with '\0'
while i < n:
dest[0] = 0
dest += 1
i += 1
return original_dest
# String concatenate function with limited length
@life(noescape(dest))
def strncat(dest: str, src: str, n: sizet) -> str:
if dest == None or src == None or n == 0: return dest
original_dest: str = dest
# Find end of dest
while dest[0] != '\0':
dest += 1
# Copy up to n chars from src
i: sizet = 0
for s in src:
if i >= n: break
dest[0] = s
dest += 1
i += 1
dest[0] = 0
return original_dest
# String length function
export def strlen(src: str) -> sizet:
length: sizet = 0
for _ in src:
length += 1
return length
# String comparison function
# Note: str1[0] - str2[0] is an i8 subtraction that wraps (0xFF means -1), so both operands
# must be promoted to i32 before subtracting (as in memcmp below); otherwise strcmp("abc","abd") returns 255 instead of -1.
def strcmp(str1: str, str2: str) -> int:
# Pointer-equality short-circuit: identical pointers guarantee identical contents, so return 0 directly and skip the full byte loop.
# Semantics strictly unchanged (same pointer => same content => strcmp result is 0); different pointers fall through to the loop below.
if str1 == str2:
return 0
while str1[0] != '\0' and str2[0] != '\0':
if str1[0] != str2[0]:
return int(str1[0]) - int(str2[0])
str1 += 1
str2 += 1
return int(str1[0]) - int(str2[0])
def samestr(str1: str, str2: str) -> bool:
return strcmp(str1, str2) == 0
# String compare function with limited length
def strncmp(str1: str, str2: str, n: sizet) -> int:
# Pointer-equality short-circuit (same as strcmp): identical pointers compare equal for any n, so return 0.
# Semantics strictly unchanged.
if str1 == str2:
return 0
i: sizet = 0
while str1[0] != '\0' and str2[0] != '\0' and i < n:
if str1[0] != str2[0]:
return int(str1[0]) - int(str2[0])
str1 += 1
str2 += 1
i += 1
if i < n:
return int(str1[0]) - int(str2[0])
return 0
# Memory compare function
def memcmp(ptr1: rawptr, ptr2: rawptr, n: sizet) -> int:
p1: char* = char*(ptr1)
p2: char* = char*(ptr2)
for i in range(int(n)):
if p1[i] != p2[i]:
return int(p1[i]) - int(p2[i])
return 0
# String find function
def strchr(s: str, cr: int) -> str:
while s[0] != '\0':
if s[0] == char(cr):
return str(s)
s += 1
if cr == '\0':
return str(s)
return None
# String reverse find function (find last occurrence of character)
@life(noescape(s))
def strrchr(s: str, cr: int) -> str:
last: str = None
while s[0] != '\0':
if s[0] == char(cr):
last = s
s += 1
if cr == '\0': return s
return last
# Substring search function (find first occurrence of needle in s, return pointer to that position, or None if not found)
# Hot-path optimization: use strchr to jump to first-char candidates instead of calling strncmp at every byte, and drop the upfront strlen(s) (strncmp stops at '\0').
# Semantics unchanged: an empty needle returns s; not found returns None.
@life(noescape(s, needle))
def strstr(s: str, needle: str) -> str:
if needle[0] == 0:
return s
needle_len: sizet = strlen(needle)
if needle_len == 0:
return s
first_ch: int = int(needle[0])
cur: str = s
while True:
hit: str = strchr(cur, first_ch)
if hit is None:
return None
if strncmp(hit, needle, needle_len) == 0:
return hit
cur = hit + 1
# String skip function
def strspn(s: str, skip: str = " ") -> int:
si: int = 0
while s[si] != 0:
found: int = 0
sj: int = 0
while skip[sj] != 0:
if s[si] == skip[sj]:
found = 1
break
sj += 1
if found == 0:
break
si += 1
return si
# Memory set function
@life(noescape(ptr))
export def memset(ptr: rawptr, value: int, num: sizet) -> rawptr:
# Original implementation:
# p: char* = char*(ptr)
# i: sizet
# for i in range(num):
# p[i] = t.CUnsignedChar(value)
if num == 0: return ptr
__asm__(f"""mov rdi, {asminp(ptr, REG_ANY)}
mov eax, {asminp(value, REG_ANY)}
mov rcx, {asminp(num, REG_ANY)}
rep stosb""",
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX,
ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RDI])
return ptr
@life(noescape(ptr))
def memset32(ptr: rawptr, value: uint32, count: sizet) -> rawptr:
# Original implementation:
# p: uint32* = uint32*(ptr)
# i: sizet
# for i in range(count):
# p[i] = value
if count == 0: return ptr
__asm__(f"""mov rdi, {asminp(ptr, REG_ANY)}
mov eax, {asminp(value, REG_ANY)}
mov rcx, {asminp(count, REG_ANY)}
rep stosd""",
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX,
ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RDI])
return ptr
# Memory copy function
@life(noescape(dest))
export def memcpy(dest: rawptr, src: rawptr, num: sizet) -> rawptr:
# Original implementation:
# d: char* = char*(dest)
# s: char* = char*(src)
# i: sizet
# for i in range(num):
# d[i] = s[i]
if num == 0: return dest
__asm__(f"""mov rdi, {asminp(dest, REG_ANY)}
mov rsi, {asminp(src, REG_ANY)}
mov rcx, {asminp(num, REG_ANY)}
rep movsb""",
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RCX,
ASM_DESCR.CLOBBER_RDI, ASM_DESCR.CLOBBER_RSI])
return dest
# Memory move function
@life(noescape(dest))
def memmove(dest: rawptr, src: rawptr, num: sizet) -> rawptr:
# Original implementation:
# d: char* = char*(dest)
# s: char* = char*(src)
# i: sizet
# if d < s:
# for i in range(num):
# d[i] = s[i]
# elif d > s:
# for i in range(num, 0, -1):
# d[i - 1] = s[i - 1]
if num == 0: return dest
__asm__(f"""mov rdi, {asminp(dest, REG_ANY)}
mov rsi, {asminp(src, REG_ANY)}
mov rcx, {asminp(num, REG_ANY)}
cmp rdi, rsi
jb 2f
std
add rdi, rcx
dec rdi
add rsi, rcx
dec rsi
rep movsb
cld
jmp 3f
2:
rep movsb
3:""",
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RCX,
ASM_DESCR.CLOBBER_RDI, ASM_DESCR.CLOBBER_RSI])
return dest
def atoi(src: str) -> int:
num: int = 0
sign: int = 1
s: str = src
if s == None: return 0
# Skip whitespace
while s[0] == ' ' or s[0] == '\t' or s[0] == '\n' or s[0] == '\r':
s += 1
# Sign
if s[0] == '-':
sign = -1
s += 1
elif s[0] == '+':
s += 1
for ch in s:
if ch < '0' or ch > '9': break
digit: int = ch - '0'
num = num * 10 + digit
return num * sign
def atoll(src: str) -> int64:
num: int64 = 0
sign: int64 = 1
s: str = src
if s == None: return 0
while s[0] == ' ' or s[0] == '\t' or s[0] == '\n' or s[0] == '\r':
s += 1
if s[0] == '-':
sign = -1
s += 1
elif s[0] == '+':
s += 1
# Hex prefix 0x / 0X
is_hex: int = 0
if s[0] == '0' and (s[1] == 'x' or s[1] == 'X'):
is_hex = 1
s += 2
if is_hex:
for ch in s:
d: int = 0
if ch >= '0' and ch <= '9':
d = ch - '0'
elif ch >= 'a' and ch <= 'f':
d = ch - 'a' + 10
elif ch >= 'A' and ch <= 'F':
d = ch - 'A' + 10
else:
break
digit: int64 = int64(d)
num = num * 16 + digit
else:
for ch in s:
if ch < '0' or ch > '9': break
d: int = ch - '0'
digit: int64 = int64(d)
num = num * 10 + digit
return num * sign
# ============================================================
# Correctly-rounded decimal literal -> double conversion (N4): mantissa in u64 (<=19 digits, 20th digit rounds once),
# scaled once by an exact power of ten in double-double (hi+lo, ~106 bits) then rounded a single final time.
# Convention: no global table (10^0..10^22 built by repeated multiplication of 10, each step exact).
# For p<0 compute 10^p*2^600 in the normal range and scale back exactly, avoiding subnormal loss in the double-double low part below ~1e-292.
# ============================================================
# _dd_split_hi - upper half of the Dekker split (hi = top 26 bits).
# Magnitude rescaling: multiply by 2^-28 when |a| > 6.7e292 and by 2^28 when |a| < 1e-292 (powers of two, lossless for normals); caller gets the lower half via lo = a - hi.
def _dd_split_hi(a: double) -> double:
aa: double = a
big: int = 0
small: int = 0
if a > 6.7e292 or a < -6.7e292:
aa = a * 3.725290298461914e-09
big = 1
elif (a > 0.0 and a < 1.0e-292) or (a < 0.0 and a > -1.0e-292):
aa = a * 268435456.0
small = 1
tt: double = aa * 134217729.0
hi: double = tt - (tt - aa)
if big != 0:
return hi * 268435456.0
if small != 0:
return hi * 3.725290298461914e-09
return hi
# _dd_mul - double-double multiply: out = a × b (out[0]=hi, out[1]=lo)
def _dd_mul(ah: double, al: double, bh: double, bl: double,
out: [2]double):
p: double = ah * bh
a_hi: double = _dd_split_hi(ah)
a_lo: double = ah - a_hi
b_hi: double = _dd_split_hi(bh)
b_lo: double = bh - b_hi
e: double = ((a_hi * b_hi - p) + a_hi * b_lo + a_lo * b_hi) + a_lo * b_lo
e = e + (ah * bl + al * bh)
s: double = p + e
out[0] = s
out[1] = e - (s - p)
# _dd_add - double-double add: out = a + b
def _dd_add(ah: double, al: double, bh: double, bl: double,
out: [2]double):
s: double = ah + bh
bb: double = s - ah
e: double = (ah - (s - bb)) + (bh - bb)
e = e + (al + bl)
r0: double = s + e
out[0] = r0
out[1] = e - (r0 - s)
# _dd_div - double-double divide: out = a / b (q0 plus one residual correction step)
def _dd_div(ah: double, al: double, bh: double, bl: double,
out: [2]double, tmp: [2]double):
q0: double = ah / bh
_dd_mul(bh, bl, q0, 0.0, tmp)
_dd_add(ah, al, -tmp[0], -tmp[1], tmp)
q1: double = tmp[0] / bh
_dd_add(q0, 0.0, q1, 0.0, out)
# _dd_from_u64 - u64 -> double-double (exact: hi is the rounded value, lo the exact residual)
def _dd_from_u64(m: uint64, out: [2]double):
hi: double = double(m)
lo: double = 0.0
if m >= uint64(9007199254740992):
lo = double(m - uint64(hi))
s: double = hi + lo
bb: double = s - hi
out[0] = s
out[1] = (hi - (s - bb)) + (lo - bb)
# _dd_pow10 - double-double 10^e (e >= 0; chunked by <=22 so each chunk is exact)
def _dd_pow10(e: int, out: [2]double, tmp: [2]double):
out[0] = 1.0
out[1] = 0.0
k: int = e
while k > 0:
step: int = 22
if k < 22:
step = k
p10: double = 1.0
for i in range(int(step)):
p10 = p10 * 10.0
_dd_mul(out[0], out[1], p10, 0.0, tmp)
out[0] = tmp[0]
out[1] = tmp[1]
k -= step
# _pow2 - 2^n (|n| <= 1000): repeated multiply by 2/0.5, exact at every step
def _pow2(n: int) -> double:
r: double = 1.0
i: int = int(n)
if i >= 0:
while i > 0:
r = r * 2.0
i -= 1
else:
while i < 0:
r = r * 0.5
i += 1
return r
# _atof_scale_stepwise - stepwise ×/÷10 scaling (fallback for subnormal/overflow cases)
def _atof_scale_stepwise(x: double, p: int) -> double:
r: double = x
e: int = p
while e > 0:
r = r * 10.0
e -= 1
while e < 0:
r = r / 10.0
e += 1
return r
def atof(src: str) -> double:
s: str = src
if s == None: return 0.0
# Skip whitespace
while s[0] == ' ' or s[0] == '\t' or s[0] == '\n' or s[0] == '\r':
s += 1
# Sign
sign: double = 1.0
if s[0] == '-':
sign = -1.0
s += 1
elif s[0] == '+':
s += 1
# Mantissa: significant digits packed into u64 (<=19 digits), tracking the decimal exponent
mant: uint64 = 0
nd: int = 0
dexp: int = 0
seen_dot: int = 0
started: int = 0
rounded: int = 0
while s[0] != '\0':
ch: char = s[0]
if ch >= '0' and ch <= '9':
d: int = ch - '0'
if started == 0:
# Leading zeros are not stored in the mantissa; leading zeros in the fraction still advance the decimal exponent
if d != 0:
started = 1
mant = uint64(d)
nd = 1
if seen_dot != 0:
dexp -= 1
elif seen_dot != 0:
dexp -= 1
elif nd < 19:
mant = mant * uint64(10) + uint64(d)
nd += 1
if seen_dot != 0:
dexp -= 1
else:
# From the 20th digit on: dropped integer digits bump the exponent; dropped fraction digits do not.
# Only the 20th digit is used for one rounding (dropped amount <= 1e-19 relative error).
if seen_dot == 0:
dexp += 1
elif rounded == 0:
if d >= 5:
mant += uint64(1)
rounded = 1
elif ch == '.':
seen_dot = 1
else:
break
s += 1
if started == 0:
return sign * 0.0
# Carry: rounding may turn 19 digits into 20
if mant >= uint64(10000000000000000000):
mant = mant // uint64(10)
dexp += 1
# Exponent part
exp: int = 0
if s[0] == 'e' or s[0] == 'E':
s += 1
exp_sign: int = 1
if s[0] == '-':
exp_sign = -1
s += 1
elif s[0] == '+':
s += 1
while s[0] >= '0' and s[0] <= '9':
exp = exp * 10 + (s[0] - '0')
s += 1
exp = exp * exp_sign
p: int = dexp + exp
if p == 0:
return sign * double(mant)
# Double-double scaling: mant × 10^p, single final rounding
ta: [2]double
tb: [2]double
tc: [2]double
tr: [2]double
_dd_from_u64(mant, ta)
v: double = 0.0
if p > 0:
_dd_pow10(p, tb, tr)
_dd_mul(ta[0], ta[1], tb[0], tb[1], tr)
v = tr[0] + tr[1]
else:
# p < 0: compute 2^600 / 10^(-p) (= 10^p * 2^600) in the normal range, then scale back exactly.
# Direct 10^p loses effective bits to subnormals for large |p|; divide the denominator in chunks of <=308 (10^313 overflows to inf and yields NaN).
tb[0] = _pow2(600)
tb[1] = 0.0
q: int = -p
while q > 0:
step2: int = 308
if q < 308:
step2 = q
_dd_pow10(step2, tc, tr)
_dd_div(tb[0], tb[1], tc[0], tc[1], tr, tc)
tb[0] = tr[0]
tb[1] = tr[1]
q -= step2
_dd_mul(ta[0], ta[1], tb[0], tb[1], tr)
v = (tr[0] + tr[1]) * _pow2(-600)
# Normal range (not NaN / not inf / not subnormal) -> the correctly rounded result
if v == v and v <= 1.7976931348623157e308 and v >= -1.7976931348623157e308 \
and (v == 0.0 or v >= 2.2250738585072014e-308 or v <= -2.2250738585072014e-308):
return sign * v
# Overflow / subnormal / NaN -> fall back to stepwise scaling
return sign * _atof_scale_stepwise(double(mant), p)
def atof_out(src: str, out: double*) -> int:
"""Write the atof result to out[0] (out-parameter form, avoiding the cross-module double return bug).
The return value only signals success (1) or failure (0); the value is passed via the out pointer."""
if out is None:
return 0
v: double = atof(src)
out[0] = v
return 1
def split(s: str, delim: str, result: []str, cap: int) -> int:
"""Split s by a single-character delimiter, writing token pointers to result[0..] and a None sentinel at the end.
cap is the declared capacity (element count); token count is limited to <= cap-1, leaving one slot for the sentinel."""
count: int = 0
start: str = s
if not s or not delim or not result:
return 0
if cap <= 0:
return 0
d: char = delim[0] # single delimiter
while start[0]:
# Skip delimiters
while start[0] == d: start += 1
if not start[0]: break
# Capacity guard: keep the last slot for the end sentinel
if count >= cap - 1:
break
# Record token start
result[count] = start
count += 1
# Find the next delimiter and truncate
while start[0] and start[0] != d: start += 1
if start[0]:
start[0] = '\0'
start += 1
result[count] = None # end marker
return count
ascii_lowercase: str = "abcdefghijklmnopqrstuvwxyz"
ascii_uppercase: str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
ascii_letters: str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
digits: str = "0123456789"
hexdigits: str = "0123456789abcdefABCDEF"
octdigits: str = "01234567"
punctuation: str = "!\"#$%&'()*+,-./:;<=>?@[]^_`{|}~"
whitespace: str = " \t\n\r\f\v"
printable: str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\f\v"