x_map/
maps.rs

1//! The primary maps module for `x-map`. This module contains all code relating to the various
2//! maps that are packaged as part of `x-map`.
3//!
4//! Many map types in this module utilize raw pointers. This unsafe code is managed internally by each
5//! map contained within, and this unsafe code should be abstracted away from library users.
6//!
7//! To support multiple environments, many maps will also contain functions for low-level operations,
8//! so that in environments such as `#![no_std]` environments, these maps are still usable.
9//!
10//! As a rule of thumb, all functions in which it is expected to retrieve a value will retrieve a
11//! reference to that value, not a copy of it.
12
13use crate::error::{CIndexMapError, MapErrorKind};
14use crate::util::mem_cmp;
15use core::alloc::Layout;
16use core::fmt::Debug;
17use core::ptr;
18
19#[derive(Debug)]
20pub struct CIndexMap<K, V> {
21    /// The initial layout used to initialize this map's keys store.
22    key_layout: Layout,
23
24    /// The initial layout used to initialize this map's values store.
25    val_layout: Layout,
26
27    /// A pointer to a space in memory in which keys will be stored at.
28    keys: *mut K,
29
30    /// A pointer to a space in memory in which values will be stored at.
31    values: *mut V,
32
33    /// The total allocated memory for this map.
34    ///
35    /// Everything in the range `self.pos + 1..self.cap` is not uninitialized, but may be old
36    /// or unwanted.
37    cap: isize,
38
39    /// Position of the cursor where the **last valid value** was inserted. `keys[pos]` and
40    /// `values[pos]` will always be initialized with valid data.
41    ///
42    /// All elements 0 to `self.pos` are guaranteed to be initialized.
43    pos: isize,
44}
45
46impl<K, V> CIndexMap<K, V> {
47    /// Constructs a new `CIndexMap`.
48    ///
49    /// # Panics
50    /// When `size_of::<K>` or `size_of::<T>` is 0.
51    pub fn new() -> CIndexMap<K, V> {
52        if size_of::<K>() == 0 || size_of::<V>() == 0 {
53            panic!("Cannot initialize CIndexMap with ZSTs!");
54        }
55
56        let key_layout = unsafe {
57            Layout::from_size_align(size_of::<K>() * 1, align_of::<K>()).unwrap()
58        };
59        let val_layout = unsafe {
60            Layout::from_size_align(size_of::<V>() * 1, align_of::<V>()).unwrap()
61        };
62
63        // SAFETY:
64        // ZSTs are not supported, this point in the initializing cannot be reached
65        // if a ZST is provided.
66        let key_ptr = unsafe {
67            std::alloc::alloc(key_layout)
68        } as *mut K;
69
70        // SAFETY:
71        // ZSTs are not supported, this point in the initializing cannot be reached
72        // if a ZST is provided.
73        let val_ptr = unsafe {
74            std::alloc::alloc(val_layout)
75        } as *mut V;
76
77        CIndexMap {
78            key_layout,
79            val_layout,
80            keys: key_ptr,
81            values: val_ptr,
82            cap: 1,
83            pos: -1,
84        }
85    }
86
87    /// Inserts a new key/value pair into the map.
88    ///
89    /// This function returns `Ok(())` if the key/value pair was inserted successfully.
90    pub fn insert(&mut self, key: K, value: V) -> crate::result::Result<()> {
91        if (self.pos + 1) >= self.cap {
92            let new_cap = self.cap + 2;
93
94            let new_key_ptr = unsafe {
95                std::alloc::realloc(
96                    self.keys as *mut u8,
97                    self.key_layout,
98                    size_of::<K>() * (new_cap as usize),
99                ) as *mut K
100            };
101
102            let new_val_ptr = unsafe {
103                std::alloc::realloc(
104                    self.values as *mut u8,
105                    self.val_layout,
106                    size_of::<V>() * (new_cap as usize),
107                ) as *mut V
108            };
109
110            if (new_val_ptr == ptr::null_mut()) || (new_key_ptr == ptr::null_mut()) {
111                return Err(CIndexMapError::new(
112                    MapErrorKind::AllocationError,
113                    "Error when attempting to allocate map memory."
114                ));
115            } else {
116                self.keys = new_key_ptr;
117                self.values = new_val_ptr;
118                self.cap = new_cap;
119            }
120        }
121
122        self.pos += 1;
123
124        // SAFETY:
125        // The data was properly aligned during initialization.
126        // The memory being pointed to was verified to be between 0 (the pointer starting position)
127        // and `self.cap`, which is the current amount of allocated space.
128        unsafe {
129            self.keys.offset(self.pos).write(key);
130            self.values.offset(self.pos).write(value);
131        }
132
133        Ok(())
134    }
135
136    /// Removes an element at the specified index.
137    pub fn remove(&mut self, index: usize) -> crate::result::Result<()> {
138        if index > (self.pos as usize) {
139            Err(
140                CIndexMapError::new(
141                    MapErrorKind::AccessError,
142                    "Attempted to access a map index that surpasses the bounds of the current map."
143                )
144            )
145        } else {
146            // SAFETY:
147            // We've determined that the index requested fits within the range of valid data.
148            unsafe {
149                // Shift all elements between `index` and `self.pos` back by one by copying
150                // the pointer and overwriting it at the position specified by `index`.
151                ptr::copy(
152                    self.keys.offset((index as isize) + 1),
153                    self.keys.offset(index as isize),
154                    (self.pos as usize - index),
155                );
156
157                ptr::copy(
158                    self.values.offset((index as isize) + 1),
159                    self.values.offset(index as isize),
160                    self.pos as usize - index,
161                );
162            }
163
164            // Decrement self.pos to ensure this is always set as the position of the
165            // last valid inserted value.
166            self.pos -= 1;
167            Ok(())
168        }
169    }
170
171    /// Returns the key to the entry at the specified index.
172    ///
173    /// Use `CIndexMap::get` to get the value of the entry based off of the key.
174    pub fn index(&self, index: usize) -> crate::result::Result<&K> {
175        if index > (self.pos as usize) {
176            Err(
177                CIndexMapError::new(
178                    MapErrorKind::AccessError,
179                    "Attempted to access a map index that surpasses the bounds of the current map."
180                )
181            )
182        } else {
183            // SAFETY:
184            // All elements `0..self.pos` should be already be initialized.
185            unsafe {
186                Ok(
187                    &*self.keys.offset(index as isize)
188                )
189            }
190        }
191    }
192
193    /// Gets the value associated with the specified key.
194    ///
195    /// This function is preferred for when the underlying Key/Value types for this map cannot,
196    /// for some reason, implement `PartialEq`.
197    ///
198    /// This performs a raw memory comparison between the supplied value and the keys in the
199    /// current map. This comparison doesn't work on a lot of dynamically sized types, like `String`
200    /// and `Vec`.
201    /// # Correct usage
202    /// ```
203    /// use x_map::maps::CIndexMap;
204    /// let mut map = CIndexMap::new();
205    /// map.insert("foo", "bar").unwrap();
206    ///
207    /// // Prints `map.get("foo").unwrap() = "bar"`
208    /// dbg!(map.get("foo").unwrap());
209    ///
210    /// // Prints `map.get_no_peq("foo").unwrap() = "bar"`
211    /// dbg!(map.get_no_peq("foo").unwrap());
212    /// ```
213    ///
214    ///
215    /// # Incorrect usage
216    /// ```
217    /// use x_map::maps::CIndexMap;
218    /// let string_one = String::from("foo");
219    /// let string_two = String::from("bar");
220    ///
221    /// let mut map = CIndexMap::new();
222    /// map.insert(string_one.to_string(), string_two.to_string()).unwrap();
223    ///
224    ///
225    /// // Prints `map.get(string_one.to_string()) = Some("bar")`
226    /// dbg!(map.get(string_one.to_string()));
227    ///
228    /// // Prints `map.get_no_peq(string_one.to_string()) = None`
229    /// dbg!(map.get_no_peq(string_one.to_string()));
230    /// ```
231    pub fn get_no_peq(&self, key: K) -> Option<&V> {
232        let key_ptr = ptr::from_ref(&key);
233        let mut i = 0;
234
235        // SAFETY:
236        // We only iterate the memory space between 0 and self.pos, which is always initialized.
237        //
238        // The type system guarantees that the supplied `key`, the position of the pointer, and the
239        // `size_of::<K>` are all already valid. This means `mem_cmp` can safely be called,
240        // as the data in `self.keys[0..self.pos]` is already initialized, and is valid for both
241        // the size of `key`, and the size of type `K`.
242        unsafe {
243            while i <= self.pos {
244                let cmp = mem_cmp(key_ptr as *const u8, self.keys.offset(i) as *const u8, size_of::<K>());
245                match cmp {
246                    None => { return Some(&*self.values.offset(i)); }
247                    Some(_) => {}
248                }
249
250                i += 1;
251            }
252        }
253
254        None
255    }
256}
257
258impl<K: PartialEq, V> CIndexMap<K, V> {
259    /// Gets the value associated with the specified key.
260    ///
261    /// Multiple implementations exist for `get`:
262    /// - When `K` does not implement `PartialEq`, `get` will perform bytewise comparison between
263    ///   the input and the map keys. This is a fallback implementation, and will not always produce
264    ///   expected results. This implementation will have unexpected results on dynamically sized
265    ///   types, such as `String` and `Vec`.
266    ///
267    /// - When `K` implements `PartialEq`, `get` will call `PartialEq::partial_eq()` for comparison
268    ///   between the input and the map keys. This is the preferred implementation, and will produce
269    ///   the expected results.
270    pub fn get(&self, key: K) -> Option<&V> {
271        // SAFETY:
272        // We know that all elements from (self.keys + 0) to (self.keys + self.pos) are initialized.
273        // Thus, reading from memory for each allocation of size_of::<K> is correct.
274        unsafe {
275            for i in 0..(self.pos + 1) {
276                if *self.keys.add(i as usize) == key {
277                    return Some(&*self.values.add(i as usize));
278                }
279            }
280        };
281
282        None
283    }
284
285    /// Checks if the map contains the provided key.
286    ///
287    /// - If it does, this function returns `true`,
288    /// - If it does not, this function returns `false`.
289    pub fn contains_key(&self, key: K) -> bool {
290        // SAFETY:
291        // We only iterate over the data between 0 and self.pos, which is always initialized.
292        unsafe {
293            for i in 0..(self.pos + 1) {
294                if *self.keys.add(i as usize) == key {
295                    return true;
296                }
297            }
298        };
299
300        false
301    }
302}
303
304impl<K, V: PartialEq> CIndexMap<K, V> {
305    /// Checks if the map contains the provided value.
306    ///
307    /// - If it does, this function returns `true`,
308    /// - If it does not, this function returns `false`.
309    pub fn contains_value(&self, value: V) -> bool {
310        // SAFETY:
311        // We only iterate over the data between 0 and self.pos, which is always initialized.
312        unsafe {
313            for i in 0..(self.pos + 1) {
314                if *self.values.add(i as usize) == value {
315                    return true;
316                }
317            }
318        };
319
320        false
321    }
322}