rune/core/object/
vector.rs

1use super::{CloneIn, Gc, IntoObject, MutObjCell, ObjCell, Object};
2use crate::{
3    core::gc::{Block, GcHeap, GcState, Trace},
4    derive_GcMoveable,
5};
6use anyhow::{Result, anyhow};
7use bumpalo::collections::Vec as GcVec;
8use rune_core::hashmap::HashSet;
9use rune_macros::Trace;
10use std::{
11    cell::Cell,
12    fmt::{self, Write},
13    ops::Deref,
14    ptr::addr_of,
15};
16
17struct LispVecInner {
18    is_const: bool,
19    inner: Cell<*const [ObjCell]>,
20}
21
22/// A lisp vector. Unlike vectors in other languages this is not resizeable. This type is
23/// represented as slice of [`ObjCell`] which is immutable by default. However with the
24/// [try_mut](LispVec::try_mut) method, you can obtain a mutable view into this slice.
25#[derive(PartialEq, Eq, Trace)]
26pub(crate) struct LispVec(GcHeap<LispVecInner>);
27
28derive_GcMoveable!(LispVec);
29
30impl Deref for LispVec {
31    type Target = [ObjCell];
32
33    fn deref(&self) -> &Self::Target {
34        self.0.get_slice()
35    }
36}
37
38impl PartialEq for LispVecInner {
39    fn eq(&self, other: &Self) -> bool {
40        self.get_slice() == other.get_slice()
41    }
42}
43
44impl Eq for LispVecInner {}
45
46impl LispVec {
47    // SAFETY: Since this type does not have an object lifetime, it is only safe
48    // to use in context of the allocator.
49    pub(in crate::core) unsafe fn new(ptr: *const [Object], constant: bool) -> Self {
50        Self(GcHeap::new(LispVecInner::new(ptr, constant), constant))
51    }
52
53    pub(crate) fn to_vec(&self) -> Vec<Object> {
54        // SAFETY: ObjCell and GcObj have the same representation.
55        let obj_slice = unsafe { &*(addr_of!(*self.0.inner.get()) as *const [Object]) };
56        obj_slice.to_vec()
57    }
58}
59
60impl LispVecInner {
61    fn get_slice(&self) -> &[ObjCell] {
62        unsafe { &*self.inner.get() }
63    }
64}
65
66impl LispVec {
67    pub(crate) fn try_mut(&self) -> Result<&[MutObjCell]> {
68        if self.0.is_const {
69            Err(anyhow!("Attempt to mutate constant Vector"))
70        } else {
71            // SAFETY: ObjCell and MutObjCell have the same representation.
72            unsafe { Ok(&*(self.0.inner.get() as *const [MutObjCell])) }
73        }
74    }
75}
76
77impl<'new> CloneIn<'new, &'new Self> for LispVec {
78    fn clone_in<const C: bool>(&self, bk: &'new Block<C>) -> Gc<&'new Self> {
79        let mut vec = GcVec::with_capacity_in(self.len(), &bk.objects);
80        vec.extend(self.iter().map(|x| x.get().clone_in(bk)));
81        vec.into_obj(bk)
82    }
83}
84
85impl Trace for LispVecInner {
86    fn trace(&self, state: &mut GcState) {
87        assert!(!self.is_const, "Attempt to trace mutable vector");
88        // Update the object pointers in the vector
89        // move the vector to the to-space.
90        //
91        // TODO: can we update and move in one step? should be able to use
92        // `alloc_slice_fill_iter`
93        let slice = unsafe { &*(self.inner.get() as *mut [Object]) };
94        let new = state.to_space.alloc_slice_copy(slice);
95        let new = unsafe { std::mem::transmute::<&mut [Object], &mut [ObjCell]>(new) };
96        for x in &mut *new {
97            x.trace(state);
98        }
99        self.inner.set(new);
100    }
101}
102
103impl fmt::Display for LispVec {
104    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
105        self.display_walk(f, &mut HashSet::default())
106    }
107}
108
109impl fmt::Debug for LispVec {
110    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
111        self.display_walk(f, &mut HashSet::default())
112    }
113}
114
115impl LispVec {
116    pub(super) fn display_walk(
117        &self,
118        f: &mut fmt::Formatter,
119        seen: &mut HashSet<*const u8>,
120    ) -> fmt::Result {
121        let ptr = (&*self.0 as *const LispVecInner).cast();
122        if seen.contains(&ptr) {
123            return write!(f, "#0");
124        }
125        seen.insert(ptr);
126
127        f.write_char('[')?;
128        for (i, x) in self.iter().enumerate() {
129            if i != 0 {
130                f.write_char(' ')?;
131            }
132            x.get().untag().display_walk(f, seen)?;
133        }
134        f.write_char(']')
135    }
136}
137
138impl LispVecInner {
139    unsafe fn new(ptr: *const [Object], is_const: bool) -> Self {
140        let ptr = ptr as *mut [ObjCell];
141        Self { is_const, inner: Cell::new(ptr) }
142    }
143}
144
145#[repr(transparent)]
146pub(crate) struct RecordBuilder<'ob>(pub(crate) GcVec<'ob, Object<'ob>>);
147
148#[derive(PartialEq, Eq, Trace)]
149pub(crate) struct Record(GcHeap<LispVecInner>);
150
151derive_GcMoveable!(Record);
152
153impl Deref for Record {
154    type Target = [ObjCell];
155
156    fn deref(&self) -> &Self::Target {
157        self.0.get_slice()
158    }
159}
160
161impl<'new> CloneIn<'new, &'new Self> for Record {
162    fn clone_in<const C: bool>(&self, bk: &'new Block<C>) -> Gc<&'new Self> {
163        let mut vec = GcVec::with_capacity_in(self.len(), &bk.objects);
164        vec.extend(self.iter().map(|x| x.get().clone_in(bk)));
165        RecordBuilder(vec).into_obj(bk)
166    }
167}
168
169impl fmt::Display for Record {
170    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
171        self.display_walk(f, &mut HashSet::default())
172    }
173}
174
175impl Record {
176    pub(crate) fn try_mut(&self) -> Result<&[MutObjCell]> {
177        if self.0.is_const {
178            Err(anyhow!("Attempt to mutate constant Vector"))
179        } else {
180            // SAFETY: ObjCell and MutObjCell have the same representation.
181            unsafe { Ok(&*(self.0.inner.get() as *const [MutObjCell])) }
182        }
183    }
184
185    pub(super) fn display_walk(
186        &self,
187        f: &mut fmt::Formatter,
188        seen: &mut HashSet<*const u8>,
189    ) -> fmt::Result {
190        let ptr = (self as *const Self).cast();
191        if seen.contains(&ptr) {
192            return write!(f, "#0");
193        }
194        seen.insert(ptr);
195        write!(f, "#s(")?;
196        for (i, x) in self.iter().enumerate() {
197            if i != 0 {
198                f.write_char(' ')?;
199            }
200            x.get().untag().display_walk(f, seen)?;
201        }
202        f.write_char(')')
203    }
204}