1use crate::heap_value::{HeapKind, HeapValue};
23
24pub const FLAG_MARKED: u8 = 0b0000_0001;
26pub const FLAG_PINNED: u8 = 0b0000_0010;
28pub const FLAG_READONLY: u8 = 0b0000_0100;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37#[repr(C, align(16))]
38pub struct HeapHeader {
39 pub kind: u16,
41 pub elem_type: u8,
45 pub flags: u8,
47 pub len: u32,
49 pub cap: u32,
51 _pad: u32,
53 pub aux: u64,
61 _reserved: u64,
63}
64
65const _: () = {
67 assert!(std::mem::size_of::<HeapHeader>() == 32);
68 assert!(std::mem::align_of::<HeapHeader>() == 16);
69};
70
71pub mod elem_types {
73 pub const UNTYPED: u8 = 0;
75 pub const F64: u8 = 1;
77 pub const I64: u8 = 2;
79 pub const STRING: u8 = 3;
81 pub const BOOL: u8 = 4;
83 pub const TYPED_OBJECT: u8 = 5;
85}
86
87impl HeapHeader {
88 #[inline]
90 pub fn new(kind: HeapKind) -> Self {
91 Self {
92 kind: kind as u16,
93 elem_type: 0,
94 flags: 0,
95 len: 0,
96 cap: 0,
97 _pad: 0,
98 aux: 0,
99 _reserved: 0,
100 }
101 }
102
103 #[inline]
105 pub fn with_len_aux(kind: HeapKind, len: u32, aux: u64) -> Self {
106 Self {
107 kind: kind as u16,
108 elem_type: 0,
109 flags: 0,
110 len,
111 cap: 0,
112 _pad: 0,
113 aux,
114 _reserved: 0,
115 }
116 }
117
118 pub fn from_heap_value(value: &HeapValue) -> Self {
122 let kind = value.kind();
123 let mut header = Self::new(kind);
124
125 match value {
126 HeapValue::String(s) => {
127 header.len = s.len() as u32;
128 }
129 HeapValue::Array(arr) => {
130 header.len = arr.len() as u32;
131 header.cap = arr.len() as u32;
132 }
133 HeapValue::TypedObject {
134 schema_id, slots, ..
135 } => {
136 header.len = slots.len() as u32;
137 header.aux = *schema_id;
138 }
139 HeapValue::Closure {
140 function_id,
141 upvalues,
142 } => {
143 header.len = upvalues.len() as u32;
144 header.aux = *function_id as u64;
145 }
146 HeapValue::DataTable(dt) => {
147 header.len = dt.row_count() as u32;
148 }
149 HeapValue::TypedTable { schema_id, table } => {
150 header.len = table.row_count() as u32;
151 header.aux = *schema_id;
152 }
153 HeapValue::RowView {
154 schema_id, row_idx, ..
155 } => {
156 header.len = 1;
157 header.aux = *schema_id;
158 header.cap = *row_idx as u32;
160 }
161 HeapValue::ColumnRef {
162 schema_id, col_id, ..
163 } => {
164 header.aux = *schema_id;
165 header.cap = *col_id;
166 }
167 HeapValue::IndexedTable {
168 schema_id,
169 table,
170 index_col,
171 } => {
172 header.len = table.row_count() as u32;
173 header.aux = *schema_id;
174 header.cap = *index_col;
175 }
176 HeapValue::Enum(_) => {
177 }
179 HeapValue::Future(id) => {
180 header.aux = *id;
181 }
182 HeapValue::TaskGroup { kind, task_ids } => {
183 header.elem_type = *kind;
184 header.len = task_ids.len() as u32;
185 }
186 _ => {}
188 }
189
190 header
191 }
192
193 #[inline]
195 pub fn heap_kind(&self) -> Option<HeapKind> {
196 HeapKind::from_u16(self.kind)
197 }
198
199 #[inline]
201 pub fn has_flag(&self, flag: u8) -> bool {
202 self.flags & flag != 0
203 }
204
205 #[inline]
207 pub fn set_flag(&mut self, flag: u8) {
208 self.flags |= flag;
209 }
210
211 #[inline]
213 pub fn clear_flag(&mut self, flag: u8) {
214 self.flags &= !flag;
215 }
216
217 pub const OFFSET_KIND: usize = 0;
219 pub const OFFSET_ELEM_TYPE: usize = 2;
221 pub const OFFSET_FLAGS: usize = 3;
223 pub const OFFSET_LEN: usize = 4;
225 pub const OFFSET_CAP: usize = 8;
227 pub const OFFSET_AUX: usize = 16;
229}
230
231impl HeapKind {
232 #[inline]
234 pub fn from_u16(v: u16) -> Option<Self> {
235 if v <= HeapKind::F32Array as u16 {
236 Some(unsafe { std::mem::transmute(v as u8) })
239 } else {
240 None
241 }
242 }
243
244 #[inline]
246 pub fn from_u8(v: u8) -> Option<Self> {
247 Self::from_u16(v as u16)
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254
255 #[test]
256 fn test_header_size_and_alignment() {
257 assert_eq!(std::mem::size_of::<HeapHeader>(), 32);
258 assert_eq!(std::mem::align_of::<HeapHeader>(), 16);
259 }
260
261 #[test]
262 fn test_header_field_offsets() {
263 assert_eq!(HeapHeader::OFFSET_KIND, 0);
265 assert_eq!(HeapHeader::OFFSET_ELEM_TYPE, 2);
266 assert_eq!(HeapHeader::OFFSET_FLAGS, 3);
267 assert_eq!(HeapHeader::OFFSET_LEN, 4);
268 assert_eq!(HeapHeader::OFFSET_CAP, 8);
269 assert_eq!(HeapHeader::OFFSET_AUX, 16);
270
271 let h = HeapHeader::new(HeapKind::String);
273 let base = &h as *const _ as usize;
274 assert_eq!(&h.kind as *const _ as usize - base, HeapHeader::OFFSET_KIND);
275 assert_eq!(
276 &h.elem_type as *const _ as usize - base,
277 HeapHeader::OFFSET_ELEM_TYPE
278 );
279 assert_eq!(
280 &h.flags as *const _ as usize - base,
281 HeapHeader::OFFSET_FLAGS
282 );
283 assert_eq!(&h.len as *const _ as usize - base, HeapHeader::OFFSET_LEN);
284 assert_eq!(&h.cap as *const _ as usize - base, HeapHeader::OFFSET_CAP);
285 assert_eq!(&h.aux as *const _ as usize - base, HeapHeader::OFFSET_AUX);
286 }
287
288 #[test]
289 fn test_new_header() {
290 let h = HeapHeader::new(HeapKind::Array);
291 assert_eq!(h.kind, HeapKind::Array as u16);
292 assert_eq!(h.elem_type, 0);
293 assert_eq!(h.flags, 0);
294 assert_eq!(h.len, 0);
295 assert_eq!(h.cap, 0);
296 assert_eq!(h.aux, 0);
297 }
298
299 #[test]
300 fn test_with_len_aux() {
301 let h = HeapHeader::with_len_aux(HeapKind::TypedObject, 5, 0xDEAD_BEEF);
302 assert_eq!(h.kind, HeapKind::TypedObject as u16);
303 assert_eq!(h.len, 5);
304 assert_eq!(h.aux, 0xDEAD_BEEF);
305 }
306
307 #[test]
308 fn test_heap_kind_roundtrip() {
309 assert_eq!(HeapKind::from_u16(0), Some(HeapKind::String));
310 assert_eq!(HeapKind::from_u16(1), Some(HeapKind::Array));
311 assert_eq!(HeapKind::from_u16(2), Some(HeapKind::TypedObject));
312 assert_eq!(
313 HeapKind::from_u16(HeapKind::F32Array as u16),
314 Some(HeapKind::F32Array)
315 );
316 assert_eq!(HeapKind::from_u16(255), None);
317 }
318
319 #[test]
320 fn test_heap_kind_from_u8() {
321 assert_eq!(HeapKind::from_u8(0), Some(HeapKind::String));
322 assert_eq!(
323 HeapKind::from_u8(HeapKind::F32Array as u8),
324 Some(HeapKind::F32Array)
325 );
326 assert_eq!(HeapKind::from_u8(200), None);
327 }
328
329 #[test]
330 fn test_flags() {
331 let mut h = HeapHeader::new(HeapKind::Array);
332 assert!(!h.has_flag(FLAG_MARKED));
333 assert!(!h.has_flag(FLAG_PINNED));
334
335 h.set_flag(FLAG_MARKED);
336 assert!(h.has_flag(FLAG_MARKED));
337 assert!(!h.has_flag(FLAG_PINNED));
338
339 h.set_flag(FLAG_PINNED);
340 assert!(h.has_flag(FLAG_MARKED));
341 assert!(h.has_flag(FLAG_PINNED));
342
343 h.clear_flag(FLAG_MARKED);
344 assert!(!h.has_flag(FLAG_MARKED));
345 assert!(h.has_flag(FLAG_PINNED));
346 }
347
348 #[test]
349 fn test_from_heap_value_string() {
350 let hv = HeapValue::String(std::sync::Arc::new("hello".to_string()));
351 let h = HeapHeader::from_heap_value(&hv);
352 assert_eq!(h.kind, HeapKind::String as u16);
353 assert_eq!(h.len, 5);
354 }
355
356 #[test]
357 fn test_from_heap_value_typed_object() {
358 let hv = HeapValue::TypedObject {
359 schema_id: 42,
360 slots: vec![crate::slot::ValueSlot::from_number(0.0); 3].into_boxed_slice(),
361 heap_mask: 0,
362 };
363 let h = HeapHeader::from_heap_value(&hv);
364 assert_eq!(h.kind, HeapKind::TypedObject as u16);
365 assert_eq!(h.len, 3);
366 assert_eq!(h.aux, 42);
367 }
368
369 #[test]
370 fn test_from_heap_value_closure() {
371 let hv = HeapValue::Closure {
372 function_id: 7,
373 upvalues: vec![],
374 };
375 let h = HeapHeader::from_heap_value(&hv);
376 assert_eq!(h.kind, HeapKind::Closure as u16);
377 assert_eq!(h.len, 0);
378 assert_eq!(h.aux, 7);
379 }
380}