koto_runtime/types/
tuple.rs

1use crate::{Ptr, Result, prelude::*};
2use std::ops::{Deref, Range};
3
4/// The Tuple type used by the Koto runtime
5#[derive(Clone)]
6pub struct KTuple(Inner);
7
8// Either the full tuple, a slice with 16bit bounds, or a slice with larger bounds
9#[derive(Clone)]
10enum Inner {
11    Full(Ptr<Vec<KValue>>),
12    Slice(TupleSlice16),
13    SliceLarge(Ptr<TupleSlice>),
14}
15
16impl KTuple {
17    /// Returns a new tuple with shared data and with restricted bounds
18    ///
19    /// The provided bounds should have indices relative to the current tuple's bounds
20    /// (i.e. instead of relative to the underlying shared tuple data), so it follows that the
21    /// result will always be a subset of the input tuple.
22    pub fn make_sub_tuple(&self, mut new_bounds: Range<usize>) -> Option<Self> {
23        let slice = match &self.0 {
24            Inner::Full(data) => TupleSlice::from(data.clone()),
25            Inner::SliceLarge(slice) => slice.deref().clone(),
26            Inner::Slice(slice) => TupleSlice::from(slice.clone()),
27        };
28
29        new_bounds.start += slice.bounds.start;
30        new_bounds.end += slice.bounds.start;
31
32        if new_bounds.end <= slice.bounds.end && slice.get(new_bounds.clone()).is_some() {
33            let result = TupleSlice {
34                data: slice.data.clone(),
35                bounds: new_bounds,
36            };
37            Some(result.into())
38        } else {
39            None
40        }
41    }
42
43    /// Returns the tuple's values as a slice
44    pub fn data(&self) -> &[KValue] {
45        self.deref()
46    }
47
48    /// Returns true if the tuple contains only immutable values
49    pub fn is_hashable(&self) -> bool {
50        self.iter().all(KValue::is_hashable)
51    }
52
53    /// Removes and returns the first value in the tuple
54    ///
55    /// The internal bounds of the tuple are adjusted to 'remove' the first element;
56    /// no change is made to the underlying tuple data.
57    pub fn pop_front(&mut self) -> Option<KValue> {
58        match &mut self.0 {
59            Inner::Full(data) => {
60                if let Some(value) = data.first().cloned() {
61                    *self = Self::from(TupleSlice {
62                        data: data.clone(),
63                        bounds: 1..data.len(),
64                    });
65                    Some(value)
66                } else {
67                    None
68                }
69            }
70            Inner::SliceLarge(slice) => {
71                if let Some(value) = slice.first().cloned() {
72                    Ptr::make_mut(slice).bounds.start += 1;
73                    Some(value)
74                } else {
75                    None
76                }
77            }
78            Inner::Slice(slice) => {
79                if let Some(value) = slice.first().cloned() {
80                    slice.bounds.start += 1;
81                    Some(value)
82                } else {
83                    None
84                }
85            }
86        }
87    }
88
89    /// Removes and returns the last value in the tuple
90    ///
91    /// The internal bounds of the tuple are adjusted to 'remove' the first element;
92    /// no change is made to the underlying tuple data.
93    pub fn pop_back(&mut self) -> Option<KValue> {
94        match &mut self.0 {
95            Inner::Full(data) => {
96                if let Some(value) = data.last().cloned() {
97                    *self = Self::from(TupleSlice {
98                        data: data.clone(),
99                        bounds: 0..data.len() - 1,
100                    });
101                    Some(value)
102                } else {
103                    None
104                }
105            }
106            Inner::SliceLarge(slice) => {
107                if let Some(value) = slice.last().cloned() {
108                    Ptr::make_mut(slice).bounds.end -= 1;
109                    Some(value)
110                } else {
111                    None
112                }
113            }
114            Inner::Slice(slice) => {
115                if let Some(value) = slice.last().cloned() {
116                    slice.bounds.end -= 1;
117                    Some(value)
118                } else {
119                    None
120                }
121            }
122        }
123    }
124
125    /// Returns true if the tuples refer to the same underlying data and have the same bounds
126    pub fn is_same_instance(&self, other: &Self) -> bool {
127        let ptr_and_bounds = |tuple: &Self| match &tuple.0 {
128            Inner::Full(data) => (Ptr::address(data), 0..data.len()),
129            Inner::Slice(slice) => (
130                Ptr::address(&slice.data),
131                slice.bounds.start as usize..slice.bounds.end as usize,
132            ),
133            Inner::SliceLarge(slice) => (Ptr::address(&slice.data), slice.bounds.clone()),
134        };
135
136        let (ptr_a, bounds_a) = ptr_and_bounds(self);
137        let (ptr_b, bounds_b) = ptr_and_bounds(other);
138
139        ptr_a == ptr_b && bounds_a == bounds_b
140    }
141
142    /// Renders the tuple into the provided display context
143    pub fn display(&self, ctx: &mut DisplayContext) -> Result<()> {
144        let id = Ptr::address(match &self.0 {
145            Inner::Full(data) => data,
146            Inner::SliceLarge(slice) => &slice.data,
147            Inner::Slice(slice) => &slice.data,
148        });
149        ctx.push_container(id);
150        ctx.append('(');
151
152        for (i, value) in self.iter().enumerate() {
153            if i > 0 {
154                ctx.append(", ");
155            }
156            value.display(ctx)?;
157        }
158
159        ctx.append(')');
160        ctx.pop_container();
161
162        Ok(())
163    }
164}
165
166impl Deref for KTuple {
167    type Target = [KValue];
168
169    fn deref(&self) -> &[KValue] {
170        match &self.0 {
171            Inner::Full(data) => data,
172            Inner::Slice(slice) => slice.deref(),
173            Inner::SliceLarge(slice) => slice.deref(),
174        }
175    }
176}
177
178thread_local! {
179    static EMPTY_TUPLE: Ptr<Vec<KValue>> = Vec::new().into();
180}
181
182impl Default for KTuple {
183    fn default() -> Self {
184        Self::from(EMPTY_TUPLE.with(|x| x.clone()))
185    }
186}
187
188impl From<Ptr<Vec<KValue>>> for KTuple {
189    fn from(data: Ptr<Vec<KValue>>) -> Self {
190        Self(Inner::Full(data))
191    }
192}
193
194impl From<Vec<KValue>> for KTuple {
195    fn from(data: Vec<KValue>) -> Self {
196        Self(Inner::Full(data.into()))
197    }
198}
199
200impl From<&[KValue]> for KTuple {
201    fn from(data: &[KValue]) -> Self {
202        Self(Inner::Full(data.to_vec().into()))
203    }
204}
205
206impl<const N: usize> From<&[KValue; N]> for KTuple {
207    fn from(data: &[KValue; N]) -> Self {
208        Self::from(data.as_slice())
209    }
210}
211
212impl From<TupleSlice> for KTuple {
213    fn from(slice: TupleSlice) -> Self {
214        match TupleSlice16::try_from(slice) {
215            Ok(slice16) => Self::from(slice16),
216            Err(slice) => Self(Inner::SliceLarge(slice.into())),
217        }
218    }
219}
220
221impl From<TupleSlice16> for KTuple {
222    fn from(slice: TupleSlice16) -> Self {
223        Self(Inner::Slice(slice))
224    }
225}
226
227#[derive(Clone)]
228struct TupleSlice {
229    data: Ptr<Vec<KValue>>,
230    bounds: Range<usize>,
231}
232
233impl Deref for TupleSlice {
234    type Target = [KValue];
235
236    fn deref(&self) -> &[KValue] {
237        // Safety: bounds have already been checked in the From impls and make_sub_tuple
238        unsafe { self.data.get_unchecked(self.bounds.clone()) }
239    }
240}
241
242impl From<Ptr<Vec<KValue>>> for TupleSlice {
243    fn from(data: Ptr<Vec<KValue>>) -> Self {
244        let bounds = 0..data.len();
245        Self { data, bounds }
246    }
247}
248
249impl From<TupleSlice16> for TupleSlice {
250    fn from(slice: TupleSlice16) -> Self {
251        Self {
252            data: slice.data,
253            bounds: u16_to_usize_range(slice.bounds),
254        }
255    }
256}
257
258// A slice with 16bit bounds, allowing it to be stored in KTuple without the overhead of additional
259// allocation.
260#[derive(Clone)]
261struct TupleSlice16 {
262    data: Ptr<Vec<KValue>>,
263    bounds: Range<u16>,
264    // A placeholder for the compiler to be able to perform niche optimization on KTuple
265    // so that its size can be kept down to 16 bytes.
266    // Although the size of TupleSlice16's fields is 12 on 64bit platforms,
267    // with padding the overall size is 16. Niche optimization isn't allowed to use
268    // padding bytes, so this 1 byte placeholder gives the compiler a legitimate spot
269    // to place the niche.
270    // Without niche optimization, the size of KTuple increases to 24 bytes,
271    // which then causes KValue's size to increase to 32, which is over the limit.
272    _niche_placeholder: ZeroU8,
273}
274
275impl Deref for TupleSlice16 {
276    type Target = [KValue];
277
278    fn deref(&self) -> &[KValue] {
279        // Safety: bounds have already been checked in the TryFrom impl
280        unsafe {
281            self.data
282                .get_unchecked(u16_to_usize_range(self.bounds.clone()))
283        }
284    }
285}
286
287impl TryFrom<TupleSlice> for TupleSlice16 {
288    type Error = TupleSlice;
289
290    fn try_from(slice: TupleSlice) -> std::result::Result<Self, Self::Error> {
291        usize_to_u16_range(slice.bounds.clone())
292            .map(|bounds| Self {
293                data: slice.data.clone(),
294                bounds,
295                _niche_placeholder: ZeroU8::Zero,
296            })
297            .ok_or(slice)
298    }
299}
300
301#[repr(u8)]
302#[derive(Clone)]
303enum ZeroU8 {
304    Zero = 0,
305}
306
307fn u16_to_usize_range(r: Range<u16>) -> Range<usize> {
308    r.start as usize..r.end as usize
309}
310
311fn usize_to_u16_range(r: Range<usize>) -> Option<Range<u16>> {
312    match (u16::try_from(r.start), u16::try_from(r.end)) {
313        (Ok(start), Ok(end)) => Some(start..end),
314        _ => None,
315    }
316}
317
318#[cfg(test)]
319mod tests {
320    use super::*;
321
322    #[test]
323    fn test_tuple_mem_size() {
324        assert!(std::mem::size_of::<KTuple>() <= 16);
325    }
326}