Skip to main content

once_list2/
any.rs

1// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use ::allocator_api2::alloc;
16use ::allocator_api2::alloc::Allocator;
17use ::allocator_api2::boxed::Box;
18use ::std::any::Any;
19use ::std::ptr::NonNull;
20
21use crate::cache_mode::CacheMode;
22use crate::cons::Cons;
23use crate::once_list::OnceListCore;
24
25impl<A: Allocator + Clone, M> OnceListCore<dyn Any, A, M>
26where
27    M: CacheMode<dyn Any, A>,
28{
29    /// Pushes an aribitrary value to the list, and returns the reference to that value.
30    ///
31    /// ```rust
32    /// use once_list2::OnceList;
33    /// use std::any::Any;
34    ///
35    /// let list = OnceList::<dyn Any>::new();
36    /// list.push_any(1);
37    /// list.push_any("hello");
38    ///
39    /// assert_eq!(list.iter().nth(0).unwrap().downcast_ref::<i32>(), Some(&1));
40    /// assert_eq!(list.iter().nth(1).unwrap().downcast_ref::<&str>(), Some(&"hello"));
41    /// ```
42    pub fn push_any<T: Any>(&self, val: T) -> &T {
43        let sized_box = Box::new_in(Cons::<T, dyn Any, A>::new(val), A::clone(&self.alloc));
44        // Because we are using the non-standard `Box`, we need to manually do the unsized coercions...
45        // Watching the PR:
46        // https://github.com/zakarumych/allocator-api2/pull/23
47        let unsized_box = unsafe {
48            let (sized_ptr, alloc) = Box::into_raw_with_allocator(sized_box);
49            // Pointer unsized coercion!
50            let unsized_ptr: *mut Cons<dyn Any, dyn Any, A> = sized_ptr;
51            Box::from_raw_in(unsized_ptr, alloc)
52        };
53        self.push_inner(
54            unsized_box,
55            // Safe because we know the given value is type `T`.
56            |c| match c.downcast_ref::<T>() {
57                Some(v) => v,
58                None => unreachable!("push_any inserted a value of a different type"),
59            },
60        )
61    }
62}
63
64impl<A: Allocator, M> OnceListCore<dyn Any, A, M>
65where
66    M: CacheMode<dyn Any, A>,
67{
68    /// Finds the first value in the list that is the same type as `T`, and returns the reference to that value.
69    ///
70    /// ```rust
71    /// use once_list2::OnceList;
72    /// use std::any::Any;
73    ///
74    /// let list = OnceList::<dyn Any>::new();
75    /// list.push_any(1);
76    /// list.push_any("hello");
77    ///
78    /// assert_eq!(list.find_by_type::<i32>(), Some(&1));
79    /// assert_eq!(list.find_by_type::<&str>(), Some(&"hello"));
80    /// assert_eq!(list.find_by_type::<Vec<u8>>(), None);
81    /// ```
82    pub fn find_by_type<T: Any>(&self) -> Option<&T> {
83        self.iter().find_map(|val| val.downcast_ref())
84    }
85
86    /// Removes the first value in the list that is the same type as `T`, and returns the value.
87    ///
88    /// ```rust
89    /// use once_list2::OnceList;
90    /// use std::any::Any;
91    ///
92    /// let mut list = OnceList::<dyn Any>::new();
93    /// list.push_any(1);
94    /// list.push_any("hello");
95    ///
96    /// assert_eq!(list.remove_by_type::<i32>(), Some(1));
97    ///
98    /// assert_eq!(list.len(), 1);
99    /// assert_eq!(list.iter().nth(0).unwrap().downcast_ref::<&str>(), Some(&"hello"));
100    /// ```
101    pub fn remove_by_type<T: Any>(&mut self) -> Option<T> {
102        self.remove_inner(
103            |v| v.is::<T>(),
104            |boxed_cons| {
105                let cons_layout = alloc::Layout::for_value::<Cons<_, _, _>>(&boxed_cons);
106                let (cons_ptr, alloc) = Box::into_raw_with_allocator(boxed_cons);
107
108                let Cons {
109                    next: next_ref,
110                    val: val_any_ref,
111                } = unsafe { &*cons_ptr };
112                // Drop the `next` field.
113                unsafe { ::std::ptr::read(next_ref) };
114
115                let val_ref = match <dyn Any>::downcast_ref::<T>(val_any_ref) {
116                    Some(v) => v,
117                    None => unreachable!("remove_by_type predicate matched but downcast failed"),
118                };
119                let val = unsafe { ::std::ptr::read(val_ref) };
120
121                unsafe {
122                    alloc.deallocate(NonNull::new_unchecked(cons_ptr as *mut u8), cons_layout);
123                }
124
125                val
126            },
127        )
128    }
129}