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}