1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
//! Iteration of the [`Raw`][crate::raw::Raw] map.

use std::marker::PhantomData;
use std::mem;
use std::sync::atomic::Ordering;

use arrayvec::ArrayVec;
use crossbeam_epoch::{Guard, Shared};

use super::config::Config;
use super::{load_data, nf, Inner, NodeFlags, Raw, LEVEL_CELLS, MAX_LEVELS};

unsafe fn extend_lifetime<'a, 'b, T: 'a + 'b>(s: Shared<'a, T>) -> Shared<'b, T> {
    mem::transmute(s)
}

struct Level<'a> {
    ptr: Shared<'a, Inner>,
    idx: usize,
}

// Notes about the lifetimes:
// The 'a here is actually a lie. We need two things from lifetimes:
// * We must not outlive the map we are iterating through (because the drop just outright destroys
//   the data).
// * The pointers must not outlive the pin we hold.
// * We do not mind us (or the pin) moving around in memory, we are only interested in when its
//   destructor is called. The references don't actually point inside the pin itself.
//
// The lifetime of the pin is the same as of the pointers we store inside of us. We check the
// lifetime relation of the map and us on the constructor, so we won't outlive the map. But
// technically, the lifetime should be something like `'self`, but it's not possible to describe.
//
// Therefore we have to make very sure to never return a reference with the 'a lifetime.
//
// For the same technical reasons, we do the extend_lifetime thing. It would be great if someone
// knew a better trick ‒ while this is probably correct, something the compiler could check would
// be much better.

/// An iterator-like structure for the raw trie.
///
/// This wraps the map and provides borrowed instances of the payloads. Note that due to the
/// borrowing from the iterator itself, it is not possible to create the true `Iterator`. As this
/// is used to implement the iterators of the wrapper convenience types, this is not considered a
/// serious limitation.
///
/// # Quirks
///
/// As noted in the crate-level documentation, changes to the content of the map done during the
/// lifetime of the iterator (both in the current thread and other threads) may or may not be
/// reflected in the returned values.
pub struct Iter<'a, C, S>
where
    C: Config,
{
    pin: Guard,
    levels: ArrayVec<[Level<'a>; MAX_LEVELS + 1]>,
    _map: PhantomData<&'a Raw<C, S>>,
}

impl<'a, C, S> Iter<'a, C, S>
where
    C: Config,
{
    /// Creates a new iterator, borrowing from the map.
    pub fn new<'m: 'a>(map: &'m Raw<C, S>) -> Self {
        let mut levels = ArrayVec::new();
        let pin = crossbeam_epoch::pin();
        let ptr = map.root.load(Ordering::Acquire, &pin);
        let ptr = unsafe { extend_lifetime(ptr) };
        levels.push(Level { ptr, idx: 0 });
        Iter {
            pin,
            levels,
            _map: PhantomData,
        }
    }

    // Not an iterator because this borrows out of the iterator itself (and effectively its pin).
    /// Produces another value, just like `Iterator::next`, except the value is bound to the
    /// lifetime of the iterator structure.
    #[allow(clippy::should_implement_trait)]
    pub fn next(&mut self) -> Option<&C::Payload> {
        loop {
            let top = self.levels.last_mut()?;

            let flags = nf(top.ptr);
            if top.ptr.is_null() {
                self.levels.pop();
            } else if flags.contains(NodeFlags::DATA) {
                let data = unsafe { load_data::<C>(top.ptr) };
                if top.idx < data.len() {
                    let result = &data[top.idx];
                    top.idx += 1;
                    return Some(result);
                } else {
                    self.levels.pop();
                }
            } else if top.idx < LEVEL_CELLS {
                let node = unsafe { top.ptr.deref() };
                let ptr = node.0[top.idx].load(Ordering::Acquire, &self.pin);
                let ptr = unsafe { extend_lifetime(ptr) };
                top.idx += 1;
                self.levels.push(Level { ptr, idx: 0 });
            } else {
                self.levels.pop();
            }
        }
    }
}