brownstone/
move_builder.rs

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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*!
A misuse-immune array builder. See [`ArrayBuilder`] for details and examples.
*/

use crate::builder;

/**
The result of pushing to an [`ArrayBuilder`]. If the push resulted in a
full array, the array is returned directly; otherwise, the builder is
returned with updated state. See [`ArrayBuilder`] for details and examples.
*/
#[derive(Debug, Clone)]
pub enum PushResult<T, const N: usize> {
    Full([T; N]),
    NotFull(ArrayBuilder<T, N>),
}

/**
Misuse-immune array builder

This `ArrayBuilder` uses move semantics to provide an array builder that
never panics or returns errors. Each call to [`push`][ArrayBuilder::push]
takes `self` by move, and returns either the builder (if it's not full yet)
or the fully initialized array (if it is). The builder therefore can only
exist while the array being built isn't full yet.


```
use brownstone::move_builder::{ArrayBuilder, PushResult};

let builder = match ArrayBuilder::start() {
    PushResult::Full(_) => unreachable!(),
    PushResult::NotFull(builder) => builder,
};

assert!(builder.is_empty());

let builder = match builder.push(5) {
    PushResult::Full(_) => unreachable!(),
    PushResult::NotFull(builder) => builder,
};

assert_eq!(builder.len(), 1);

let builder = match builder.push(6) {
    PushResult::Full(_) => unreachable!(),
    PushResult::NotFull(builder) => builder,
};

assert_eq!(builder.len(), 2);
assert_eq!(builder.finished_slice(), [5, 6]);

let array = match builder.push(7) {
    PushResult::Full(array) => array,
    PushResult::NotFull(_) => unreachable!(),
};

assert_eq!(array, [5, 6, 7]);
```
*/
#[derive(Debug, Clone)]
pub struct ArrayBuilder<T, const N: usize> {
    builder: builder::ArrayBuilder<T, N>,
    // Invariant: while this instance exists, the builder is not full
}

impl<T, const N: usize> ArrayBuilder<T, N> {
    /**
    Create a new [`ArrayBuilder`]. If `N == 0`, immediately return an empty
    array, rather than the builder.
    */
    #[inline]
    pub fn start() -> PushResult<T, N> {
        // Invariant preserved: if N == 0, return the array immediately
        match builder::ArrayBuilder::new().try_finish() {
            Ok(array) => PushResult::Full(array),
            Err(builder) => PushResult::NotFull(Self { builder }),
        }
    }

    /**
    Returns true if there are no initialized elements in the array.
    */
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.builder.is_empty()
    }

    /**
    Returns the number of initialized elements in the array. Guaranteed to
    be less than `N`.
    */
    #[inline]
    pub fn len(&self) -> usize {
        self.builder.len()
    }

    /**
    Add a new initialized element to the array. If this causes the array
    to become fully initialized, the array is returned; otherwise, a new
    builder is returned.
    */
    #[inline]
    pub fn push(self, value: T) -> PushResult<T, N> {
        // Destructure self to ensure that an ArrayBuilder never exists with
        // a full array
        let mut builder = self.builder;

        // The unsafes here have debug_asserts checking their correctness.

        // Invariant presumed: the array is not full, so this push is safe
        match unsafe { builder.push_unchecked(value) } {
            // Invariant preserved: We only create a new ArrayBuilder if the
            // array is not full yet
            builder::PushResult::NotFull => PushResult::NotFull(Self { builder }),
            builder::PushResult::Full => {
                // Invariant preserved: if this push fills the array, the array
                // is returned immediately
                PushResult::Full(unsafe { builder.finish_unchecked() })
            }
        }
    }

    /**
    Get the slice of the array that has already been initialized.
    */
    #[inline]
    pub fn finished_slice(&self) -> &[T] {
        self.builder.finished_slice()
    }

    /**
    Get the mutable slice of the array that has already been initialized.
    */
    #[inline]
    pub fn finished_slice_mut(&mut self) -> &mut [T] {
        self.builder.finished_slice_mut()
    }
}