use std::alloc::{alloc_zeroed, dealloc, Layout};
pub const HK_V2_TYPED_STRUCT: u16 = 132;
const HEADER_KIND_OFFSET: usize = 4;
#[unsafe(no_mangle)]
pub extern "C" fn jit_v2_struct_alloc(total_size: u32) -> *mut u8 {
let size = total_size as usize;
if size < 8 {
return std::ptr::null_mut();
}
let layout = match Layout::from_size_align(size, 8) {
Ok(l) => l,
Err(_) => return std::ptr::null_mut(),
};
let ptr = unsafe { alloc_zeroed(layout) };
if ptr.is_null() {
return ptr;
}
unsafe {
(ptr as *mut u32).write(1);
(ptr.add(HEADER_KIND_OFFSET) as *mut u16).write(HK_V2_TYPED_STRUCT);
}
ptr
}
#[unsafe(no_mangle)]
pub extern "C" fn jit_v2_struct_get_f64(ptr: *const u8, offset: u32) -> f64 {
unsafe { *(ptr.add(offset as usize) as *const f64) }
}
#[unsafe(no_mangle)]
pub extern "C" fn jit_v2_struct_set_f64(ptr: *mut u8, offset: u32, val: f64) {
unsafe {
*(ptr.add(offset as usize) as *mut f64) = val;
}
}
#[unsafe(no_mangle)]
pub extern "C" fn jit_v2_struct_get_i64(ptr: *const u8, offset: u32) -> i64 {
unsafe { *(ptr.add(offset as usize) as *const i64) }
}
#[unsafe(no_mangle)]
pub extern "C" fn jit_v2_struct_set_i64(ptr: *mut u8, offset: u32, val: i64) {
unsafe {
*(ptr.add(offset as usize) as *mut i64) = val;
}
}
#[unsafe(no_mangle)]
pub extern "C" fn jit_v2_struct_get_i32(ptr: *const u8, offset: u32) -> i32 {
unsafe { *(ptr.add(offset as usize) as *const i32) }
}
#[unsafe(no_mangle)]
pub extern "C" fn jit_v2_struct_set_i32(ptr: *mut u8, offset: u32, val: i32) {
unsafe {
*(ptr.add(offset as usize) as *mut i32) = val;
}
}
#[unsafe(no_mangle)]
pub extern "C" fn jit_v2_struct_get_bool(ptr: *const u8, offset: u32) -> u8 {
unsafe { *ptr.add(offset as usize) }
}
#[unsafe(no_mangle)]
pub extern "C" fn jit_v2_struct_set_bool(ptr: *mut u8, offset: u32, val: u8) {
unsafe {
*ptr.add(offset as usize) = val;
}
}
#[unsafe(no_mangle)]
pub extern "C" fn jit_v2_struct_get_ptr(ptr: *const u8, offset: u32) -> *mut u8 {
unsafe { *(ptr.add(offset as usize) as *const *mut u8) }
}
#[unsafe(no_mangle)]
pub extern "C" fn jit_v2_struct_set_ptr(ptr: *mut u8, offset: u32, val: *mut u8) {
unsafe {
*(ptr.add(offset as usize) as *mut *mut u8) = val;
}
}
#[unsafe(no_mangle)]
pub extern "C" fn jit_v2_struct_retain(ptr: *mut u8) {
if ptr.is_null() {
return;
}
unsafe {
let rc_ptr = ptr as *mut u32;
let rc = rc_ptr.read();
rc_ptr.write(rc.saturating_add(1));
}
}
#[unsafe(no_mangle)]
pub extern "C" fn jit_v2_struct_release(ptr: *mut u8, total_size: u32) {
if ptr.is_null() {
return;
}
unsafe {
let rc_ptr = ptr as *mut u32;
let rc = rc_ptr.read();
if rc <= 1 {
let layout = Layout::from_size_align_unchecked(total_size as usize, 8);
dealloc(ptr, layout);
} else {
rc_ptr.write(rc - 1);
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn jit_v2_struct_refcount(ptr: *const u8) -> u32 {
if ptr.is_null() {
return 0;
}
unsafe { (ptr as *const u32).read() }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_alloc_and_header() {
let ptr = jit_v2_struct_alloc(24);
assert!(!ptr.is_null());
assert_eq!(jit_v2_struct_refcount(ptr), 1);
let kind = unsafe { (ptr.add(HEADER_KIND_OFFSET) as *const u16).read() };
assert_eq!(kind, HK_V2_TYPED_STRUCT);
jit_v2_struct_release(ptr, 24);
}
#[test]
fn test_f64_field_access() {
let ptr = jit_v2_struct_alloc(24);
assert!(!ptr.is_null());
jit_v2_struct_set_f64(ptr, 8, 3.14);
jit_v2_struct_set_f64(ptr, 16, 2.718);
assert_eq!(jit_v2_struct_get_f64(ptr, 8), 3.14);
assert_eq!(jit_v2_struct_get_f64(ptr, 16), 2.718);
jit_v2_struct_release(ptr, 24);
}
#[test]
fn test_i64_field_access() {
let ptr = jit_v2_struct_alloc(16);
assert!(!ptr.is_null());
jit_v2_struct_set_i64(ptr, 8, -42);
assert_eq!(jit_v2_struct_get_i64(ptr, 8), -42);
jit_v2_struct_set_i64(ptr, 8, i64::MAX);
assert_eq!(jit_v2_struct_get_i64(ptr, 8), i64::MAX);
jit_v2_struct_release(ptr, 16);
}
#[test]
fn test_i32_field_access() {
let ptr = jit_v2_struct_alloc(16);
assert!(!ptr.is_null());
jit_v2_struct_set_i32(ptr, 8, 999);
assert_eq!(jit_v2_struct_get_i32(ptr, 8), 999);
jit_v2_struct_set_i32(ptr, 8, -1);
assert_eq!(jit_v2_struct_get_i32(ptr, 8), -1);
jit_v2_struct_release(ptr, 16);
}
#[test]
fn test_bool_field_access() {
let ptr = jit_v2_struct_alloc(16);
assert!(!ptr.is_null());
assert_eq!(jit_v2_struct_get_bool(ptr, 8), 0);
jit_v2_struct_set_bool(ptr, 8, 1);
assert_eq!(jit_v2_struct_get_bool(ptr, 8), 1);
jit_v2_struct_set_bool(ptr, 8, 0);
assert_eq!(jit_v2_struct_get_bool(ptr, 8), 0);
jit_v2_struct_release(ptr, 16);
}
#[test]
fn test_ptr_field_access() {
let ptr = jit_v2_struct_alloc(16);
assert!(!ptr.is_null());
assert!(jit_v2_struct_get_ptr(ptr, 8).is_null());
let inner = jit_v2_struct_alloc(16);
jit_v2_struct_set_ptr(ptr, 8, inner);
assert_eq!(jit_v2_struct_get_ptr(ptr, 8), inner);
jit_v2_struct_release(inner, 16);
jit_v2_struct_release(ptr, 16);
}
#[test]
fn test_mixed_fields() {
let ptr = jit_v2_struct_alloc(32);
assert!(!ptr.is_null());
jit_v2_struct_set_f64(ptr, 8, 1.5);
jit_v2_struct_set_i64(ptr, 16, 100);
jit_v2_struct_set_bool(ptr, 24, 1);
assert_eq!(jit_v2_struct_get_f64(ptr, 8), 1.5);
assert_eq!(jit_v2_struct_get_i64(ptr, 16), 100);
assert_eq!(jit_v2_struct_get_bool(ptr, 24), 1);
jit_v2_struct_release(ptr, 32);
}
#[test]
fn test_retain_release_refcount() {
let ptr = jit_v2_struct_alloc(16);
assert!(!ptr.is_null());
assert_eq!(jit_v2_struct_refcount(ptr), 1);
jit_v2_struct_retain(ptr);
assert_eq!(jit_v2_struct_refcount(ptr), 2);
jit_v2_struct_retain(ptr);
assert_eq!(jit_v2_struct_refcount(ptr), 3);
jit_v2_struct_release(ptr, 16);
assert_eq!(jit_v2_struct_refcount(ptr), 2);
jit_v2_struct_release(ptr, 16);
assert_eq!(jit_v2_struct_refcount(ptr), 1);
jit_v2_struct_release(ptr, 16);
}
#[test]
fn test_null_safety() {
jit_v2_struct_retain(std::ptr::null_mut());
jit_v2_struct_release(std::ptr::null_mut(), 16);
assert_eq!(jit_v2_struct_refcount(std::ptr::null()), 0);
}
#[test]
fn test_alloc_too_small() {
let ptr = jit_v2_struct_alloc(4);
assert!(ptr.is_null());
let ptr = jit_v2_struct_alloc(0);
assert!(ptr.is_null());
}
#[test]
fn test_zeroed_fields() {
let ptr = jit_v2_struct_alloc(32);
assert!(!ptr.is_null());
assert_eq!(jit_v2_struct_get_f64(ptr, 8), 0.0);
assert_eq!(jit_v2_struct_get_f64(ptr, 16), 0.0);
assert_eq!(jit_v2_struct_get_i64(ptr, 8), 0);
assert_eq!(jit_v2_struct_get_i32(ptr, 8), 0);
assert_eq!(jit_v2_struct_get_bool(ptr, 8), 0);
jit_v2_struct_release(ptr, 32);
}
#[test]
fn test_overwrite_field() {
let ptr = jit_v2_struct_alloc(16);
assert!(!ptr.is_null());
jit_v2_struct_set_f64(ptr, 8, 1.0);
assert_eq!(jit_v2_struct_get_f64(ptr, 8), 1.0);
jit_v2_struct_set_f64(ptr, 8, 2.0);
assert_eq!(jit_v2_struct_get_f64(ptr, 8), 2.0);
jit_v2_struct_release(ptr, 16);
}
}