Skip to main content

intuicio_data/
data_stack.rs

1//! Type-erased stack that carries values between function calls.
2//!
3//! [`DataStack`] is the single place where Intuicio moves data. Native and
4//! script functions both take their arguments off it and put their results
5//! back on it, so neither side can tell the other apart.
6//!
7//! # Layout
8//!
9//! The stack is one flat byte buffer that grows upwards. Each pushed value is
10//! stored as its bytes followed by its [`TypeHash`], so a pop can check the
11//! type before reading anything back. The stack also remembers a drop function
12//! per type it has seen, so it can destroy values it no longer knows the Rust
13//! type of.
14//!
15//! Registers, the analogue of local variables, live on the same buffer. A
16//! register is a slot with a fixed type that can be empty or full, and it is
17//! addressed by index rather than by position. See [`DataStackRegisterAccess`].
18//!
19//! # Rules
20//!
21//! Data is only ever **moved**, never copied or cloned. A pop takes the value
22//! off the stack. A move into a register empties the place the value came
23//! from. A type that wants copies has to provide a function that pushes a
24//! duplicate itself.
25//!
26//! ```
27//! # use intuicio_data::data_stack::{DataStack, DataStackMode};
28//! let mut stack = DataStack::new(1024, DataStackMode::Mixed);
29//! stack.push(42_i32);
30//! assert_eq!(stack.pop::<i32>().unwrap(), 42);
31//! ```
32use crate::{Finalize, pointer_alignment_padding, type_hash::TypeHash};
33use smallvec::SmallVec;
34use std::{
35    alloc::Layout,
36    collections::{HashMap, hash_map::Entry},
37    ops::Range,
38};
39
40/// How to destroy a value of one type, remembered per type the stack saw.
41#[derive(Debug, Copy, Clone)]
42struct DataStackFinalizer {
43    callback: unsafe fn(*mut ()),
44    layout: Layout,
45}
46
47/// Header stored above a register slot.
48///
49/// `finalizer` doubles as the empty or full flag: [`None`] means the slot
50/// holds no value. `padding` records how many bytes were skipped below the
51/// slot to align it, so unwinding can give them back.
52#[derive(Debug, Copy, Clone)]
53struct DataStackRegisterTag {
54    type_hash: TypeHash,
55    layout: Layout,
56    finalizer: Option<unsafe fn(*mut ())>,
57    padding: u8,
58}
59
60/// Marker of a stack position, taken with [`DataStack::store`].
61///
62/// Passing it to [`DataStack::restore`] unwinds everything pushed after it,
63/// running the drop function of every value on the way. Passing it to
64/// [`DataStack::reverse`] flips the order of those items instead.
65pub struct DataStackToken(usize);
66
67impl DataStackToken {
68    /// Builds a token pointing at an arbitrary position.
69    ///
70    /// # Safety
71    ///
72    /// `position` must be a real item boundary of the stack it will be used
73    /// with. Restoring to a position inside a value leaves the stack reading
74    /// garbage as type tags.
75    pub unsafe fn new(position: usize) -> Self {
76        Self(position)
77    }
78}
79
80/// Handle to one register slot, taken with [`DataStack::access_register`].
81///
82/// A register has a fixed type chosen when it was pushed, and is either
83/// empty or full. Reads and takes check the requested type against the
84/// register type and return [`None`] on a mismatch.
85pub struct DataStackRegisterAccess<'a> {
86    stack: &'a mut DataStack,
87    position: usize,
88}
89
90impl<'a> DataStackRegisterAccess<'a> {
91    /// Returns the type this register slot was declared with.
92    pub fn type_hash(&self) -> TypeHash {
93        unsafe {
94            self.stack
95                .memory
96                .as_ptr()
97                .add(self.position)
98                .cast::<DataStackRegisterTag>()
99                .read_unaligned()
100                .type_hash
101        }
102    }
103
104    /// Returns the memory layout of the register slot.
105    pub fn layout(&self) -> Layout {
106        unsafe {
107            self.stack
108                .memory
109                .as_ptr()
110                .add(self.position)
111                .cast::<DataStackRegisterTag>()
112                .read_unaligned()
113                .layout
114        }
115    }
116
117    /// Returns type and layout together, reading the header only once.
118    pub fn type_hash_layout(&self) -> (TypeHash, Layout) {
119        unsafe {
120            let tag = self
121                .stack
122                .memory
123                .as_ptr()
124                .add(self.position)
125                .cast::<DataStackRegisterTag>()
126                .read_unaligned();
127            (tag.type_hash, tag.layout)
128        }
129    }
130
131    /// Returns `true` when the register currently holds a value.
132    pub fn has_value(&self) -> bool {
133        unsafe {
134            self.stack
135                .memory
136                .as_ptr()
137                .add(self.position)
138                .cast::<DataStackRegisterTag>()
139                .read_unaligned()
140                .finalizer
141                .is_some()
142        }
143    }
144
145    /// Borrows the stored value, or returns [`None`] when the register is empty
146    /// or holds another type.
147    pub fn read<T: 'static>(&'a self) -> Option<&'a T> {
148        unsafe {
149            let tag = self
150                .stack
151                .memory
152                .as_ptr()
153                .add(self.position)
154                .cast::<DataStackRegisterTag>()
155                .read_unaligned();
156            if tag.type_hash == TypeHash::of::<T>() && tag.finalizer.is_some() {
157                self.stack
158                    .memory
159                    .as_ptr()
160                    .add(self.position - tag.layout.size())
161                    .cast::<T>()
162                    .as_ref()
163            } else {
164                None
165            }
166        }
167    }
168
169    /// Borrows the stored value mutably, or returns [`None`] when the register
170    /// is empty or holds another type.
171    pub fn write<T: 'static>(&'a mut self) -> Option<&'a mut T> {
172        unsafe {
173            let tag = self
174                .stack
175                .memory
176                .as_ptr()
177                .add(self.position)
178                .cast::<DataStackRegisterTag>()
179                .read_unaligned();
180            if tag.type_hash == TypeHash::of::<T>() && tag.finalizer.is_some() {
181                self.stack
182                    .memory
183                    .as_mut_ptr()
184                    .add(self.position - tag.layout.size())
185                    .cast::<T>()
186                    .as_mut()
187            } else {
188                None
189            }
190        }
191    }
192
193    /// Moves the value out and leaves the register empty.
194    ///
195    /// Returns [`None`] when the register is empty or holds another type.
196    pub fn take<T: 'static>(&mut self) -> Option<T> {
197        unsafe {
198            let mut tag = self
199                .stack
200                .memory
201                .as_ptr()
202                .add(self.position)
203                .cast::<DataStackRegisterTag>()
204                .read_unaligned();
205            if tag.type_hash == TypeHash::of::<T>() && tag.finalizer.is_some() {
206                tag.finalizer = None;
207                self.stack
208                    .memory
209                    .as_mut_ptr()
210                    .add(self.position)
211                    .cast::<DataStackRegisterTag>()
212                    .write_unaligned(tag);
213                Some(
214                    self.stack
215                        .memory
216                        .as_ptr()
217                        .add(self.position - tag.layout.size())
218                        .cast::<T>()
219                        .read_unaligned(),
220                )
221            } else {
222                None
223            }
224        }
225    }
226
227    /// Drops the stored value in place and leaves the register empty.
228    ///
229    /// Returns `false` when there was nothing to drop.
230    pub fn free(&mut self) -> bool {
231        unsafe {
232            let mut tag = self
233                .stack
234                .memory
235                .as_ptr()
236                .add(self.position)
237                .cast::<DataStackRegisterTag>()
238                .read_unaligned();
239            if let Some(finalizer) = tag.finalizer {
240                (finalizer)(
241                    self.stack
242                        .memory
243                        .as_mut_ptr()
244                        .add(self.position - tag.layout.size())
245                        .cast::<()>(),
246                );
247                tag.finalizer = None;
248                self.stack
249                    .memory
250                    .as_mut_ptr()
251                    .add(self.position)
252                    .cast::<DataStackRegisterTag>()
253                    .write_unaligned(tag);
254                true
255            } else {
256                false
257            }
258        }
259    }
260
261    /// Moves a value into the register, dropping whatever was there.
262    ///
263    /// Does nothing when `T` is not the type the register was declared with.
264    pub fn set<T: Finalize + 'static>(&mut self, value: T) {
265        unsafe {
266            let mut tag = self
267                .stack
268                .memory
269                .as_ptr()
270                .add(self.position)
271                .cast::<DataStackRegisterTag>()
272                .read_unaligned();
273            if tag.type_hash == TypeHash::of::<T>() {
274                if let Some(finalizer) = tag.finalizer {
275                    (finalizer)(
276                        self.stack
277                            .memory
278                            .as_mut_ptr()
279                            .add(self.position - tag.layout.size())
280                            .cast::<()>(),
281                    );
282                } else {
283                    tag.finalizer = Some(T::finalize_raw);
284                }
285                self.stack
286                    .memory
287                    .as_mut_ptr()
288                    .add(self.position - tag.layout.size())
289                    .cast::<T>()
290                    .write_unaligned(value);
291                self.stack
292                    .memory
293                    .as_mut_ptr()
294                    .add(self.position)
295                    .cast::<DataStackRegisterTag>()
296                    .write_unaligned(tag);
297            }
298        }
299    }
300
301    /// Moves this register value into `other`, leaving this one empty.
302    ///
303    /// Does nothing when the two registers differ in type or layout, or when
304    /// they are the same slot.
305    pub fn move_to(&mut self, other: &mut Self) {
306        if self.position == other.position {
307            return;
308        }
309        unsafe {
310            let mut tag = self
311                .stack
312                .memory
313                .as_ptr()
314                .add(self.position)
315                .cast::<DataStackRegisterTag>()
316                .read_unaligned();
317            let other_tag = other
318                .stack
319                .memory
320                .as_ptr()
321                .add(self.position)
322                .cast::<DataStackRegisterTag>()
323                .read_unaligned();
324            if tag.type_hash == other_tag.type_hash && tag.layout == other_tag.layout {
325                if let Some(finalizer) = other_tag.finalizer {
326                    (finalizer)(
327                        self.stack
328                            .memory
329                            .as_mut_ptr()
330                            .add(other.position - other_tag.layout.size())
331                            .cast::<()>(),
332                    );
333                }
334                tag.finalizer = None;
335                let source = self
336                    .stack
337                    .memory
338                    .as_ptr()
339                    .add(self.position - tag.layout.size());
340                let target = self
341                    .stack
342                    .memory
343                    .as_mut_ptr()
344                    .add(other.position - other_tag.layout.size());
345                target.copy_from(source, tag.layout.size());
346                self.stack
347                    .memory
348                    .as_mut_ptr()
349                    .add(self.position)
350                    .cast::<DataStackRegisterTag>()
351                    .write_unaligned(tag);
352            }
353        }
354    }
355}
356
357/// What a [`DataStack`] is allowed to hold.
358///
359/// A context keeps its call stack and its registers in two separate stacks,
360/// so each of them can be restricted to what it actually needs.
361#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
362pub enum DataStackMode {
363    /// Pushed values only, no registers.
364    Values,
365    /// Registers only, no pushed values.
366    Registers,
367    #[default]
368    /// Both, which is the default.
369    Mixed,
370}
371
372impl DataStackMode {
373    /// Returns `true` when pushing and popping values is allowed.
374    pub fn allows_values(self) -> bool {
375        matches!(self, Self::Values | Self::Mixed)
376    }
377
378    /// Returns `true` when registers are allowed.
379    pub fn allows_registers(self) -> bool {
380        matches!(self, Self::Registers | Self::Mixed)
381    }
382}
383
384/// One item seen by [`DataStack::visit`], reported from the top down.
385///
386/// Mostly useful for debugging and for tools that want to show what is
387/// currently on a stack.
388pub enum DataStackVisitedItem<'a> {
389    /// A pushed value.
390    Value {
391        type_hash: TypeHash,
392        layout: Layout,
393        data: &'a [u8],
394        range: Range<usize>,
395    },
396    /// A register slot. `valid` tells whether it currently holds a value.
397    Register {
398        type_hash: TypeHash,
399        layout: Layout,
400        data: &'a [u8],
401        range: Range<usize>,
402        valid: bool,
403    },
404}
405
406/// Type-erased stack of values and registers.
407///
408/// Capacity is fixed at construction and rounded up to a power of two. A push
409/// that does not fit fails instead of growing the stack, so a running script
410/// cannot make the host reallocate under it. Dropping the stack unwinds
411/// everything still on it. See the [module docs](self) for the layout.
412pub struct DataStack {
413    memory: Vec<u8>,
414    position: usize,
415    mode: DataStackMode,
416    finalizers: HashMap<TypeHash, DataStackFinalizer>,
417    registers: Vec<usize>,
418    drop: bool,
419}
420
421impl Drop for DataStack {
422    fn drop(&mut self) {
423        if self.drop {
424            self.restore(DataStackToken(0));
425        }
426    }
427}
428
429impl DataStack {
430    /// Allocates a stack of at least `capacity` bytes, rounded up to a power of
431    /// two.
432    pub fn new(mut capacity: usize, mode: DataStackMode) -> Self {
433        capacity = capacity.next_power_of_two();
434        Self {
435            memory: vec![0; capacity],
436            position: 0,
437            mode,
438            finalizers: Default::default(),
439            registers: vec![],
440            drop: true,
441        }
442    }
443
444    /// Returns how many bytes are used.
445    pub fn position(&self) -> usize {
446        self.position
447    }
448
449    /// Returns the total capacity in bytes.
450    pub fn size(&self) -> usize {
451        self.memory.len()
452    }
453
454    /// Returns how many bytes are still free.
455    pub fn available(&self) -> usize {
456        self.size().saturating_sub(self.position)
457    }
458
459    /// Returns the used part of the buffer, tags included.
460    pub fn as_bytes(&self) -> &[u8] {
461        &self.memory[0..self.position]
462    }
463
464    /// Walks the stack from the top down, calling `f` for every item.
465    ///
466    /// Stops early when `f` returns `false`, or when an item cannot be read.
467    pub fn visit(&self, mut f: impl FnMut(DataStackVisitedItem) -> bool) {
468        let type_layout = Layout::new::<TypeHash>().pad_to_align();
469        let tag_layout = Layout::new::<DataStackRegisterTag>().pad_to_align();
470        let mut position = self.position;
471        while position > 0 {
472            if position < type_layout.size() {
473                return;
474            }
475            position -= type_layout.size();
476            let type_hash = unsafe {
477                self.memory
478                    .as_ptr()
479                    .add(position)
480                    .cast::<TypeHash>()
481                    .read_unaligned()
482            };
483            if type_hash == TypeHash::of::<DataStackRegisterTag>() {
484                if position < tag_layout.size() {
485                    return;
486                }
487                position -= tag_layout.size();
488                let tag = unsafe {
489                    self.memory
490                        .as_ptr()
491                        .add(position)
492                        .cast::<DataStackRegisterTag>()
493                        .read_unaligned()
494                };
495                if position < tag.layout.size() {
496                    return;
497                }
498                position -= tag.layout.size();
499                let range = position..(position + tag.layout.size());
500                let status = f(DataStackVisitedItem::Register {
501                    type_hash: tag.type_hash,
502                    layout: tag.layout,
503                    data: &self.memory[range.clone()],
504                    range,
505                    valid: tag.finalizer.is_some(),
506                });
507                if !status {
508                    return;
509                }
510                position -= tag.padding as usize;
511            } else if let Some(finalizer) = self.finalizers.get(&type_hash) {
512                if position < finalizer.layout.size() {
513                    return;
514                }
515                position -= finalizer.layout.size();
516                let range = position..(position + finalizer.layout.size());
517                let status = f(DataStackVisitedItem::Value {
518                    type_hash,
519                    layout: finalizer.layout,
520                    data: &self.memory[range.clone()],
521                    range,
522                });
523                if !status {
524                    return;
525                }
526            }
527        }
528    }
529
530    /// Moves a value onto the stack.
531    ///
532    /// Returns `false` without touching anything when the mode forbids values
533    /// or the value does not fit.
534    pub fn push<T: Finalize + Sized + 'static>(&mut self, value: T) -> bool {
535        if !self.mode.allows_values() {
536            return false;
537        }
538        let value_layout = Layout::new::<T>().pad_to_align();
539        let type_layout = Layout::new::<TypeHash>().pad_to_align();
540        if self.position + value_layout.size() + type_layout.size() > self.size() {
541            return false;
542        }
543        let type_hash = TypeHash::of::<T>();
544        self.finalizers
545            .entry(type_hash)
546            .or_insert(DataStackFinalizer {
547                callback: T::finalize_raw,
548                layout: value_layout,
549            });
550        unsafe {
551            self.memory
552                .as_mut_ptr()
553                .add(self.position)
554                .cast::<T>()
555                .write_unaligned(value);
556            self.position += value_layout.size();
557            self.memory
558                .as_mut_ptr()
559                .add(self.position)
560                .cast::<TypeHash>()
561                .write_unaligned(type_hash);
562            self.position += type_layout.size();
563        }
564        true
565    }
566
567    /// [`DataStack::push`] for a value whose type is only known at runtime.
568    ///
569    /// # Safety
570    ///
571    /// `data` must be a valid byte image of a value of the type named by
572    /// `type_hash`, matching `layout`, and `finalizer` must be the drop
573    /// function of that type. The bytes are moved, so the caller must not drop
574    /// the source afterwards.
575    pub unsafe fn push_raw(
576        &mut self,
577        layout: Layout,
578        type_hash: TypeHash,
579        finalizer: unsafe fn(*mut ()),
580        data: &[u8],
581    ) -> bool {
582        if !self.mode.allows_values() {
583            return false;
584        }
585        let value_layout = layout.pad_to_align();
586        let type_layout = Layout::new::<TypeHash>().pad_to_align();
587        if data.len() != value_layout.size()
588            && self.position + value_layout.size() + type_layout.size() > self.size()
589        {
590            return false;
591        }
592        self.finalizers
593            .entry(type_hash)
594            .or_insert(DataStackFinalizer {
595                callback: finalizer,
596                layout: value_layout,
597            });
598        self.memory[self.position..(self.position + value_layout.size())].copy_from_slice(data);
599        self.position += value_layout.size();
600        unsafe {
601            self.memory
602                .as_mut_ptr()
603                .add(self.position)
604                .cast::<TypeHash>()
605                .write_unaligned(type_hash)
606        };
607        self.position += type_layout.size();
608        true
609    }
610
611    /// Reserves an empty register for values of type `T` and returns its index.
612    pub fn push_register<T: Finalize + 'static>(&mut self) -> Option<usize> {
613        unsafe { self.push_register_raw(TypeHash::of::<T>(), Layout::new::<T>().pad_to_align()) }
614    }
615
616    /// Reserves a register for `T` and moves `value` into it, returning its
617    /// index.
618    pub fn push_register_value<T: Finalize + 'static>(&mut self, value: T) -> Option<usize> {
619        let result = self.push_register::<T>()?;
620        let mut access = self.access_register(result)?;
621        access.set(value);
622        Some(result)
623    }
624
625    /// [`DataStack::push_register`] for a type only known at runtime.
626    ///
627    /// # Safety
628    ///
629    /// `value_layout` must be the real layout of the type named by
630    /// `type_hash`. A wrong layout makes every later access to that register
631    /// read or write out of bounds.
632    pub unsafe fn push_register_raw(
633        &mut self,
634        type_hash: TypeHash,
635        value_layout: Layout,
636    ) -> Option<usize> {
637        if !self.mode.allows_registers() {
638            return None;
639        }
640        let tag_layout = Layout::new::<DataStackRegisterTag>().pad_to_align();
641        let type_layout = Layout::new::<TypeHash>().pad_to_align();
642        let padding = unsafe { self.alignment_padding(value_layout.align()) };
643        if self.position + padding + value_layout.size() + tag_layout.size() + type_layout.size()
644            > self.size()
645        {
646            return None;
647        }
648        unsafe {
649            self.position += padding + value_layout.size();
650            let position = self.position;
651            self.memory
652                .as_mut_ptr()
653                .add(self.position)
654                .cast::<DataStackRegisterTag>()
655                .write_unaligned(DataStackRegisterTag {
656                    type_hash,
657                    layout: value_layout,
658                    finalizer: None,
659                    padding: padding as u8,
660                });
661            self.position += tag_layout.size();
662            self.memory
663                .as_mut_ptr()
664                .add(self.position)
665                .cast::<TypeHash>()
666                .write_unaligned(TypeHash::of::<DataStackRegisterTag>());
667            self.position += type_layout.size();
668            self.registers.push(position);
669            Some(self.registers.len() - 1)
670        }
671    }
672
673    /// Moves the whole content of `other` on top of this stack.
674    ///
675    /// Gives `other` back untouched when it does not fit. Used to hand a batch
676    /// of arguments prepared elsewhere to a call.
677    pub fn push_stack(&mut self, mut other: Self) -> Result<(), Self> {
678        if self.available() < other.position {
679            return Err(other);
680        }
681        self.memory[self.position..(self.position + other.position)]
682            .copy_from_slice(&other.memory[0..other.position]);
683        self.position += other.position;
684        self.finalizers
685            .extend(other.finalizers.iter().map(|(key, value)| {
686                (
687                    *key,
688                    DataStackFinalizer {
689                        callback: value.callback,
690                        layout: value.layout,
691                    },
692                )
693            }));
694        unsafe { other.prevent_drop() };
695        Ok(())
696    }
697
698    /// Moves a register value onto the stack, leaving the register empty.
699    ///
700    /// Returns `false` when the mode forbids values or the value does not fit.
701    pub fn push_from_register(&mut self, register: &mut DataStackRegisterAccess) -> bool {
702        if !self.mode.allows_values() {
703            return false;
704        }
705        let type_layout = Layout::new::<TypeHash>().pad_to_align();
706        let mut tag = unsafe {
707            register
708                .stack
709                .memory
710                .as_ptr()
711                .add(register.position)
712                .cast::<DataStackRegisterTag>()
713                .read_unaligned()
714        };
715        if self.position + tag.layout.size() + type_layout.size() > self.size() {
716            return false;
717        }
718        if let Entry::Vacant(e) = self.finalizers.entry(tag.type_hash)
719            && let Some(finalizer) = tag.finalizer
720        {
721            e.insert(DataStackFinalizer {
722                callback: finalizer,
723                layout: tag.layout,
724            });
725        }
726        tag.finalizer = None;
727        unsafe {
728            let source = register
729                .stack
730                .memory
731                .as_ptr()
732                .add(register.position - tag.layout.size());
733            let target = self.memory.as_mut_ptr().add(self.position);
734            target.copy_from(source, tag.layout.size());
735            self.position += tag.layout.size();
736            self.memory
737                .as_mut_ptr()
738                .add(self.position)
739                .cast::<TypeHash>()
740                .write_unaligned(tag.type_hash);
741            self.position += type_layout.size();
742            register
743                .stack
744                .memory
745                .as_mut_ptr()
746                .add(register.position)
747                .cast::<DataStackRegisterTag>()
748                .write_unaligned(tag);
749        }
750        true
751    }
752
753    /// Moves the top value off the stack.
754    ///
755    /// Returns [`None`], leaving the stack untouched, when the top value is not
756    /// a `T`.
757    pub fn pop<T: Sized + 'static>(&mut self) -> Option<T> {
758        if !self.mode.allows_values() {
759            return None;
760        }
761        let type_layout = Layout::new::<TypeHash>().pad_to_align();
762        let value_layout = Layout::new::<T>().pad_to_align();
763        if self.position < type_layout.size() + value_layout.size() {
764            return None;
765        }
766        let type_hash = unsafe {
767            self.memory
768                .as_mut_ptr()
769                .add(self.position - type_layout.size())
770                .cast::<TypeHash>()
771                .read_unaligned()
772        };
773        if type_hash != TypeHash::of::<T>() || type_hash == TypeHash::of::<DataStackRegisterTag>() {
774            return None;
775        }
776        self.position -= type_layout.size();
777        let result = unsafe {
778            self.memory
779                .as_ptr()
780                .add(self.position - value_layout.size())
781                .cast::<T>()
782                .read_unaligned()
783        };
784        self.position -= value_layout.size();
785        Some(result)
786    }
787
788    /// [`DataStack::pop`] without knowing the type, returning the raw bytes
789    /// along with the layout, type and drop function.
790    ///
791    /// # Safety
792    ///
793    /// The returned bytes are an owned value that nothing drops for the caller.
794    /// Losing them leaks, and dropping them twice is undefined. Feed them back
795    /// to [`DataStack::push_raw`] or run the returned finalizer once.
796    #[allow(clippy::type_complexity)]
797    pub unsafe fn pop_raw(&mut self) -> Option<(Layout, TypeHash, unsafe fn(*mut ()), Vec<u8>)> {
798        if !self.mode.allows_values() {
799            return None;
800        }
801        let type_layout = Layout::new::<TypeHash>().pad_to_align();
802        if self.position < type_layout.size() {
803            return None;
804        }
805        let type_hash = unsafe {
806            self.memory
807                .as_mut_ptr()
808                .add(self.position - type_layout.size())
809                .cast::<TypeHash>()
810                .read_unaligned()
811        };
812        if type_hash == TypeHash::of::<DataStackRegisterTag>() {
813            return None;
814        }
815        let finalizer = self.finalizers.get(&type_hash)?;
816        if self.position < type_layout.size() + finalizer.layout.size() {
817            return None;
818        }
819        self.position -= type_layout.size();
820        let data = self.memory[(self.position - finalizer.layout.size())..self.position].to_vec();
821        self.position -= finalizer.layout.size();
822        Some((finalizer.layout, type_hash, finalizer.callback, data))
823    }
824
825    /// Drops the top value in place instead of returning it.
826    ///
827    /// Returns `false` when the top of the stack is a register.
828    pub fn drop(&mut self) -> bool {
829        if !self.mode.allows_values() {
830            return false;
831        }
832        let type_layout = Layout::new::<TypeHash>().pad_to_align();
833        self.position -= type_layout.size();
834        let type_hash = unsafe {
835            self.memory
836                .as_ptr()
837                .add(self.position)
838                .cast::<TypeHash>()
839                .read_unaligned()
840        };
841        if type_hash == TypeHash::of::<DataStackRegisterTag>() {
842            return false;
843        }
844        if let Some(finalizer) = self.finalizers.get(&type_hash) {
845            self.position -= finalizer.layout.size();
846            unsafe {
847                (finalizer.callback)(self.memory.as_mut_ptr().add(self.position).cast::<()>());
848            }
849        }
850        true
851    }
852
853    /// Removes the topmost register, dropping its value if it holds one.
854    ///
855    /// Returns `false` when the top of the stack is not a register.
856    pub fn drop_register(&mut self) -> bool {
857        if !self.mode.allows_registers() {
858            return false;
859        }
860        let tag_layout = Layout::new::<DataStackRegisterTag>().pad_to_align();
861        let type_layout = Layout::new::<TypeHash>().pad_to_align();
862        unsafe {
863            let type_hash = self
864                .memory
865                .as_mut_ptr()
866                .add(self.position - type_layout.size())
867                .cast::<TypeHash>()
868                .read_unaligned();
869            if type_hash != TypeHash::of::<DataStackRegisterTag>() {
870                return false;
871            }
872            self.position -= type_layout.size();
873            self.position -= tag_layout.size();
874            let tag = self
875                .memory
876                .as_ptr()
877                .add(self.position)
878                .cast::<DataStackRegisterTag>()
879                .read_unaligned();
880            self.position -= tag.layout.size() - tag.padding as usize;
881            if let Some(finalizer) = tag.finalizer {
882                (finalizer)(self.memory.as_mut_ptr().add(self.position).cast::<()>());
883            }
884            self.registers.pop();
885        }
886        true
887    }
888
889    /// Moves the top `data_count` values into a new stack of their own.
890    ///
891    /// `capacity` sizes the new stack, and is raised when the values need more.
892    /// Used to detach arguments for a call that runs elsewhere, for example on
893    /// another thread.
894    pub fn pop_stack(&mut self, mut data_count: usize, capacity: Option<usize>) -> Self {
895        let type_layout = Layout::new::<TypeHash>().pad_to_align();
896        let mut size = 0;
897        let mut position = self.position;
898        let mut finalizers = HashMap::new();
899        while data_count > 0 && position > 0 {
900            data_count -= 1;
901            position -= type_layout.size();
902            size += type_layout.size();
903            let type_hash = unsafe {
904                self.memory
905                    .as_mut_ptr()
906                    .add(position)
907                    .cast::<TypeHash>()
908                    .read_unaligned()
909            };
910            if let Some(finalizer) = self.finalizers.get(&type_hash) {
911                position -= finalizer.layout.size();
912                size += finalizer.layout.size();
913                finalizers.insert(
914                    type_hash,
915                    DataStackFinalizer {
916                        callback: finalizer.callback,
917                        layout: finalizer.layout,
918                    },
919                );
920            }
921        }
922        let mut result = Self::new(capacity.unwrap_or(size).max(size), self.mode);
923        result.memory[0..size].copy_from_slice(&self.memory[position..self.position]);
924        result.finalizers.extend(finalizers);
925        self.position = position;
926        result.position = size;
927        result
928    }
929
930    /// Moves the top value into a register, dropping whatever the register
931    /// held.
932    ///
933    /// Returns `false` when the types do not match or the stack is empty.
934    pub fn pop_to_register(&mut self, register: &mut DataStackRegisterAccess) -> bool {
935        if !self.mode.allows_values() {
936            return false;
937        }
938        let type_layout = Layout::new::<TypeHash>().pad_to_align();
939        if self.position < type_layout.size() {
940            return false;
941        }
942        let type_hash = unsafe {
943            self.memory
944                .as_mut_ptr()
945                .add(self.position - type_layout.size())
946                .cast::<TypeHash>()
947                .read_unaligned()
948        };
949        let mut tag = unsafe {
950            register
951                .stack
952                .memory
953                .as_ptr()
954                .add(register.position)
955                .cast::<DataStackRegisterTag>()
956                .read_unaligned()
957        };
958        if type_hash != tag.type_hash || type_hash == TypeHash::of::<DataStackRegisterTag>() {
959            return false;
960        }
961        if self.position < type_layout.size() + tag.layout.size() {
962            return false;
963        }
964        let finalizer = match self.finalizers.get(&type_hash) {
965            Some(finalizer) => finalizer.callback,
966            None => return false,
967        };
968        unsafe {
969            if let Some(finalizer) = tag.finalizer {
970                (finalizer)(
971                    register
972                        .stack
973                        .memory
974                        .as_mut_ptr()
975                        .add(register.position - tag.layout.size())
976                        .cast::<()>(),
977                );
978            }
979            tag.finalizer = Some(finalizer);
980            let source = self
981                .memory
982                .as_ptr()
983                .add(self.position - type_layout.size() - tag.layout.size());
984            let target = register
985                .stack
986                .memory
987                .as_mut_ptr()
988                .add(register.position - tag.layout.size());
989            target.copy_from(source, tag.layout.size());
990            register
991                .stack
992                .memory
993                .as_mut_ptr()
994                .add(register.position)
995                .cast::<DataStackRegisterTag>()
996                .write_unaligned(tag);
997        }
998        self.position -= type_layout.size();
999        self.position -= tag.layout.size();
1000        true
1001    }
1002
1003    /// Marks the current position, to unwind or reverse back to later.
1004    pub fn store(&self) -> DataStackToken {
1005        DataStackToken(self.position)
1006    }
1007
1008    /// Unwinds down to `token`, dropping every value and register above it.
1009    ///
1010    /// This is how a scope cleans up after itself.
1011    pub fn restore(&mut self, token: DataStackToken) {
1012        let type_layout = Layout::new::<TypeHash>().pad_to_align();
1013        let tag_layout = Layout::new::<DataStackRegisterTag>().pad_to_align();
1014        let tag_type_hash = TypeHash::of::<DataStackRegisterTag>();
1015        while self.position > token.0 {
1016            self.position -= type_layout.size();
1017            let type_hash = unsafe {
1018                self.memory
1019                    .as_ptr()
1020                    .add(self.position)
1021                    .cast::<TypeHash>()
1022                    .read_unaligned()
1023            };
1024            if type_hash == tag_type_hash {
1025                unsafe {
1026                    let tag = self
1027                        .memory
1028                        .as_ptr()
1029                        .add(self.position - tag_layout.size())
1030                        .cast::<DataStackRegisterTag>()
1031                        .read_unaligned();
1032                    self.position -= tag_layout.size();
1033                    self.position -= tag.layout.size();
1034                    if let Some(finalizer) = tag.finalizer {
1035                        (finalizer)(self.memory.as_mut_ptr().add(self.position).cast::<()>());
1036                    }
1037                    self.position -= tag.padding as usize;
1038                    self.registers.pop();
1039                }
1040            } else if let Some(finalizer) = self.finalizers.get(&type_hash) {
1041                self.position -= finalizer.layout.size();
1042                unsafe {
1043                    (finalizer.callback)(self.memory.as_mut_ptr().add(self.position).cast::<()>());
1044                }
1045            }
1046        }
1047    }
1048
1049    /// Reverses the order of the items pushed since `token`.
1050    ///
1051    /// Callers push arguments in declaration order while callees pop them in
1052    /// the same order, so the block has to be flipped in between.
1053    /// See [`DataStackPack::stack_push_reversed`].
1054    pub fn reverse(&mut self, token: DataStackToken) {
1055        let size = self.position.saturating_sub(token.0);
1056        let mut meta_data = SmallVec::<[_; 8]>::with_capacity(8);
1057        let mut meta_registers = 0;
1058        let type_layout = Layout::new::<TypeHash>().pad_to_align();
1059        let tag_layout = Layout::new::<DataStackRegisterTag>().pad_to_align();
1060        let tag_type_hash = TypeHash::of::<DataStackRegisterTag>();
1061        let mut position = self.position;
1062        while position > token.0 {
1063            position -= type_layout.size();
1064            let type_hash = unsafe {
1065                self.memory
1066                    .as_mut_ptr()
1067                    .add(position)
1068                    .cast::<TypeHash>()
1069                    .read_unaligned()
1070            };
1071            if type_hash == tag_type_hash {
1072                unsafe {
1073                    let tag = self
1074                        .memory
1075                        .as_ptr()
1076                        .add(self.position - tag_layout.size())
1077                        .cast::<DataStackRegisterTag>()
1078                        .read_unaligned();
1079                    position -= tag_layout.size();
1080                    position -= tag.layout.size();
1081                    meta_data.push((
1082                        position - token.0,
1083                        type_layout.size() + tag_layout.size() + tag.layout.size(),
1084                    ));
1085                    meta_registers += 1;
1086                }
1087            } else if let Some(finalizer) = self.finalizers.get(&type_hash) {
1088                position -= finalizer.layout.size();
1089                meta_data.push((
1090                    position - token.0,
1091                    type_layout.size() + finalizer.layout.size(),
1092                ));
1093            }
1094        }
1095        if meta_data.len() <= 1 {
1096            return;
1097        }
1098        let mut memory = SmallVec::<[_; 256]>::new();
1099        memory.resize(size, 0);
1100        memory.copy_from_slice(&self.memory[token.0..self.position]);
1101        for (source_position, size) in meta_data {
1102            self.memory[position..(position + size)]
1103                .copy_from_slice(&memory[source_position..(source_position + size)]);
1104            position += size;
1105        }
1106        let start = self.registers.len() - meta_registers;
1107        self.registers[start..].reverse();
1108    }
1109
1110    /// Returns the type of the top item without moving anything.
1111    ///
1112    /// A register reports the type tag of its header, not the type it stores.
1113    pub fn peek(&self) -> Option<TypeHash> {
1114        if self.position == 0 {
1115            return None;
1116        }
1117        let type_layout = Layout::new::<TypeHash>().pad_to_align();
1118        Some(unsafe {
1119            self.memory
1120                .as_ptr()
1121                .add(self.position - type_layout.size())
1122                .cast::<TypeHash>()
1123                .read_unaligned()
1124        })
1125    }
1126
1127    /// Returns how many registers are alive.
1128    pub fn registers_count(&self) -> usize {
1129        self.registers.len()
1130    }
1131
1132    /// Takes a handle to one register, or [`None`] when the index is unused.
1133    pub fn access_register(&'_ mut self, index: usize) -> Option<DataStackRegisterAccess<'_>> {
1134        let position = *self.registers.get(index)?;
1135        Some(DataStackRegisterAccess {
1136            stack: self,
1137            position,
1138        })
1139    }
1140
1141    /// Takes handles to two different registers at once, for moving a value
1142    /// between them.
1143    ///
1144    /// Returns [`None`] when the indices are equal or unused.
1145    pub fn access_registers_pair(
1146        &'_ mut self,
1147        a: usize,
1148        b: usize,
1149    ) -> Option<(DataStackRegisterAccess<'_>, DataStackRegisterAccess<'_>)> {
1150        if a == b {
1151            return None;
1152        }
1153        let position_a = *self.registers.get(a)?;
1154        let position_b = *self.registers.get(b)?;
1155        unsafe {
1156            Some((
1157                DataStackRegisterAccess {
1158                    stack: (self as *mut Self).as_mut()?,
1159                    position: position_a,
1160                },
1161                DataStackRegisterAccess {
1162                    stack: (self as *mut Self).as_mut()?,
1163                    position: position_b,
1164                },
1165            ))
1166        }
1167    }
1168
1169    /// Stops this stack from unwinding its content when it is dropped.
1170    ///
1171    /// # Safety
1172    ///
1173    /// Everything still on the stack leaks unless its ownership was already
1174    /// handed to someone else, which is what [`DataStack::push_stack`] does.
1175    pub unsafe fn prevent_drop(&mut self) {
1176        self.drop = false;
1177    }
1178
1179    /// Returns the padding needed at the current position to reach
1180    /// `alignment`.
1181    ///
1182    /// # Safety
1183    ///
1184    /// Reads the buffer pointer at the current position, which must be inside
1185    /// the allocation.
1186    #[inline]
1187    unsafe fn alignment_padding(&self, alignment: usize) -> usize {
1188        pointer_alignment_padding(
1189            unsafe { self.memory.as_ptr().add(self.position) },
1190            alignment,
1191        )
1192    }
1193}
1194
1195/// Moves a tuple of values on and off a [`DataStack`] in one step.
1196///
1197/// This is the bridge between a Rust call with typed arguments and the untyped
1198/// stack. Argument tuples and result tuples both go through it.
1199/// Implemented for tuples of up to sixteen elements, and for `()`.
1200pub trait DataStackPack: Sized {
1201    /// Pushes every element in tuple order.
1202    fn stack_push(self, stack: &mut DataStack);
1203
1204    /// Pushes every element so that the first one ends up on top.
1205    ///
1206    /// This is the order a callee expects, since it pops its arguments from
1207    /// first to last.
1208    fn stack_push_reversed(self, stack: &mut DataStack) {
1209        let token = stack.store();
1210        self.stack_push(stack);
1211        stack.reverse(token);
1212    }
1213
1214    /// Pops every element in tuple order.
1215    ///
1216    /// # Panics
1217    ///
1218    /// Panics when the stack does not hold the expected types.
1219    fn stack_pop(stack: &mut DataStack) -> Self;
1220
1221    /// Returns the types of the tuple elements, in order.
1222    fn pack_types() -> Vec<TypeHash>;
1223}
1224
1225impl DataStackPack for () {
1226    fn stack_push(self, _: &mut DataStack) {}
1227
1228    fn stack_pop(_: &mut DataStack) -> Self {}
1229
1230    fn pack_types() -> Vec<TypeHash> {
1231        vec![]
1232    }
1233}
1234
1235/// Implements [`DataStackPack`] for a tuple of the given element types.
1236macro_rules! impl_data_stack_tuple {
1237    ($($type:ident),+) => {
1238        impl<$($type: 'static),+> DataStackPack for ($($type,)+) {
1239            #[allow(non_snake_case)]
1240            fn stack_push(self, stack: &mut DataStack) {
1241                let ($( $type, )+) = self;
1242                $( stack.push($type); )+
1243            }
1244
1245            #[allow(non_snake_case)]
1246            fn stack_pop(stack: &mut DataStack) -> Self {
1247                ($(
1248                    stack.pop::<$type>().unwrap_or_else(
1249                        || panic!("Could not pop data of type: {}", std::any::type_name::<$type>())
1250                    ),
1251                )+)
1252            }
1253
1254            #[allow(non_snake_case)]
1255            fn pack_types() -> Vec<TypeHash> {
1256                vec![ $( TypeHash::of::<$type>() ),+ ]
1257            }
1258        }
1259    };
1260}
1261
1262impl_data_stack_tuple!(A);
1263impl_data_stack_tuple!(A, B);
1264impl_data_stack_tuple!(A, B, C);
1265impl_data_stack_tuple!(A, B, C, D);
1266impl_data_stack_tuple!(A, B, C, D, E);
1267impl_data_stack_tuple!(A, B, C, D, E, F);
1268impl_data_stack_tuple!(A, B, C, D, E, F, G);
1269impl_data_stack_tuple!(A, B, C, D, E, F, G, H);
1270impl_data_stack_tuple!(A, B, C, D, E, F, G, H, I);
1271impl_data_stack_tuple!(A, B, C, D, E, F, G, H, I, J);
1272impl_data_stack_tuple!(A, B, C, D, E, F, G, H, I, J, K);
1273impl_data_stack_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
1274impl_data_stack_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
1275impl_data_stack_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
1276impl_data_stack_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
1277impl_data_stack_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
1278
1279#[cfg(test)]
1280mod tests {
1281    use crate::{
1282        data_stack::{DataStack, DataStackMode},
1283        type_hash::TypeHash,
1284    };
1285    use std::{alloc::Layout, cell::RefCell, rc::Rc};
1286
1287    #[test]
1288    fn test_data_stack() {
1289        struct Droppable(Rc<RefCell<bool>>);
1290
1291        impl Drop for Droppable {
1292            fn drop(&mut self) {
1293                *self.0.borrow_mut() = true;
1294            }
1295        }
1296
1297        let dropped = Rc::new(RefCell::new(false));
1298        let mut stack = DataStack::new(10240, DataStackMode::Values);
1299        assert_eq!(stack.size(), 16384);
1300        assert_eq!(stack.position(), 0);
1301        stack.push(Droppable(dropped.clone()));
1302        assert_eq!(
1303            stack.position(),
1304            if cfg!(feature = "typehash_debug_name") {
1305                32
1306            } else {
1307                16
1308            }
1309        );
1310        let token = stack.store();
1311        stack.push(42_usize);
1312        assert_eq!(
1313            stack.position(),
1314            if cfg!(feature = "typehash_debug_name") {
1315                64
1316            } else {
1317                32
1318            }
1319        );
1320        stack.push(true);
1321        assert_eq!(
1322            stack.position(),
1323            if cfg!(feature = "typehash_debug_name") {
1324                89
1325            } else {
1326                41
1327            }
1328        );
1329        stack.push(4.2_f32);
1330        assert_eq!(
1331            stack.position(),
1332            if cfg!(feature = "typehash_debug_name") {
1333                117
1334            } else {
1335                53
1336            }
1337        );
1338        assert!(!*dropped.borrow());
1339        assert!(stack.pop::<()>().is_none());
1340        stack.push(());
1341        assert_eq!(
1342            stack.position(),
1343            if cfg!(feature = "typehash_debug_name") {
1344                141
1345            } else {
1346                61
1347            }
1348        );
1349        stack.reverse(token);
1350        let mut stack2 = stack.pop_stack(2, None);
1351        assert_eq!(
1352            stack.position(),
1353            if cfg!(feature = "typehash_debug_name") {
1354                84
1355            } else {
1356                36
1357            }
1358        );
1359        assert_eq!(
1360            stack2.size(),
1361            if cfg!(feature = "typehash_debug_name") {
1362                64
1363            } else {
1364                32
1365            }
1366        );
1367        assert_eq!(
1368            stack2.position(),
1369            if cfg!(feature = "typehash_debug_name") {
1370                57
1371            } else {
1372                25
1373            }
1374        );
1375        assert_eq!(stack2.pop::<usize>().unwrap(), 42_usize);
1376        assert_eq!(
1377            stack2.position(),
1378            if cfg!(feature = "typehash_debug_name") {
1379                25
1380            } else {
1381                9
1382            }
1383        );
1384        assert!(stack2.pop::<bool>().unwrap());
1385        assert_eq!(stack2.position(), 0);
1386        stack2.push(true);
1387        stack2.push(42_usize);
1388        stack.push_stack(stack2).ok().unwrap();
1389        assert_eq!(
1390            stack.position(),
1391            if cfg!(feature = "typehash_debug_name") {
1392                141
1393            } else {
1394                61
1395            }
1396        );
1397        assert_eq!(stack.pop::<usize>().unwrap(), 42_usize);
1398        assert_eq!(
1399            stack.position(),
1400            if cfg!(feature = "typehash_debug_name") {
1401                109
1402            } else {
1403                45
1404            }
1405        );
1406        assert!(stack.pop::<bool>().unwrap());
1407        assert_eq!(
1408            stack.position(),
1409            if cfg!(feature = "typehash_debug_name") {
1410                84
1411            } else {
1412                36
1413            }
1414        );
1415        assert_eq!(stack.pop::<f32>().unwrap(), 4.2_f32);
1416        assert_eq!(
1417            stack.position(),
1418            if cfg!(feature = "typehash_debug_name") {
1419                56
1420            } else {
1421                24
1422            }
1423        );
1424        stack.pop::<()>().unwrap();
1425        assert_eq!(
1426            stack.position(),
1427            if cfg!(feature = "typehash_debug_name") {
1428                32
1429            } else {
1430                16
1431            }
1432        );
1433        stack.push(42_usize);
1434        unsafe {
1435            let (layout, type_hash, finalizer, data) = stack.pop_raw().unwrap();
1436            assert_eq!(layout, Layout::new::<usize>().pad_to_align());
1437            assert_eq!(type_hash, TypeHash::of::<usize>());
1438            assert!(stack.push_raw(layout, type_hash, finalizer, &data));
1439            assert_eq!(
1440                stack.position(),
1441                if cfg!(feature = "typehash_debug_name") {
1442                    64
1443                } else {
1444                    32
1445                }
1446            );
1447            assert_eq!(stack.pop::<usize>().unwrap(), 42_usize);
1448            assert_eq!(
1449                stack.position(),
1450                if cfg!(feature = "typehash_debug_name") {
1451                    32
1452                } else {
1453                    16
1454                }
1455            );
1456        }
1457        drop(stack);
1458        assert!(*dropped.borrow());
1459
1460        let mut stack = DataStack::new(10240, DataStackMode::Registers);
1461        assert_eq!(stack.size(), 16384);
1462        stack.push_register::<bool>().unwrap();
1463        stack.drop_register();
1464        let a = stack.push_register_value(true).unwrap();
1465        assert!(*stack.access_register(a).unwrap().read::<bool>().unwrap());
1466        assert!(stack.access_register(a).unwrap().take::<bool>().unwrap());
1467        assert!(!stack.access_register(a).unwrap().has_value());
1468        let b = stack.push_register_value(0usize).unwrap();
1469        stack.access_register(b).unwrap().set(42usize);
1470        assert_eq!(
1471            *stack.access_register(b).unwrap().read::<usize>().unwrap(),
1472            42
1473        );
1474    }
1475}