tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

freelist.rs (7682B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 //! A generic backing store for caches.
      6 //!
      7 //! `FreeList` is a simple vector-backed data structure where each entry in the
      8 //! vector contains an Option<T>. It maintains an index-based (rather than
      9 //! pointer-based) free list to efficiently locate the next unused entry. If all
     10 //! entries are occupied, insertion appends a new element to the vector.
     11 //!
     12 //! It also supports both strong and weak handle semantics. There is exactly one
     13 //! (non-Clonable) strong handle per occupied entry, which must be passed by
     14 //! value into `free()` to release an entry. Strong handles can produce an
     15 //! unlimited number of (Clonable) weak handles, which are used to perform
     16 //! lookups which may fail of the entry has been freed. A per-entry epoch ensures
     17 //! that weak handle lookups properly fail even if the entry has been freed and
     18 //! reused.
     19 //!
     20 //! TODO(gw): Add an occupied list head, for fast iteration of the occupied list
     21 //! to implement retain() style functionality.
     22 
     23 use std::{fmt, u32};
     24 use std::marker::PhantomData;
     25 
     26 #[derive(Debug, Copy, Clone, MallocSizeOf, PartialEq)]
     27 #[cfg_attr(feature = "capture", derive(Serialize))]
     28 #[cfg_attr(feature = "replay", derive(Deserialize))]
     29 struct Epoch(u32);
     30 
     31 impl Epoch {
     32    /// Mints a new epoch.
     33    ///
     34    /// We start at 1 so that 0 is always an invalid epoch.
     35    fn new() -> Self {
     36        Epoch(1)
     37    }
     38 
     39    /// Returns an always-invalid epoch.
     40    fn invalid() -> Self {
     41        Epoch(0)
     42    }
     43 }
     44 
     45 #[cfg_attr(feature = "capture", derive(Serialize))]
     46 #[cfg_attr(feature = "replay", derive(Deserialize))]
     47 #[derive(MallocSizeOf)]
     48 pub struct FreeListHandle<M> {
     49    index: u32,
     50    epoch: Epoch,
     51    _marker: PhantomData<M>,
     52 }
     53 
     54 /// More-compact textual representation for debug logging.
     55 impl<M> fmt::Debug for FreeListHandle<M> {
     56    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
     57        f.debug_struct("StrongHandle")
     58            .field("index", &self.index)
     59            .field("epoch", &self.epoch.0)
     60            .finish()
     61    }
     62 }
     63 
     64 impl<M> FreeListHandle<M> {
     65    pub fn weak(&self) -> WeakFreeListHandle<M> {
     66        WeakFreeListHandle {
     67            index: self.index,
     68            epoch: self.epoch,
     69            _marker: PhantomData,
     70        }
     71    }
     72 
     73    pub fn invalid() -> Self {
     74        Self {
     75            index: 0,
     76            epoch: Epoch::invalid(),
     77            _marker: PhantomData,
     78        }
     79    }
     80 
     81    /// Returns true if this handle and the supplied weak handle reference
     82    /// the same underlying location in the freelist.
     83    pub fn matches(&self, weak_handle: &WeakFreeListHandle<M>) -> bool {
     84        self.index == weak_handle.index &&
     85        self.epoch == weak_handle.epoch
     86    }
     87 }
     88 
     89 impl<M> Clone for WeakFreeListHandle<M> {
     90    fn clone(&self) -> Self {
     91        WeakFreeListHandle {
     92            index: self.index,
     93            epoch: self.epoch,
     94            _marker: PhantomData,
     95        }
     96    }
     97 }
     98 
     99 impl<M> PartialEq for WeakFreeListHandle<M> {
    100    fn eq(&self, other: &Self) -> bool {
    101        self.index == other.index && self.epoch == other.epoch
    102    }
    103 }
    104 
    105 #[cfg_attr(feature = "capture", derive(Serialize))]
    106 #[cfg_attr(feature = "replay", derive(Deserialize))]
    107 #[derive(MallocSizeOf)]
    108 pub struct WeakFreeListHandle<M> {
    109    index: u32,
    110    epoch: Epoch,
    111    _marker: PhantomData<M>,
    112 }
    113 
    114 /// More-compact textual representation for debug logging.
    115 impl<M> fmt::Debug for WeakFreeListHandle<M> {
    116    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    117        f.debug_struct("WeakHandle")
    118            .field("index", &self.index)
    119            .field("epoch", &self.epoch.0)
    120            .finish()
    121    }
    122 }
    123 
    124 impl<M> WeakFreeListHandle<M> {
    125    /// Returns an always-invalid handle.
    126    pub fn invalid() -> Self {
    127        Self {
    128            index: 0,
    129            epoch: Epoch::invalid(),
    130            _marker: PhantomData,
    131        }
    132    }
    133 }
    134 
    135 #[derive(Debug, MallocSizeOf)]
    136 #[cfg_attr(feature = "capture", derive(Serialize))]
    137 #[cfg_attr(feature = "replay", derive(Deserialize))]
    138 struct Slot<T> {
    139    next: Option<u32>,
    140    epoch: Epoch,
    141    value: Option<T>,
    142 }
    143 
    144 #[derive(Debug, MallocSizeOf)]
    145 #[cfg_attr(feature = "capture", derive(Serialize))]
    146 #[cfg_attr(feature = "replay", derive(Deserialize))]
    147 pub struct FreeList<T, M> {
    148    slots: Vec<Slot<T>>,
    149    free_list_head: Option<u32>,
    150    active_count: usize,
    151    _marker: PhantomData<M>,
    152 }
    153 
    154 impl<T, M> FreeList<T, M> {
    155    /// Mints a new `FreeList` with no entries.
    156    ///
    157    /// Triggers a 1-entry allocation.
    158    pub fn new() -> Self {
    159        // We guarantee that we never have zero entries by starting with one
    160        // free entry. This allows WeakFreeListHandle::invalid() to work
    161        // without adding any additional branches.
    162        let first_slot = Slot {
    163            next: None,
    164            epoch: Epoch::new(),
    165            value: None,
    166        };
    167        FreeList {
    168            slots: vec![first_slot],
    169            free_list_head: Some(0),
    170            active_count: 0,
    171            _marker: PhantomData,
    172        }
    173    }
    174 
    175    pub fn clear(&mut self) {
    176        self.slots.truncate(1);
    177        self.slots[0] = Slot {
    178            next: None,
    179            epoch: Epoch::new(),
    180            value: None,
    181        };
    182        self.free_list_head = Some(0);
    183        self.active_count = 0;
    184    }
    185 
    186    #[allow(dead_code)]
    187    pub fn get(&self, id: &FreeListHandle<M>) -> &T {
    188        self.slots[id.index as usize].value.as_ref().unwrap()
    189    }
    190 
    191    #[allow(dead_code)]
    192    pub fn get_mut(&mut self, id: &FreeListHandle<M>) -> &mut T {
    193        self.slots[id.index as usize].value.as_mut().unwrap()
    194    }
    195 
    196    pub fn get_opt(&self, id: &WeakFreeListHandle<M>) -> Option<&T> {
    197        let slot = &self.slots[id.index as usize];
    198        if slot.epoch == id.epoch {
    199            slot.value.as_ref()
    200        } else {
    201            None
    202        }
    203    }
    204 
    205    pub fn get_opt_mut(&mut self, id: &WeakFreeListHandle<M>) -> Option<&mut T> {
    206        let slot = &mut self.slots[id.index as usize];
    207        if slot.epoch == id.epoch {
    208            slot.value.as_mut()
    209        } else {
    210            None
    211        }
    212    }
    213 
    214    pub fn insert(&mut self, item: T) -> FreeListHandle<M> {
    215        self.active_count += 1;
    216 
    217        match self.free_list_head {
    218            Some(free_index) => {
    219                let slot = &mut self.slots[free_index as usize];
    220 
    221                // Remove from free list.
    222                self.free_list_head = slot.next;
    223                slot.next = None;
    224                slot.value = Some(item);
    225 
    226                FreeListHandle {
    227                    index: free_index,
    228                    epoch: slot.epoch,
    229                    _marker: PhantomData,
    230                }
    231            }
    232            None => {
    233                let index = self.slots.len() as u32;
    234                let epoch = Epoch::new();
    235 
    236                self.slots.push(Slot {
    237                    next: None,
    238                    epoch,
    239                    value: Some(item),
    240                });
    241 
    242                FreeListHandle {
    243                    index,
    244                    epoch,
    245                    _marker: PhantomData,
    246                }
    247            }
    248        }
    249    }
    250 
    251    pub fn free(&mut self, id: FreeListHandle<M>) -> T {
    252        self.active_count -= 1;
    253        let slot = &mut self.slots[id.index as usize];
    254        slot.next = self.free_list_head;
    255        slot.epoch = Epoch(slot.epoch.0 + 1);
    256        self.free_list_head = Some(id.index);
    257        slot.value.take().unwrap()
    258    }
    259 
    260    #[allow(dead_code)]
    261    pub fn len(&self) -> usize {
    262        self.active_count
    263    }
    264 }