Allocate a Perry 0.5.x heap string suitable for returning across the FFI boundary (declared as `returns: "string"` in package.json). Layout matches `StringHeader` above: 5×u32 header (utf16_len, byte_len, capacity, refcount, flags) followed by UTF-8 data. Older engine code allocated 12 bytes (Perry 0.4.x layout) and Perry's 0.5.x runtime read 8 bytes into the payload — strings came back with a ga
(s: &str)
| 43 | /// 0.5.x runtime read 8 bytes into the payload — strings came back with a |
| 44 | /// garbage prefix and read past the end. Always go through this helper. |
| 45 | pub fn alloc_perry_string(s: &str) -> *const u8 { |
| 46 | let bytes = s.as_bytes(); |
| 47 | let byte_len = bytes.len(); |
| 48 | // ASCII fast path: utf16_len == byte_len when every byte is < 0x80. |
| 49 | let utf16_len = if bytes.iter().all(|&b| b < 0x80) { |
| 50 | byte_len |
| 51 | } else { |
| 52 | s.encode_utf16().count() |
| 53 | }; |
| 54 | let total = std::mem::size_of::<StringHeader>() + byte_len; |
| 55 | let layout = std::alloc::Layout::from_size_align(total, 4).unwrap(); |
| 56 | unsafe { |
| 57 | let ptr = std::alloc::alloc(layout); |
| 58 | if ptr.is_null() { return std::ptr::null(); } |
| 59 | *(ptr as *mut u32) = utf16_len as u32; |
| 60 | *(ptr.add(4) as *mut u32) = byte_len as u32; |
| 61 | *(ptr.add(8) as *mut u32) = byte_len as u32; // capacity |
| 62 | *(ptr.add(12) as *mut u32) = 1; // refcount=unique |
| 63 | *(ptr.add(16) as *mut u32) = 0; // flags |
| 64 | std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr.add(20), byte_len); |
| 65 | ptr |
| 66 | } |
| 67 | } |
no test coverage detected