Compare commits

..

No commits in common. "d47c933c047ecec3d4dfa13ea6ef908dc414cc98" and "81417de6cadedb0ddc9cc19313cb267833682c4f" have entirely different histories.

6 changed files with 179 additions and 687 deletions

View file

@ -1,45 +1,21 @@
# Makefile: Compile and link main.asm using nasm and mold, intermediate files in target/
TARGET_DIR := target
SRC := src/lib.asm src/int_to_str.asm src/vec.asm
OBJ := $(patsubst src/%.asm,$(TARGET_DIR)/%.o,$(SRC))
BIN_SRC := src/main.asm src/panic.asm
BIN_OBJ := $(patsubst src/%.asm,$(TARGET_DIR)/%.o,$(BIN_SRC))
SRC := src/main.asm src/lib.asm src/int_to_str.asm
OBJ := $(addprefix $(TARGET_DIR)/, $(notdir $(SRC:.asm=.o)))
BIN := $(TARGET_DIR)/main
TEST_SRCS := $(wildcard tests/*.rs)
TEST_BINS := $(patsubst tests/%.rs,$(TARGET_DIR)/tests/%,$(TEST_SRCS))
OBJ_LINK_ARGS := $(foreach o,$(OBJ),-C link-arg=$(o))
.PHONY: all clean
all: $(BIN)
test-bins: $(TEST_BINS)
test: test-bins
@echo "Running all tests.."
@for b in $(TEST_BINS); do \
echo "==> Running $$b"; \
"$$b" || exit $$?; \
done
# pattern rule: compile each .rs into a binary with the same base name
$(TARGET_DIR)/tests/%: tests/%.rs | $(OBJ) $(TARGET_DIR)/tests
@echo "[$(RUSTC)] $< -> $@"
rustc -Clink-arg=-fuse-ld=mold $(OBJ_LINK_ARGS) -g -o $@ $<
$(TARGET_DIR):
mkdir -p $(TARGET_DIR)/tests
$(TARGET_DIR)/tests: $(TARGET_DIR)
mkdir -p $(TARGET_DIR)/tests
mkdir -p $(TARGET_DIR)
$(TARGET_DIR)/%.o: src/%.asm | $(TARGET_DIR)
nasm -f elf64 -g $< -o $@
$(BIN): $(OBJ) $(BIN_OBJ)
$(BIN): $(OBJ)
mold -run ld -o $(BIN) $(OBJ)
run: $(BIN)

View file

@ -1,44 +1,50 @@
default rel
section .rdata
panic_msg db "panic occured!", 10
panic_msg_len equ $ - panic_msg
oom_msg db "oom!", 10
oom_msg db "panic: oom!", 10
oom_msg_len equ $ - oom_msg
file_error_msg db "Could not open file: "
file_error_msg_len equ $ - file_error_msg
error_msg db "Error: "
error_msg_len equ $ - error_msg
section .text
global oom
global panic
global strlen
global strcmp
global strchr
global streq
global memcpy
global memswap
global eprint_str
global exit
global error_to_str
global eprint_error
global alloc_pages
global allocate
global is_alpha
global is_numeric
global is_id_continue
global is_id_start
extern panic
;; ==============================
;; Helper functions
;; ==============================
;; Abort the program with a default panic message
panic:
mov rcx, panic_msg
mov rdx, panic_msg_len
call eprint_str
; exit with error code 1
mov rax, 60 ; syscall: exit
mov rdi, 1 ; status: 1
syscall
;; Abort the program with a default panic message
oom:
lea rdi, [oom_msg]
lea rsi, [oom_msg_len]
mov rcx, oom_msg
mov rdx, oom_msg_len
call eprint_str
; exit with error code 1
mov rax, 60 ; syscall: exit
@ -46,28 +52,28 @@ oom:
syscall
;; abort the program
;; rdi: status code
;; rcx: status code
exit:
mov rax, 60 ; syscall: exit
mov rdi, rcx
syscall
;; Writes a string to stderr:
;; rdi: pointer to string
;; rsi: length of string
;; rcx: pointer to string
;; rdx: length of string
eprint_str:
mov rax, 1 ; syscall: write
mov rdx, rsi ; len: length
mov rsi, rdi ; buf: str
mov rdi, 2 ; fd: stderr
mov rsi, rcx ; buf: str
syscall
ret
;; calculates length of null-terminated string
;; rdi: pointer to string
;; rcx: pointer to string
strlen:
xor rax, rax ; length counter
.strlen_loop:
cmp byte [rdi + rax], 0
cmp byte [rcx + rax], 0
je .strlen_done
inc rax
jmp .strlen_loop
@ -75,173 +81,109 @@ strlen:
ret
;; Checks two byte slices for equality
;; rdi: pointer to first slice
;; rsi: length of first slice
;; rdx: pointer to second slice
;; rcx: length of second slice
;; rcx: pointer to first slice
;; rdx: length of first slice
;; r8: pointer to second slice
;; r9: length of second slice
;; returns: 1 if equal, 0 if not equal
;; fn streq(a: &[u8], b: &[u8]) -> bool
streq:
; if a.len() == b.len() {
cmp rsi, rcx
jne .false
; for i in 0..a.len() {
xor r8, r8
cmp rdx, r9
jne .not_equal ; lengths differ
xor r10, r10 ; index = 0
.loop:
cmp r8, rsi
jge .true
; if a[i] != b[i] {
mov al, [rdi + r8]
mov cl, [rdx + r8]
cmp al, cl
; return false;
jne .false
; }
inc r8
cmp r10, rdx
jge .equal ; done all bytes
mov al, [rcx + r10] ; char from a
mov bl, [r8 + r10] ; char from b
cmp al, bl
jne .not_equal ; chars differ
inc r10
jmp .loop
; }
; return true;
.true:
mov rax, 1
.equal:
mov rax, 1 ; equal
ret
; } else {
; return false;
.false:
xor rax, rax
.not_equal:
xor rax, rax ; not equal
ret
; }
;; Compares two byte slices
;; rdi: pointer to first slice
;; rsi: length of first slice
;; rdx: pointer to second slice
;; rcx: length of second slice
;; returns: -1, 0, or 1 in rax
;; fn strcmp(a: &[u8], b: &[u8]) -> Ordering
;; rcx: pointer to first slice
;; rdx: length of first slice
;; r8: pointer to second slice
;; r9: length of second slice
; returns: -1, 0, or 1 in rax
strcmp:
; let min_len = min(a.len(), b.len());
mov rax, rsi
cmp rsi, rcx
cmovg rax, rcx
; for i in 0..min_len {
xor r8, r8
xor rax, rax ; result = 0 (assume equal)
test rdx, rdx
jz .check_empty_b ; if len a == 0
test r9, r9
jz .check_empty_a ; if len b == 0
mov r10, rdx ; min(len a, len b) in r10
cmp r9, rdx
cmovb r10, r9
xor r11, r11 ; index = 0
.loop:
cmp r8, rax
jge .length_check
mov r9b, [rdi + r8]
mov r10b, [rdx + r8]
; if a[i] < b[i] {
cmp r9b, r10b
; return Ordering::Less;
jb .less
; } else if a[i] > b[i] {
; return Ordering::Greater;
ja .greater
; }
inc r8
cmp r11, r10
jge .after_loop ; done min(len a, len b) bytes
mov al, [rcx + r11] ; char from a
mov bl, [r8 + r11] ; char from b
cmp al, bl
jb .less ; if al < bl: return -1
ja .greater ; if al > bl: return 1
inc r11
jmp .loop
; }
; if a.len() < b.len() {
.length_check:
cmp rsi, rcx
; return Ordering::Less;
jb .less
; } else if a.len() > b.len() {
; return Ordering::Greater;
ja .greater
; } else {
; return Ordering::Equal;
xor rax, rax
.after_loop:
cmp rdx, r9
je .equal ; lengths equal, strings equal
jb .less ; a shorter than b
ja .greater ; a longer than b
.equal:
xor rax, rax ; 0
ret
; }
.less:
mov rax, -1
ret
.greater:
mov rax, 1
ret
.check_empty_a:
test rdx, rdx
jz .equal ; both empty
jmp .greater ; a not empty, b empty
.check_empty_b:
test r9, r9
jz .equal ; both empty
jmp .less ; b not empty, a empty
;; Copy bytes from one memory location to another
;; rdi: destination pointer
;; rsi: source pointer
;; rdx: number of bytes to copy
;; fn memcpy(dest: *mut u8, src: *const u8, n: usize)
;; rcx: destination pointer
;; rdx: source pointer
;; r8: number of bytes to copy
memcpy:
; for i in 0..n {
xor r8, r8
.loop:
cmp r8, rdx
jge .done
; dest[i] = src[i];
mov al, [rsi + r8]
mov [rdi + r8], al
inc r8
jmp .loop
; }
.done:
ret
;; Swap bytes between two memory locations
;; rdi: pointer to first memory location
;; rsi: pointer to second memory location
;; rdx: number of bytes to swap
;; fn memswap(a: *mut u8, b: *mut u8, n: usize)
memswap:
; for i in 0..n {
xor r8, r8
.loop:
cmp r8, rdx
jge .done
; let temp = a[i];
mov al, [rdi + r8]
; a[i] = b[i];
mov cl, [rsi + r8]
mov [rdi + r8], cl
; b[i] = temp;
mov [rsi + r8], al
inc r8
jmp .loop
; }
.done:
ret
;; Returns a pointer to the first occurrence of c in s, or null if not found
;; rdi: pointer to byte slice
;; rsi: length of byte slice
;; dl: byte to find
;; fn strchr(s: &[u8], c: u8) -> &[u8]
strchr:
; if s.len() == 0 {
test rsi, rsi
; return &[];
je .null
; } else {
xor r8, r8
; for i in 0..s.len() {
.loop:
cmp r8, rsi
jge .null
; if s[i] == c {
mov al, [rdi + r8]
cmp al, dl
; return &s[i..];
je .found
; }
inc r8
; }
jmp .loop
; }
.null:
xor rax, rax
xor rdx, rdx
ret
.found:
add rdi, r8
mov rax, rdi
mov rdx, rsi
sub rdx, r8
xor r10, r10
.memcpy_loop_byte:
cmp r10, r8
jge .memcpy_done
mov al, [rdx + r10]
mov [rcx + r10], al
inc r10
jmp .memcpy_loop_byte
.memcpy_done:
ret
section .rdata
@ -256,16 +198,16 @@ section .rdata
section .text
;; Converts an error code to a str (pointer, length) pair
;; rdi: error code
;; rcx: error code
;; Returns:
;; rax: pointer to string
;; rdx: length of string
error_to_str:
cmp rdi, -21
cmp rcx, -21
je .e_is_dir
cmp rdi, -5
cmp rcx, -5
je .e_io
cmp rdi, -9
cmp rcx, -9
je .e_bad_fd
; unknown error
@ -285,74 +227,92 @@ error_to_str:
mov rdx, e_bad_fd_len
ret
;; rdi: error code
;; fn eprint_error(err_code: isize)
;; rcx: error code
eprint_error:
; let err_code = err_code;
push rdi
; eprint_str(ERROR_STR, ERROR_STR.len());
lea rdi, [error_msg]
lea rsi, [error_msg_len]
call eprint_str
; let (err, len) = error_to_str(err_code);
pop rdi
; prologue
push rsi
; get error string
call error_to_str
; eprint_str(err, len);
mov rdi, rax
mov rsi, rdx
mov r12, rax ; r12 = pointer to error string
mov r13, rdx ; r13 = length of error string
mov rsi, r13
add rsi, error_msg_len
add rsi, 1
add rsi, 15
and rsi, -16 ; align up to 16
sub rsp, rsi ; allocate buffer
push rsi ; save allocation size
; copy error_msg
lea rcx, [rsp + 8]
mov rdx, error_msg
mov r8, error_msg_len
call memcpy
; copy error string
lea rcx, [rsp + 8 + error_msg_len]
mov rdx, r12
mov r8, r13
call memcpy
; trailing newline
lea rdx, [rsp + 8 + error_msg_len + r13]
mov byte [rdx], 10
; print error message
lea rcx, [rsp + 8]
mov rdx, error_msg_len
add rdx, r13
add rdx, 1 ; include newline
call eprint_str
pop rsi
add rsp, rsi ; dealloc
; epilogue
pop rsi
ret
;; rdi: number of bytes to allocate
;; fn allocate(bytes: usize) -> *mut u8
allocate:
;; Allocates n pages of memory
;; rcx: number of pages
;; Returns:
;; rax: pointer to allocated memory
alloc_pages:
mov rax, 9 ; syscall: mmap
mov rsi, rdi ; length: number of bytes
xor rdi, rdi ; addr: NULL
mov rsi, rcx ; length: number of pages
shl rsi, 12 ; length in bytes (page size = 4096)
mov rdx, 3 ; prot: PROT_READ | PROT_WRITE
mov r10, 34 ; flags: MAP_PRIVATE | MAP_ANONYMOUS
mov r8, -1 ; fd: -1
xor r9, r9 ; offset: 0
syscall
cmp rax, -1 ; check for error
cmp rax, -4095 ; check for error
jae .alloc_error
ret
.alloc_error:
mov rdi, rax
mov rcx, rax ; error code
call eprint_error
call oom
;; Returns 1 if cl is an ASCII alphabetic character, 0 otherwise
;; rdi: byte to check
;; fn is_alpha(c: u8) -> bool
;; cl: byte to check
is_alpha:
; if ('A' <= c
cmp dil, 'A'
jb .false
; && c <= 'Z')
cmp dil, 'Z'
jbe .true
; || ('a' <= c
cmp dil, 'a'
jb .false
; && c <= 'z') {
cmp dil, 'z'
jbe .true
; return true;
.true:
cmp cl, 'A'
jb .not_alpha
cmp cl, 'Z'
jbe .is_alpha_ret
cmp cl, 'a'
jb .not_alpha
cmp cl, 'z'
jbe .is_alpha_ret
.is_alpha_ret:
mov rax, 1
ret
; } else {
; return false;
.false:
.not_alpha:
xor rax, rax
ret
;; check if dil is numeric (decimal)
is_numeric:
cmp dil, '0'
cmp cl, '0'
jb .not_numeric
cmp dil, '9'
cmp cl, '9'
jbe .is_numeric_ret
.is_numeric_ret:
mov rax, 1
@ -361,13 +321,13 @@ is_numeric:
xor rax, rax
ret
;; dil: byte to check
;; cl: byte to check
is_id_continue:
call is_alpha
test rax, rax
cmp rax, 1
je .is_id_continue_ret
call is_numeric
test rax, rax
cmp rax, 1
je .is_id_continue_ret
cmp cl, '_'
je .is_id_continue_ret
@ -377,10 +337,10 @@ is_id_continue:
mov rax, 1
ret
;; dil: byte to check
;; cl: byte to check
is_id_start:
call is_alpha
test rax, rax
cmp rax, 1
je .is_ret
cmp cl, '_'
je .is_ret

View file

@ -12,35 +12,21 @@ extern exit
extern error_to_str
extern eprint_error
extern alloc_pages
extern allocate
extern is_alpha
extern is_numeric
extern is_id_continue
extern is_id_start
extern vec_tests
section .data
hello_msg db "Hello, World!", 10
hello_msg_len equ $ - hello_msg
file_error_msg db "Could not open file: "
file_error_msg_len equ $ - file_error_msg
test_success db "All tests passed!", 10
test_success_len equ $ - test_success
section .text
global _start
_start:
mov rdi, test_success
mov rsi, test_success_len
call eprint_str
mov rdi, 0
call exit
compiler_entry:
; get filename from argv[1]
; argv is at rsp + 8
; check if argc > 1
@ -172,7 +158,9 @@ tokeniser_init:
; allocate buffer
mov rcx, r15
call allocate
add rcx, 4095
shr rcx, 12 ; divide by 4096
call alloc_pages
mov [buffer], rax
mov [buffer_len], r15

View file

@ -1,19 +0,0 @@
default rel
section .rdata
panic_msg db "panic occured!", 10
panic_msg_len equ $ - panic_msg
extern eprint_str
section .text
global panic
;; Abort the program with a default panic message
panic:
lea rdi, [panic_msg]
lea rsi, [panic_msg_len]
call eprint_str
; exit with error code 1
mov rax, 60 ; syscall: exit
mov rdi, 1 ; status: 1
syscall

View file

@ -1,338 +0,0 @@
section .rdata
INIT_CAPACITY equ 4096
section .text
extern memcpy
extern memswap
extern oom
extern panic
extern eprint_str
extern allocate
global vec_init
global vec_push
global vec_pop
global vec_swap
global vec_remove
global vec_get
global vec_drop
global vec_tests
;; Byte vector structure
;; struct Vec {
;; data: *mut u8,
;; len: usize,
;; capacity: usize,
;; item_size: usize,
;; drop: Option<fn(*mut u8)>,
;; }
;; size: 40 bytes
;; align: 8 bytes
;; Initialize a new vector
;; rdi: pointer to Vec struct
;; rsi: item_size
;; rdx: drop function pointer
;; fn vec_init(vec: *mut Vec, item_size: usize, drop: Option<fn(*mut u8)>)
vec_init:
push rbp
mov rbp, rsp
push rdx
push rsi
push rdi
; (*vec).data = allocate(INIT_CAPACITY);
mov rdi, INIT_CAPACITY
call allocate
pop r15
mov [r15], rax
; (*vec).len = 0;
mov qword [r15 + 8], 0
pop rsi
mov qword [r15 + 24], rsi ; item_size
; (*vec).capacity = INIT_CAPACITY / item_size;
mov rax, INIT_CAPACITY
xor rdx, rdx
div rsi
mov qword [r15 + 16], rax
pop rdx
mov qword [r15 + 32], rdx ; drop
pop rbp
ret
;; rdi: pointer to Vec struct
;; rsi: index
;; fn vec_get(vec: *mut Vec, index: usize) -> *mut u8
vec_get:
push rbp
mov rbp, rsp
; if (index >= vec.len) panic();
mov rax, [rdi + 8] ; len
cmp rsi, rax
jge .panic
; return &mut vec.data[index * vec.item_size];
mov rax, [rdi + 24] ; item_size
mul rsi ; index * item_size
mov rsi, [rdi] ; data
add rax, rsi ; data + index * item_size
pop rbp
ret
.panic:
call panic
pop rbp
ret
;; rdi: pointer to Vec struct
;; rsi: pointer to data to push
;; rdx: size of data to push
;; fn vec_push(vec: *mut Vec, data: &[u8])
vec_push:
push rbp
mov rbp, rsp
; if data.len() != vec.item_size panic();
mov rax, [rdi + 24]
cmp rdx, rax
jne .panic
; let vec, ptr, size;
sub rsp, 24
mov [rsp], rdi ; save vec
mov [rsp + 8], rsi ; save data ptr
mov [rsp + 16], rdx ; save data size
; vec_try_grow(vec, size);
call vec_try_grow
; memcpy(&vec.data[vec.len], data, size);
mov rax, [rsp] ; vec
mov rcx, [rax] ; vec.data
mov rdi, [rax + 8] ; vec.len
mov rax, [rax + 24] ; vec.item_size
mul rdi ; len * item_size
add rax, rcx ; data + len * item_size
mov rdi, rax ; dest ptr
mov rsi, [rsp + 8] ; data ptr
mov rdx, [rsp + 16] ; data size
call memcpy
; vec.len += 1;
mov rax, [rsp] ; vec
add qword [rax + 8], 1
add rsp, 24
pop rbp
ret
.panic:
call panic
pop rbp
ret
;; rdi: pointer to Vec struct
;; fn vec_pop(vec: *mut Vec)
vec_pop:
push rbp
mov rbp, rsp
; if vec.len == 0 panic();
mov rax, [rdi + 8] ; len
test rax, rax
je .underflow
; vec.len -= 1;
sub rax, 1
mov [rdi + 8], rax
; if let Some(drop) = vec.drop {
mov r8, [rdi + 32]
test r8, r8
je .ret
; drop(&mut vec.data[vec.len..vec.len + vec.item_size]);
mul qword [rdi + 24] ; len * item_size
mov rsi, [rdi] ; data
add rax, rsi ; data + len * item_size
mov rdi, rax
call r8
.ret:
pop rbp
ret
; }
.underflow:
call panic
pop rbp
ret
;; rdi: pointer to Vec struct
;; rsi: index a
;; rdx: index b
;; fn vec_swap(vec: *mut Vec, a: usize, b: usize)
vec_swap:
push rbp
mov rbp, rsp
push rbx
; let vec, a, b;
sub rsp, 24
mov [rsp], rdi
; let max_index = max(a, b);
mov rbx, rsi
cmp rbx, rdx
cmovl rbx, rdx
; if (max_index >= vec.len) panic();
mov rax, [rdi + 8] ; len
cmp rbx, rax
jge .panic
; a = a * item_size;
mov rax, [rdi + 24] ; item_size
mul rbx ; b * item_size
mov [rsp + 16], rax
; b = b * item_size;
mov rax, rsi
mul rbx ; a * item_size
mov [rsp + 8], rax
; // SAFETY: a and b must not overlap
; memswap(&mut vec.data.add(a), &mut vec.data.add(b), vec.item_size);
mov rcx, [rdi + 24] ; item_size
lea rdi, [rdi] ; vec.data
mov rsi, rdi
add rdi, [rsp + 16] ; data + a
add rsi, [rsp + 24] ; data + b
call memswap
add rsp, 24
pop rbx
pop rbp
ret
.panic:
call panic
pop rbx
ret
;; rdi: pointer to Vec struct
;; rsi: index
;; fn vec_remove(vec: *mut Vec, index: usize)
vec_remove:
push rbp
mov rbp, rsp
; if (index >= vec.len) panic();
mov rax, [rdi + 8] ; len
cmp rsi, rax
jge .panic
; if (vec.len - 1 != index {
sub rax, 1
cmp rsi, rax
je .remove_last
; vec_swap(vec, index, vec.len - 1);
mov rdx, rax ; len - 1
call vec_swap
; }
; vec.len -= 1;
.remove_last:
mov rax, [rdi + 8] ; len
sub rax, 1
mov [rdi + 8], rax
; if let Some(drop) = vec.drop {
mov r8, [rdi + 32]
test r8, r8
je .ret
; drop(&mut vec.data[vec.len..vec.len + vec.item_size]);
mul qword [rdi + 24] ; len * item_size
lea rdi, [rdi] ; data
add rdi, rax ; data + len * item_size
call r8
; }
.ret:
pop rbp
ret
.panic:
call panic
pop rbp
ret
;; rdi: pointer to Vec struct
;; fn vec_try_grow(vec: *mut Vec, push_size: usize) -> bool
vec_try_grow:
push rbp
mov rbp, rsp
push rbx
mov rax, [rdi + 16] ; capacity
mov rbx, [rdi + 8] ; len
cmp rbx, rax
jg .grow
pop rbx
xor rax, rax
pop rbp
ret
.grow:
; let vec;
; let new_capacity;
; let new_data;
sub rsp, 24
mov [rsp], rdi
; new_capacity = max(capacity * 2, len + 1)
mov rcx, [rdi + 16] ; capacity
shl rcx, 1 ; capacity * 2
cmp rcx, rbx
cmovl rcx, rbx ; rcx = new_capacity
mov [rsp + 8], rcx
mov rax, rcx
mul qword [rdi + 24] ; new_capacity * item_size
; new_data = allocate(new_capacity);
mov rdi, rax
call allocate
mov [rsp + 16], rax
mov rdi, [rsp]
; memcpy(new_data, old_data, len * vec.item_size);
mov rax, [rdi + 8] ; len
mul qword [rdi + 24] ; len * item_size
mov rdx, rax ; len
mov rsi, [rdi] ; old_data
mov rdi, [rsp + 16] ; new_data
call memcpy
; free(old_data) // TODO
; update vec
mov rdi, [rsp]
mov rax, [rsp + 16]
mov [rdi], rax ; data = new_data
mov rcx, [rsp + 8]
mov [rdi + 16], rcx ; capacity = new_capacity
add rsp, 24
pop rbx
xor rax, rax
pop rbp
ret
;; rdi: pointer to Vec struct
vec_drop:
push rbp
mov rbp, rsp
push r12
push r13
push r14
push rdi
; if let Some(drop) = vec.drop {
mov r12, [rdi + 32]
test r12, r12
je .ret
; for i in 0..vec.len {
xor r13, r13
mov r14, [rdi + 8] ; len
.loop:
cmp r13, r14
jge .ret
mov rdi, [rsp]
; drop(&mut vec.data[i * vec.item_size..(i + 1) * vec.item_size]);
mov rax, r13
mul qword [rdi + 24] ; i * item_size
mov rdi, [rdi] ; data
add rdi, rax
call r12
inc r13
jmp .loop
; }
; }
; free(vec.data);
.ret:
pop rdi
pop r14
pop r13
pop r12
pop rbp
ret

View file

@ -1,75 +0,0 @@
#[repr(C)]
pub struct BlobVec {
pub data: *mut u8,
pub len: usize,
pub cap: usize,
pub elem_size: usize,
pub drop: Option<extern "C" fn(*mut u8)>,
}
#[no_mangle]
extern "C" fn panic() -> ! {
panic!("Called panic from external code.");
}
unsafe impl Send for BlobVec {}
unsafe impl Sync for BlobVec {}
unsafe extern "C" {
unsafe fn vec_init(vec: *mut BlobVec, elem_size: usize, drop: Option<extern "C" fn(*mut u8)>);
unsafe fn vec_push(vec: *mut BlobVec, elem: *const u8, size: usize);
unsafe fn vec_pop(vec: *mut BlobVec);
unsafe fn vec_get(vec: *mut BlobVec, index: usize) -> *mut u8;
unsafe fn vec_remove(vec: *mut BlobVec, index: usize);
unsafe fn vec_drop(vec: *mut BlobVec);
unsafe fn exit(code: i32) -> !;
}
fn main() {
static mut DROPS: usize = 1;
let mut vec = BlobVec {
data: core::ptr::null_mut(),
len: 0,
cap: 0,
elem_size: 1,
drop: None,
};
fn as_slice<T>(vec: &BlobVec) -> &[T] {
assert_eq!(vec.elem_size, core::mem::size_of::<T>());
unsafe { core::slice::from_raw_parts(vec.data as *const T, vec.len) }
}
extern "C" fn drop(ptr: *mut u8) {
unsafe {
DROPS *= ptr.cast::<u32>().read() as usize;
}
}
unsafe {
vec_init(&mut vec, 4, Some(drop));
let mut value: u32 = 2;
assert_eq!(vec.len, 0);
vec_push(&mut vec, &value as *const u32 as *const u8, 4);
assert_eq!(vec.len, 1);
assert_eq!(as_slice::<u32>(&vec), &[2]);
let retrieved = *(vec_get(&mut vec, 0) as *mut u32);
assert_eq!(retrieved, 2);
assert_eq!(DROPS, 1);
vec_pop(&mut vec);
assert_eq!(vec.len, 0);
assert_eq!(DROPS, 2);
value = 3;
vec_push(&mut vec, &value as *const u32 as *const u8, 4);
assert_eq!(as_slice::<u32>(&vec), &[3]);
value = 5;
vec_push(&mut vec, &value as *const u32 as *const u8, 4);
assert_eq!(as_slice::<u32>(&vec), &[3, 5]);
assert_eq!(vec.len, 2);
vec_pop(&mut vec);
vec_pop(&mut vec);
assert_eq!(DROPS, 2 * 3 * 5);
eprintln!("Push/pop test passed\n");
}
}