2026-01-08 18:34:34 +01:00
|
|
|
# Dynamic arrays (qword elements)
|
|
|
|
|
#
|
|
|
|
|
# Layout at address `arr`:
|
|
|
|
|
# [arr + 0] len (qword)
|
|
|
|
|
# [arr + 8] cap (qword)
|
|
|
|
|
# [arr + 16] data (qword) = arr + 24
|
|
|
|
|
# [arr + 24] elements (cap * 8 bytes)
|
|
|
|
|
#
|
|
|
|
|
# Allocation: mmap; free: munmap.
|
|
|
|
|
# Growth: allocate new block, copy elements, munmap old block.
|
|
|
|
|
|
2026-02-03 09:41:11 +01:00
|
|
|
import mem.sl
|
|
|
|
|
|
2026-02-10 11:31:39 +01:00
|
|
|
#arr_to_dyn [* | std_arr] -> [* | dyn_arr]
|
|
|
|
|
word arr_to_dyn
|
2026-02-10 17:47:59 +01:00
|
|
|
dup @ dup dup 2 * 3 + alloc dup rot !
|
2026-02-10 11:31:39 +01:00
|
|
|
dup rot 2 * swap 8 + swap ! dup 16 +
|
|
|
|
|
dup 8 + dup -rot ! 0 swap 3 pick dup @
|
|
|
|
|
8 * swap 8 + swap memcpy 2drop drop nip
|
|
|
|
|
end
|
|
|
|
|
|
2026-02-05 21:36:03 +01:00
|
|
|
#arr_new [* | cap] -> [* | arr]
|
2026-02-03 09:41:11 +01:00
|
|
|
# Create a new array with given initial capacity (minimum 1)
|
|
|
|
|
word arr_new
|
|
|
|
|
dup 1 < if drop 1 end
|
|
|
|
|
dup 8 * 24 + alloc
|
|
|
|
|
dup 0 ! # len = 0
|
|
|
|
|
over over 8 + swap ! # cap = requested cap
|
|
|
|
|
dup 24 + over 16 + swap ! # data = arr + 24
|
|
|
|
|
nip
|
|
|
|
|
end
|
2026-01-08 18:34:34 +01:00
|
|
|
|
2026-02-05 21:36:03 +01:00
|
|
|
#arr_len [* | arr] -> [* | len]
|
2026-02-03 09:41:11 +01:00
|
|
|
word arr_len @ end
|
2026-01-08 18:34:34 +01:00
|
|
|
|
2026-02-05 21:36:03 +01:00
|
|
|
#arr_cap [* | arr] -> [* | cap]
|
2026-02-03 09:41:11 +01:00
|
|
|
word arr_cap 8 + @ end
|
2026-01-08 18:34:34 +01:00
|
|
|
|
2026-02-05 21:36:03 +01:00
|
|
|
#arr_data [* | arr] -> [* | ptr]
|
2026-02-03 09:41:11 +01:00
|
|
|
word arr_data 16 + @ end
|
2026-01-08 18:34:34 +01:00
|
|
|
|
2026-02-05 21:36:03 +01:00
|
|
|
#arr_free [* | arr] -> [*]
|
2026-02-03 09:41:11 +01:00
|
|
|
word arr_free
|
|
|
|
|
dup arr_cap 8 * 24 + free
|
|
|
|
|
end
|
|
|
|
|
|
2026-02-18 13:58:08 +01:00
|
|
|
# Helper: copy n qwords from src to dst
|
|
|
|
|
#arr_copy_elements [*, dst, src | n] -> [*]
|
2026-02-03 09:41:11 +01:00
|
|
|
word arr_copy_elements
|
|
|
|
|
while dup 0 > do
|
|
|
|
|
over @ 3 pick swap ! # dst = *src
|
|
|
|
|
swap 8 + swap # src += 8
|
|
|
|
|
rot 8 + -rot # dst += 8
|
|
|
|
|
1 -
|
|
|
|
|
end
|
|
|
|
|
drop 2drop
|
|
|
|
|
end
|
2026-01-08 18:34:34 +01:00
|
|
|
|
2026-02-19 16:14:41 +01:00
|
|
|
#arr_reserve [*, arr | cap] -> [* | arr]
|
2026-01-08 18:34:34 +01:00
|
|
|
# Ensures capacity >= cap; returns (possibly moved) arr pointer.
|
2026-02-03 09:41:11 +01:00
|
|
|
word arr_reserve
|
2026-02-19 16:14:41 +01:00
|
|
|
dup 1 < if drop 1 end swap # stack: [*, reqcap | arr]
|
2026-02-03 09:41:11 +01:00
|
|
|
|
|
|
|
|
# Check: if arr_cap >= reqcap, do nothing
|
|
|
|
|
over over arr_cap swap
|
|
|
|
|
>= if
|
|
|
|
|
nip
|
|
|
|
|
else
|
|
|
|
|
# Allocate new block
|
|
|
|
|
over 8 * 24 + alloc
|
|
|
|
|
|
|
|
|
|
# Copy header
|
|
|
|
|
over arr_len over swap ! # len
|
|
|
|
|
2 pick over 8 + swap ! # cap = reqcap
|
|
|
|
|
dup 24 + over 16 + swap ! # data = newarr + 24
|
|
|
|
|
|
|
|
|
|
# Copy elements
|
|
|
|
|
dup arr_data
|
|
|
|
|
2 pick arr_data
|
|
|
|
|
3 pick arr_len
|
|
|
|
|
arr_copy_elements
|
|
|
|
|
|
|
|
|
|
# Free old and return new
|
|
|
|
|
swap arr_free
|
|
|
|
|
nip
|
|
|
|
|
end
|
|
|
|
|
end
|
2026-01-08 18:34:34 +01:00
|
|
|
|
2026-02-19 16:14:41 +01:00
|
|
|
#arr_push [*, arr | x] -> [* | arr]
|
2026-02-03 09:41:11 +01:00
|
|
|
# Push element onto array, growing if needed
|
|
|
|
|
word arr_push
|
2026-02-19 16:14:41 +01:00
|
|
|
swap
|
2026-02-03 09:41:11 +01:00
|
|
|
dup arr_len over arr_cap >= if
|
|
|
|
|
dup arr_cap dup 1 < if drop 1 end 2 *
|
2026-02-19 16:14:41 +01:00
|
|
|
arr_reserve
|
2026-02-03 09:41:11 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Store x at data[len]
|
|
|
|
|
dup arr_data over arr_len 8 * +
|
|
|
|
|
rot over swap ! drop
|
|
|
|
|
|
|
|
|
|
# Increment len
|
|
|
|
|
dup @ 1 + over swap !
|
|
|
|
|
end
|
2026-01-08 18:34:34 +01:00
|
|
|
|
2026-02-05 21:36:03 +01:00
|
|
|
#arr_pop [* | arr] -> [*, arr | x]
|
2026-02-03 09:41:11 +01:00
|
|
|
# Pop element from array (returns 0 if empty)
|
|
|
|
|
word arr_pop
|
|
|
|
|
dup arr_len 0 == if
|
2026-02-05 21:36:03 +01:00
|
|
|
0
|
2026-02-03 09:41:11 +01:00
|
|
|
else
|
|
|
|
|
# Decrement len
|
|
|
|
|
dup @ 1 - over swap !
|
|
|
|
|
# Get element at new len position
|
|
|
|
|
dup arr_data over arr_len 8 * + @
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2026-02-19 16:14:41 +01:00
|
|
|
#arr_get [*, arr | i] -> [* | x]
|
2026-02-03 09:41:11 +01:00
|
|
|
# Get element at index i
|
|
|
|
|
word arr_get
|
2026-02-19 16:14:41 +01:00
|
|
|
swap arr_data swap 8 * + @
|
2026-02-03 09:41:11 +01:00
|
|
|
end
|
|
|
|
|
|
2026-02-19 16:14:41 +01:00
|
|
|
#arr_set [*, arr, x | i] -> [*]
|
2026-02-03 09:41:11 +01:00
|
|
|
# Set element at index i to x
|
|
|
|
|
word arr_set
|
2026-02-19 16:14:41 +01:00
|
|
|
rot arr_data swap 8 * + swap !
|
2026-02-10 17:47:59 +01:00
|
|
|
end
|
2026-02-18 16:05:48 +01:00
|
|
|
|
|
|
|
|
#dyn_arr_clone [* | dyn_arr] -> [* | dyn_arr_copy]
|
|
|
|
|
word dyn_arr_clone
|
|
|
|
|
dup arr_len
|
|
|
|
|
dup arr_new
|
|
|
|
|
|
|
|
|
|
dup arr_data
|
|
|
|
|
3 pick arr_data
|
|
|
|
|
3 pick
|
|
|
|
|
arr_copy_elements
|
|
|
|
|
|
|
|
|
|
dup >r
|
|
|
|
|
swap !
|
|
|
|
|
drop
|
|
|
|
|
r>
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
#arr_item_ptr [*, i | arr] -> [* | ptr]
|
|
|
|
|
word arr_item_ptr
|
|
|
|
|
swap 8 * swap 8 + +
|
|
|
|
|
end
|
|
|
|
|
|
2026-02-19 16:14:41 +01:00
|
|
|
#arr_get_static [*, arr | i] -> [* | x]
|
2026-02-18 16:05:48 +01:00
|
|
|
# Get element from built-in static array
|
|
|
|
|
word arr_get_static
|
2026-02-19 16:14:41 +01:00
|
|
|
swap arr_item_ptr @
|
2026-02-18 16:05:48 +01:00
|
|
|
end
|
|
|
|
|
|
2026-02-19 16:14:41 +01:00
|
|
|
#arr_set_static [*, arr, x | i] -> [*]
|
2026-02-18 16:05:48 +01:00
|
|
|
# Set element in built-in static array
|
|
|
|
|
word arr_set_static
|
2026-02-19 16:14:41 +01:00
|
|
|
rot arr_item_ptr swap !
|
2026-02-18 16:05:48 +01:00
|
|
|
end
|
|
|
|
|
|
2026-02-19 10:42:53 +01:00
|
|
|
#arr_static_free [* | arr] -> [*]
|
|
|
|
|
# Free built-in static array allocation produced by list literals.
|
|
|
|
|
word arr_static_free
|
|
|
|
|
dup @ 1 + 8 * free
|
2026-02-18 16:05:48 +01:00
|
|
|
end
|
|
|
|
|
|
2026-02-19 10:42:53 +01:00
|
|
|
#sort [*, addr | len] -> [*]
|
|
|
|
|
# In-place ascending sort of qword elements at `addr`.
|
|
|
|
|
:asm sort {
|
|
|
|
|
mov rcx, [r12] ; len
|
|
|
|
|
mov rbx, [r12 + 8] ; addr
|
|
|
|
|
add r12, 16
|
|
|
|
|
|
|
|
|
|
cmp rcx, 1
|
|
|
|
|
jle .done
|
|
|
|
|
|
|
|
|
|
dec rcx ; outer = len - 1
|
|
|
|
|
.outer:
|
|
|
|
|
xor rdx, rdx ; j = 0
|
|
|
|
|
|
|
|
|
|
.inner:
|
|
|
|
|
cmp rdx, rcx
|
|
|
|
|
jge .next_outer
|
|
|
|
|
|
|
|
|
|
lea r8, [rbx + rdx*8] ; &data[j]
|
|
|
|
|
mov r9, [r8] ; a = data[j]
|
|
|
|
|
mov r10, [r8 + 8] ; b = data[j+1]
|
|
|
|
|
cmp r9, r10
|
|
|
|
|
jle .no_swap
|
|
|
|
|
|
|
|
|
|
mov [r8], r10
|
|
|
|
|
mov [r8 + 8], r9
|
|
|
|
|
|
|
|
|
|
.no_swap:
|
|
|
|
|
inc rdx
|
|
|
|
|
jmp .inner
|
|
|
|
|
|
|
|
|
|
.next_outer:
|
|
|
|
|
dec rcx
|
|
|
|
|
jnz .outer
|
|
|
|
|
|
|
|
|
|
.done:
|
|
|
|
|
ret
|
|
|
|
|
}
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
#sort8 [*, addr | len] -> [*]
|
|
|
|
|
# In-place ascending sort of byte elements at `addr`.
|
|
|
|
|
:asm sort8 {
|
|
|
|
|
mov rcx, [r12] ; len
|
|
|
|
|
mov rbx, [r12 + 8] ; addr
|
|
|
|
|
add r12, 16
|
|
|
|
|
|
2026-02-18 16:05:48 +01:00
|
|
|
cmp rcx, 1
|
|
|
|
|
jle .done
|
|
|
|
|
|
|
|
|
|
dec rcx ; outer = len - 1
|
|
|
|
|
.outer:
|
|
|
|
|
xor rdx, rdx ; j = 0
|
|
|
|
|
|
|
|
|
|
.inner:
|
|
|
|
|
cmp rdx, rcx
|
|
|
|
|
jge .next_outer
|
|
|
|
|
|
2026-02-19 10:42:53 +01:00
|
|
|
lea r8, [rbx + rdx] ; &data[j]
|
|
|
|
|
movzx r9, byte [r8] ; a = data[j]
|
|
|
|
|
movzx r10, byte [r8 + 1] ; b = data[j+1]
|
|
|
|
|
cmp r9, r10
|
2026-02-18 16:05:48 +01:00
|
|
|
jle .no_swap
|
|
|
|
|
|
2026-02-19 10:42:53 +01:00
|
|
|
mov byte [r8], r10b
|
|
|
|
|
mov byte [r8 + 1], r9b
|
2026-02-18 16:05:48 +01:00
|
|
|
|
|
|
|
|
.no_swap:
|
|
|
|
|
inc rdx
|
|
|
|
|
jmp .inner
|
|
|
|
|
|
|
|
|
|
.next_outer:
|
|
|
|
|
dec rcx
|
|
|
|
|
jnz .outer
|
|
|
|
|
|
|
|
|
|
.done:
|
|
|
|
|
ret
|
|
|
|
|
}
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
#arr_clone [* | arr] -> [* | arr_copy]
|
|
|
|
|
# Clone built-in static array (len header + payload)
|
|
|
|
|
word arr_clone
|
|
|
|
|
dup @ 1 +
|
|
|
|
|
dup 8 * alloc
|
|
|
|
|
dup >r
|
|
|
|
|
rot rot
|
|
|
|
|
arr_copy_elements
|
|
|
|
|
r>
|
|
|
|
|
end
|
|
|
|
|
|
2026-02-19 10:42:53 +01:00
|
|
|
#sorted [*, addr | len] -> [* | sorted_addr]
|
|
|
|
|
# Clone qword elements and return sorted copy.
|
|
|
|
|
word sorted
|
|
|
|
|
dup >r
|
|
|
|
|
8 * alloc
|
|
|
|
|
dup >r
|
|
|
|
|
swap
|
|
|
|
|
r@
|
|
|
|
|
arr_copy_elements
|
|
|
|
|
r>
|
|
|
|
|
r>
|
|
|
|
|
over >r
|
|
|
|
|
sort
|
|
|
|
|
r>
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
#sorted8 [*, addr | len] -> [* | sorted_addr]
|
|
|
|
|
# Clone byte elements and return sorted copy.
|
|
|
|
|
word sorted8
|
|
|
|
|
dup >r
|
|
|
|
|
alloc
|
|
|
|
|
dup >r
|
|
|
|
|
swap
|
|
|
|
|
r@
|
|
|
|
|
while dup 0 > do
|
|
|
|
|
over c@ 3 pick swap c!
|
|
|
|
|
swap 1 + swap
|
|
|
|
|
rot 1 + -rot
|
|
|
|
|
1 -
|
|
|
|
|
end
|
|
|
|
|
drop 2drop
|
|
|
|
|
r>
|
|
|
|
|
r>
|
|
|
|
|
over >r
|
|
|
|
|
sort8
|
|
|
|
|
r>
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
#arr_sort [* | arr] -> [* | arr]
|
|
|
|
|
# Sort built-in static array in-place in ascending order.
|
|
|
|
|
word arr_sort
|
|
|
|
|
dup >r
|
|
|
|
|
dup 8 +
|
|
|
|
|
swap @
|
|
|
|
|
sort
|
|
|
|
|
r>
|
|
|
|
|
end
|
|
|
|
|
|
2026-02-18 16:05:48 +01:00
|
|
|
#arr_sorted [* | arr] -> [* | arr_sorted]
|
|
|
|
|
word arr_sorted
|
|
|
|
|
arr_clone
|
|
|
|
|
arr_sort
|
|
|
|
|
end
|
|
|
|
|
|
2026-02-19 10:42:53 +01:00
|
|
|
#dyn_arr_sort [* | dyn_arr] -> [* | dyn_arr]
|
|
|
|
|
word dyn_arr_sort
|
|
|
|
|
dup >r
|
|
|
|
|
dup arr_data
|
|
|
|
|
swap arr_len
|
|
|
|
|
sort
|
|
|
|
|
r>
|
|
|
|
|
end
|
|
|
|
|
|
2026-02-18 16:05:48 +01:00
|
|
|
#dyn_arr_sorted [* | dyn_arr] -> [* | dyn_arr_sorted]
|
|
|
|
|
word dyn_arr_sorted
|
|
|
|
|
dyn_arr_clone
|
|
|
|
|
dyn_arr_sort
|
|
|
|
|
end
|