1use std::alloc::{alloc_zeroed, dealloc, Layout};
25
26pub const HK_V2_TYPED_STRUCT: u16 = 132;
29
30const HEADER_KIND_OFFSET: usize = 4;
32
33#[unsafe(no_mangle)]
43pub extern "C" fn jit_v2_struct_alloc(total_size: u32) -> *mut u8 {
44 let size = total_size as usize;
45 if size < 8 {
46 return std::ptr::null_mut();
47 }
48
49 let layout = match Layout::from_size_align(size, 8) {
50 Ok(l) => l,
51 Err(_) => return std::ptr::null_mut(),
52 };
53
54 let ptr = unsafe { alloc_zeroed(layout) };
55 if ptr.is_null() {
56 return ptr;
57 }
58
59 unsafe {
60 (ptr as *mut u32).write(1);
62 (ptr.add(HEADER_KIND_OFFSET) as *mut u16).write(HK_V2_TYPED_STRUCT);
64 }
65
66 ptr
67}
68
69#[unsafe(no_mangle)]
79pub extern "C" fn jit_v2_struct_get_f64(ptr: *const u8, offset: u32) -> f64 {
80 unsafe { *(ptr.add(offset as usize) as *const f64) }
81}
82
83#[unsafe(no_mangle)]
85pub extern "C" fn jit_v2_struct_set_f64(ptr: *mut u8, offset: u32, val: f64) {
86 unsafe {
87 *(ptr.add(offset as usize) as *mut f64) = val;
88 }
89}
90
91#[unsafe(no_mangle)]
97pub extern "C" fn jit_v2_struct_get_i64(ptr: *const u8, offset: u32) -> i64 {
98 unsafe { *(ptr.add(offset as usize) as *const i64) }
99}
100
101#[unsafe(no_mangle)]
103pub extern "C" fn jit_v2_struct_set_i64(ptr: *mut u8, offset: u32, val: i64) {
104 unsafe {
105 *(ptr.add(offset as usize) as *mut i64) = val;
106 }
107}
108
109#[unsafe(no_mangle)]
115pub extern "C" fn jit_v2_struct_get_i32(ptr: *const u8, offset: u32) -> i32 {
116 unsafe { *(ptr.add(offset as usize) as *const i32) }
117}
118
119#[unsafe(no_mangle)]
121pub extern "C" fn jit_v2_struct_set_i32(ptr: *mut u8, offset: u32, val: i32) {
122 unsafe {
123 *(ptr.add(offset as usize) as *mut i32) = val;
124 }
125}
126
127#[unsafe(no_mangle)]
133pub extern "C" fn jit_v2_struct_get_bool(ptr: *const u8, offset: u32) -> u8 {
134 unsafe { *ptr.add(offset as usize) }
135}
136
137#[unsafe(no_mangle)]
139pub extern "C" fn jit_v2_struct_set_bool(ptr: *mut u8, offset: u32, val: u8) {
140 unsafe {
141 *ptr.add(offset as usize) = val;
142 }
143}
144
145#[unsafe(no_mangle)]
151pub extern "C" fn jit_v2_struct_get_ptr(ptr: *const u8, offset: u32) -> *mut u8 {
152 unsafe { *(ptr.add(offset as usize) as *const *mut u8) }
153}
154
155#[unsafe(no_mangle)]
157pub extern "C" fn jit_v2_struct_set_ptr(ptr: *mut u8, offset: u32, val: *mut u8) {
158 unsafe {
159 *(ptr.add(offset as usize) as *mut *mut u8) = val;
160 }
161}
162
163#[unsafe(no_mangle)]
172pub extern "C" fn jit_v2_struct_retain(ptr: *mut u8) {
173 if ptr.is_null() {
174 return;
175 }
176 unsafe {
177 let rc_ptr = ptr as *mut u32;
178 let rc = rc_ptr.read();
179 rc_ptr.write(rc.saturating_add(1));
180 }
181}
182
183#[unsafe(no_mangle)]
193pub extern "C" fn jit_v2_struct_release(ptr: *mut u8, total_size: u32) {
194 if ptr.is_null() {
195 return;
196 }
197 unsafe {
198 let rc_ptr = ptr as *mut u32;
199 let rc = rc_ptr.read();
200 if rc <= 1 {
201 let layout = Layout::from_size_align_unchecked(total_size as usize, 8);
203 dealloc(ptr, layout);
204 } else {
205 rc_ptr.write(rc - 1);
206 }
207 }
208}
209
210#[unsafe(no_mangle)]
212pub extern "C" fn jit_v2_struct_refcount(ptr: *const u8) -> u32 {
213 if ptr.is_null() {
214 return 0;
215 }
216 unsafe { (ptr as *const u32).read() }
217}
218
219#[cfg(test)]
224mod tests {
225 use super::*;
226
227 #[test]
228 fn test_alloc_and_header() {
229 let ptr = jit_v2_struct_alloc(24);
231 assert!(!ptr.is_null());
232
233 assert_eq!(jit_v2_struct_refcount(ptr), 1);
235
236 let kind = unsafe { (ptr.add(HEADER_KIND_OFFSET) as *const u16).read() };
238 assert_eq!(kind, HK_V2_TYPED_STRUCT);
239
240 jit_v2_struct_release(ptr, 24);
242 }
243
244 #[test]
245 fn test_f64_field_access() {
246 let ptr = jit_v2_struct_alloc(24);
249 assert!(!ptr.is_null());
250
251 jit_v2_struct_set_f64(ptr, 8, 3.14);
253 jit_v2_struct_set_f64(ptr, 16, 2.718);
254
255 assert_eq!(jit_v2_struct_get_f64(ptr, 8), 3.14);
257 assert_eq!(jit_v2_struct_get_f64(ptr, 16), 2.718);
258
259 jit_v2_struct_release(ptr, 24);
260 }
261
262 #[test]
263 fn test_i64_field_access() {
264 let ptr = jit_v2_struct_alloc(16);
266 assert!(!ptr.is_null());
267
268 jit_v2_struct_set_i64(ptr, 8, -42);
269 assert_eq!(jit_v2_struct_get_i64(ptr, 8), -42);
270
271 jit_v2_struct_set_i64(ptr, 8, i64::MAX);
272 assert_eq!(jit_v2_struct_get_i64(ptr, 8), i64::MAX);
273
274 jit_v2_struct_release(ptr, 16);
275 }
276
277 #[test]
278 fn test_i32_field_access() {
279 let ptr = jit_v2_struct_alloc(16);
281 assert!(!ptr.is_null());
282
283 jit_v2_struct_set_i32(ptr, 8, 999);
284 assert_eq!(jit_v2_struct_get_i32(ptr, 8), 999);
285
286 jit_v2_struct_set_i32(ptr, 8, -1);
287 assert_eq!(jit_v2_struct_get_i32(ptr, 8), -1);
288
289 jit_v2_struct_release(ptr, 16);
290 }
291
292 #[test]
293 fn test_bool_field_access() {
294 let ptr = jit_v2_struct_alloc(16);
296 assert!(!ptr.is_null());
297
298 assert_eq!(jit_v2_struct_get_bool(ptr, 8), 0);
300
301 jit_v2_struct_set_bool(ptr, 8, 1);
302 assert_eq!(jit_v2_struct_get_bool(ptr, 8), 1);
303
304 jit_v2_struct_set_bool(ptr, 8, 0);
305 assert_eq!(jit_v2_struct_get_bool(ptr, 8), 0);
306
307 jit_v2_struct_release(ptr, 16);
308 }
309
310 #[test]
311 fn test_ptr_field_access() {
312 let ptr = jit_v2_struct_alloc(16);
314 assert!(!ptr.is_null());
315
316 assert!(jit_v2_struct_get_ptr(ptr, 8).is_null());
318
319 let inner = jit_v2_struct_alloc(16);
321 jit_v2_struct_set_ptr(ptr, 8, inner);
322 assert_eq!(jit_v2_struct_get_ptr(ptr, 8), inner);
323
324 jit_v2_struct_release(inner, 16);
325 jit_v2_struct_release(ptr, 16);
326 }
327
328 #[test]
329 fn test_mixed_fields() {
330 let ptr = jit_v2_struct_alloc(32);
333 assert!(!ptr.is_null());
334
335 jit_v2_struct_set_f64(ptr, 8, 1.5);
336 jit_v2_struct_set_i64(ptr, 16, 100);
337 jit_v2_struct_set_bool(ptr, 24, 1);
338
339 assert_eq!(jit_v2_struct_get_f64(ptr, 8), 1.5);
340 assert_eq!(jit_v2_struct_get_i64(ptr, 16), 100);
341 assert_eq!(jit_v2_struct_get_bool(ptr, 24), 1);
342
343 jit_v2_struct_release(ptr, 32);
344 }
345
346 #[test]
347 fn test_retain_release_refcount() {
348 let ptr = jit_v2_struct_alloc(16);
349 assert!(!ptr.is_null());
350
351 assert_eq!(jit_v2_struct_refcount(ptr), 1);
353
354 jit_v2_struct_retain(ptr);
356 assert_eq!(jit_v2_struct_refcount(ptr), 2);
357
358 jit_v2_struct_retain(ptr);
360 assert_eq!(jit_v2_struct_refcount(ptr), 3);
361
362 jit_v2_struct_release(ptr, 16);
364 assert_eq!(jit_v2_struct_refcount(ptr), 2);
365
366 jit_v2_struct_release(ptr, 16);
368 assert_eq!(jit_v2_struct_refcount(ptr), 1);
369
370 jit_v2_struct_release(ptr, 16);
372 }
374
375 #[test]
376 fn test_null_safety() {
377 jit_v2_struct_retain(std::ptr::null_mut());
379 jit_v2_struct_release(std::ptr::null_mut(), 16);
380 assert_eq!(jit_v2_struct_refcount(std::ptr::null()), 0);
381 }
382
383 #[test]
384 fn test_alloc_too_small() {
385 let ptr = jit_v2_struct_alloc(4);
387 assert!(ptr.is_null());
388
389 let ptr = jit_v2_struct_alloc(0);
390 assert!(ptr.is_null());
391 }
392
393 #[test]
394 fn test_zeroed_fields() {
395 let ptr = jit_v2_struct_alloc(32);
397 assert!(!ptr.is_null());
398
399 assert_eq!(jit_v2_struct_get_f64(ptr, 8), 0.0);
401 assert_eq!(jit_v2_struct_get_f64(ptr, 16), 0.0);
402 assert_eq!(jit_v2_struct_get_i64(ptr, 8), 0);
403 assert_eq!(jit_v2_struct_get_i32(ptr, 8), 0);
404 assert_eq!(jit_v2_struct_get_bool(ptr, 8), 0);
405
406 jit_v2_struct_release(ptr, 32);
407 }
408
409 #[test]
410 fn test_overwrite_field() {
411 let ptr = jit_v2_struct_alloc(16);
412 assert!(!ptr.is_null());
413
414 jit_v2_struct_set_f64(ptr, 8, 1.0);
415 assert_eq!(jit_v2_struct_get_f64(ptr, 8), 1.0);
416
417 jit_v2_struct_set_f64(ptr, 8, 2.0);
419 assert_eq!(jit_v2_struct_get_f64(ptr, 8), 2.0);
420
421 jit_v2_struct_release(ptr, 16);
422 }
423}