Skip to main content

kdb/
list.rs

1use crate::kbox::KBox;
2use crate::type_traits::*;
3use crate::{k::K, ConversionError};
4use crate::{k_type::KTypeCode, kapi};
5use std::slice;
6use std::{marker::PhantomData, slice::SliceIndex};
7use std::{mem, str};
8use std::{ops, ptr::NonNull};
9
10use std::{
11    iter::FromIterator,
12    slice::{Iter, IterMut},
13};
14
15unsafe fn as_slice_uninit<'a, T>(k: *mut K) -> &'a mut [mem::MaybeUninit<T>] {
16    let list = &(*k).union.list;
17    slice::from_raw_parts_mut(&list.g0 as *const _ as *mut _, list.n as usize)
18}
19
20unsafe fn as_slice_mut<'a, T>(k: *mut K) -> &'a mut [T] {
21    let list = &(*k).union.list;
22    slice::from_raw_parts_mut(&list.g0 as *const _ as *mut _, list.n as usize)
23}
24
25unsafe fn as_slice<'a, T>(k: *const K) -> &'a [T] {
26    let list = &(*k).union.list;
27    slice::from_raw_parts(&list.g0 as *const _ as *const _, list.n as usize)
28}
29
30/// Lists are the KDB equivalent of Rust's `Vec`. They contain collections of values
31/// and their contents be looked up by index.
32///
33/// # Examples
34/// ```
35/// use kdb::{KBox, List, list};
36///
37/// let mut l = list![i32; 1, 2, 3, 4, 5];
38/// l.push(6);
39/// let sl = &l[..]; // we can take slices and use them like other rust slices.
40/// assert_eq!(21, sl.iter().copied().sum());
41/// ```
42///
43/// # Appending to lists
44///
45/// When you append to lists in KDB, it will potentially reallocate the raw list
46/// which gives you a new and different pointer. So you can only extend lists safely if you own them,
47/// which means methods like `push` and `join` are only available on types of `KBox<List<_>>`.
48///
49/// You
50/// Notes for best performance: using `list!` or `.collect()` to create a populated list will typically result in better performance
51/// than using `new` and `push`. This is because they will, where possible, allocate a list large enough for
52/// all items up front. `push` will reallocate whenever needed.
53#[repr(transparent)]
54pub struct List<T> {
55    k: K,
56    _p: PhantomData<T>,
57}
58
59impl<T: KListable> List<T> {
60    /// Returns the contents of the list as a slice
61    #[inline]
62    pub fn as_slice(&self) -> &[T::ListItem] {
63        unsafe { as_slice(&self.k) }
64    }
65
66    /// Returns the contents of the list as a mutable slice
67    #[inline]
68    pub fn as_slice_mut(&mut self) -> &mut [T::ListItem] {
69        unsafe { as_slice_mut(&mut self.k) }
70    }
71
72    /// Returns an iterator over the list.
73    #[inline]
74    pub fn iter(&self) -> Iter<T::ListItem> {
75        self.as_slice().iter()
76    }
77
78    /// Returns the number of elements in the list.
79    #[inline]
80    pub fn len(&self) -> usize {
81        self.as_slice().len()
82    }
83
84    /// Returns true if the list has a length of 0.
85    #[inline]
86    pub fn is_empty(&self) -> bool {
87        self.len() == 0
88    }
89
90    /// Returns an iterator that allows modifying each value.
91    #[inline]
92    pub fn iter_mut(&mut self) -> IterMut<T::ListItem> {
93        self.as_slice_mut().iter_mut()
94    }
95
96    /// Returns a reference to an element or a subslice, depending on the type of index.
97    #[inline]
98    pub fn get<I: SliceIndex<[T::ListItem]>>(&self, index: I) -> Option<&I::Output> {
99        self.as_slice().get(index)
100    }
101
102    /// Returns a mutable reference to an element or a subslice, depending on the type of index.
103    #[inline]
104    pub fn get_mut<I: SliceIndex<[T::ListItem]>>(&mut self, index: I) -> Option<&mut I::Output> {
105        self.as_slice_mut().get_mut(index)
106    }
107}
108
109impl<T: KListable> KBox<List<T>> {
110    /// Creates a new empty list.
111    ///
112    /// Note that if you are converting a rust collection to a list, `collect` is a more efficient.
113    /// If you are creating a list with a set of known elements, use the `list!` macro.
114    #[inline]
115    pub fn new_list() -> Self {
116        unsafe { mem::transmute(kapi::ktn(T::LIST_TYPE_CODE.into(), 0)) }
117    }
118
119    /// Appends a list to this one, consuming it and adding it's elements to the new one.
120    #[inline]
121    pub fn join(&mut self, list: KBox<List<T>>) {
122        unsafe {
123            self.k = NonNull::new_unchecked(
124                kapi::jv(&mut (self.k.as_ptr() as *mut K), list.into_raw() as *const K) as *mut K as *mut List<T>
125            )
126        }
127    }
128
129    /// Appends an element to the end of the list.
130    #[inline]
131    pub fn push(&mut self, item: T::ListItem) {
132        unsafe {
133            self.k = NonNull::new_unchecked(T::join_to(item, self.k.as_ptr() as *mut K) as *mut List<T>);
134        }
135    }
136}
137
138impl<T: KListable> KTyped for List<T> {
139    const K_TYPE: KTypeCode = T::LIST_TYPE_CODE;
140}
141
142impl List<i8> {
143    /// Attempts to convert to a valid utf-8 string.
144    /// This will return an error if the string contains invalid utf-8 characters.
145    /// This function does not allocate.
146    pub fn try_as_str(&self) -> Result<&str, ConversionError> {
147        #[allow(clippy::transmute_ptr_to_ptr)]
148        let s: &[u8] = unsafe { mem::transmute(self.as_slice()) };
149        Ok(str::from_utf8(s)?)
150    }
151
152    /// Converts the symbol to a rust str without checking if it is valid.
153    ///
154    /// # Safety
155    ///
156    /// The string must be valid UTF-8.
157    /// It's length must be less than or equal to isize::MAX.
158    pub unsafe fn as_str_unchecked(&self) -> &str {
159        #[allow(clippy::transmute_ptr_to_ptr)]
160        let s: &[u8] = mem::transmute(self.as_slice());
161        str::from_utf8_unchecked(s)
162    }
163}
164
165impl<T> KObject for List<T> {
166    fn k_ptr(&self) -> *const K {
167        &self.k
168    }
169
170    fn k_ptr_mut(&mut self) -> *mut K {
171        &mut self.k
172    }
173}
174
175impl<T> private::Sealed for List<T> {}
176
177impl<T: KListable> ops::Index<ops::RangeFrom<usize>> for List<T> {
178    type Output = [T::ListItem];
179    fn index(&self, i: ops::RangeFrom<usize>) -> &Self::Output {
180        self.as_slice().index(i)
181    }
182}
183
184impl<T: KListable> ops::Index<ops::RangeTo<usize>> for List<T> {
185    type Output = [T::ListItem];
186    fn index(&self, i: ops::RangeTo<usize>) -> &Self::Output {
187        self.as_slice().index(i)
188    }
189}
190
191impl<T: KListable> ops::Index<ops::Range<usize>> for List<T> {
192    type Output = [T::ListItem];
193    fn index(&self, i: ops::Range<usize>) -> &Self::Output {
194        self.as_slice().index(i)
195    }
196}
197
198impl<T: KListable> ops::Index<usize> for List<T> {
199    type Output = T::ListItem;
200    fn index(&self, i: usize) -> &Self::Output {
201        self.as_slice().index(i)
202    }
203}
204
205impl<T: KListable> ops::Index<ops::RangeFull> for List<T> {
206    type Output = [T::ListItem];
207    fn index(&self, _: ops::RangeFull) -> &Self::Output {
208        self.as_slice()
209    }
210}
211
212impl<T: KListable> ops::IndexMut<ops::RangeFrom<usize>> for List<T> {
213    fn index_mut(&mut self, i: ops::RangeFrom<usize>) -> &mut Self::Output {
214        self.as_slice_mut().index_mut(i)
215    }
216}
217
218impl<T: KListable> ops::IndexMut<ops::RangeTo<usize>> for List<T> {
219    fn index_mut(&mut self, i: ops::RangeTo<usize>) -> &mut Self::Output {
220        self.as_slice_mut().index_mut(i)
221    }
222}
223
224impl<T: KListable> ops::IndexMut<ops::Range<usize>> for List<T> {
225    fn index_mut(&mut self, i: ops::Range<usize>) -> &mut Self::Output {
226        self.as_slice_mut().index_mut(i)
227    }
228}
229
230impl<T: KListable> ops::IndexMut<usize> for List<T> {
231    fn index_mut(&mut self, i: usize) -> &mut Self::Output {
232        self.as_slice_mut().index_mut(i)
233    }
234}
235
236impl<T: KListable> ops::IndexMut<ops::RangeFull> for List<T> {
237    fn index_mut(&mut self, _: ops::RangeFull) -> &mut Self::Output {
238        self.as_slice_mut()
239    }
240}
241
242impl<T: KListable> FromIterator<T::ListItem> for KBox<List<T>> {
243    fn from_iter<I: IntoIterator<Item = T::ListItem>>(iter: I) -> Self {
244        let iter = iter.into_iter();
245        //TODO: Better here would be to specialize on ExactSizeIterator. However I can't do that
246        // until specialization is stabilized. This is reliable modulo buggy Iterator impls.
247        match iter.size_hint() {
248            (x, Some(y)) if x == y => {
249                let k = unsafe { kapi::ktn(T::LIST_TYPE_CODE.into(), y as i64) };
250                let slice = unsafe { as_slice_uninit(k) };
251                slice
252                    .iter_mut()
253                    .zip(iter)
254                    .for_each(|(dest, src)| *dest = mem::MaybeUninit::new(src));
255                unsafe { mem::transmute(k) }
256            }
257            _ => {
258                let mut list = Self::new_list();
259                list.extend(iter);
260                list
261            }
262        }
263    }
264}
265
266impl<T: KListable> Extend<T::ListItem> for KBox<List<T>> {
267    fn extend<I: IntoIterator<Item = T::ListItem>>(&mut self, iter: I) {
268        for item in iter {
269            self.push(item);
270        }
271    }
272}
273
274/// Create a list from a set of supplied values.
275///
276/// #Example
277/// ```
278/// # use std::time::SystemTime;
279/// use kdb::{Any, KBox, List, Timestamp, list, symbol};
280/// // A list of ints, using type inference
281/// let a = list![i32; 1, 2, 3, 4, 5];
282/// assert_eq!(15, a.iter().copied().sum());
283///
284/// // Same list, without an explicit type in the macro
285/// let b: KBox<List<i32>> = list![1, 2, 3, 4, 5];
286///
287/// // Creating a mixed list requires the type
288/// let c = list![Any; 1, symbol("Hello"), Timestamp::from(SystemTime::now())];
289/// assert_eq!(c.len(), 3)
290#[macro_export]
291macro_rules! list{
292    ($($expr:expr),+$(,)?) => {
293        list![_;$($expr,)+]
294    };
295    ($t:ty; $($expr:expr),+$(,)?) => {
296        vec![$($expr.into(),)+].into_iter().collect::<$crate::KBox<$crate::List<$t>>>()
297    };
298}
299
300#[cfg(test)]
301mod test {
302    #![allow(clippy::float_cmp)]
303
304    use crate::{symbol, Date, DateTime, Minute, Month, Second, Symbol, Time, Timespan, Timestamp};
305
306    #[cfg(feature = "uuid")]
307    use uuid::Uuid;
308
309    #[test]
310    pub fn list_macro_creates_lists() {
311        assert_eq!(6u8, list![u8; 1, 2, 3 ].iter().copied().sum());
312        assert_eq!(6i8, list![i8; 1, 2, 3 ].iter().copied().sum());
313        assert_eq!(6i16, list![i16; 1i16, 2i16, 3i16].iter().copied().sum());
314        assert_eq!(6i32, list![i32; 1, 2, 3 ].iter().copied().sum());
315        assert_eq!(6i64, list![i64; 1, 2, 3 ].iter().copied().sum());
316
317        assert_eq!(6f32, list![f32; 1., 2., 3. ].iter().copied().sum());
318        assert_eq!(6f64, list![f64; 1., 2., 3. ].iter().copied().sum());
319        assert_eq!(
320            vec![true, false, true],
321            list![bool; true, false, true].iter().copied().collect::<Vec<_>>()
322        );
323
324        assert_eq!(
325            vec![Second::from(1), Second::from(2), Second::from(3)],
326            list![Second; 1, 2, 3].iter().copied().collect::<Vec<_>>()
327        );
328
329        assert_eq!(
330            vec![Minute::from(1), Minute::from(2), Minute::from(3)],
331            list![Minute; 1, 2, 3].iter().copied().collect::<Vec<_>>()
332        );
333
334        assert_eq!(
335            vec![Month::from(1), Month::from(2), Month::from(3)],
336            list![Month; 1, 2, 3].iter().copied().collect::<Vec<_>>()
337        );
338
339        assert_eq!(
340            vec![Time::from(1), Time::from(2), Time::from(3)],
341            list![Time; 1, 2, 3].iter().copied().collect::<Vec<_>>()
342        );
343        assert_eq!(
344            vec![Date::new(2020, 1, 1), Date::new(2020, 1, 2), Date::new(2020, 1, 3)],
345            list![Date; Date::new(2020, 1, 1), Date::new(2020, 1, 2), Date::new(2020, 1, 3)]
346                .iter()
347                .copied()
348                .collect::<Vec<_>>()
349        );
350        assert_eq!(
351            vec![DateTime::from(1.), DateTime::from(2.), DateTime::from(3.)],
352            list![DateTime; 1., 2., 3.].iter().copied().collect::<Vec<_>>()
353        );
354        assert_eq!(
355            vec![Timestamp::from(1), Timestamp::from(2), Timestamp::from(3)],
356            list![Timestamp; 1, 2, 3].iter().copied().collect::<Vec<_>>()
357        );
358        assert_eq!(
359            vec![Timespan::from(1), Timespan::from(2), Timespan::from(3)],
360            list![Timespan; 1, 2, 3].iter().copied().collect::<Vec<_>>()
361        );
362
363        assert_eq!(
364            vec![symbol("Hello"), symbol("World")],
365            list![Symbol; symbol("Hello"), symbol("World")]
366                .iter()
367                .copied()
368                .collect::<Vec<_>>()
369        );
370
371        #[cfg(feature = "uuid")]
372        assert_eq!(
373            vec![Uuid::from_u128(1), Uuid::from_u128(2), Uuid::from_u128(3)],
374            list![Uuid;Uuid::from_u128(1), Uuid::from_u128(2), Uuid::from_u128(3)]
375                .iter()
376                .copied()
377                .collect::<Vec<_>>()
378        );
379    }
380}