(path_ptr: *const u8)
| 2020 | |
| 2021 | #[no_mangle] |
| 2022 | pub extern "C" fn bloom_read_file(path_ptr: *const u8) -> *const u8 { |
| 2023 | let path = str_from_header(path_ptr); |
| 2024 | match std::fs::read_to_string(resolve_path(path)) { |
| 2025 | Ok(contents) => { |
| 2026 | // Return Perry-format string: StringHeader (length u32 + capacity u32 + refcount u32) followed by UTF-8 data |
| 2027 | let bytes = contents.as_bytes(); |
| 2028 | let len = bytes.len(); |
| 2029 | let total = 12 + len; // 12 bytes header (3 × u32) + data |
| 2030 | let layout = std::alloc::Layout::from_size_align(total, 4).unwrap(); |
| 2031 | unsafe { |
| 2032 | let ptr = std::alloc::alloc(layout); |
| 2033 | if ptr.is_null() { return std::ptr::null(); } |
| 2034 | *(ptr as *mut u32) = len as u32; // length |
| 2035 | *(ptr.add(4) as *mut u32) = len as u32; // capacity |
| 2036 | *(ptr.add(8) as *mut u32) = 1; // refcount (unique) |
| 2037 | std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr.add(12), len); |
| 2038 | ptr |
| 2039 | } |
| 2040 | } |
| 2041 | // A null pointer would NaN-box into a string-typed JS value pointing at |
| 2042 | // address 0; `.length`/`.charCodeAt` then dereference the header at a |
| 2043 | // negative offset and segfault. Return a valid empty Perry string so |
| 2044 | // callers that probe via `data.length === 0` (e.g. level discovery) |
| 2045 | // are safe. Mirrors the macOS native crate. |
| 2046 | Err(_) => alloc_perry_string(""), |
| 2047 | } |
| 2048 | } |
| 2049 | |
| 2050 | #[no_mangle] |
| 2051 | pub extern "C" fn bloom_get_time() -> f64 { |
no test coverage detected