Skip to main content

automation_structures/connectives/
buffer.rs

1//! Reusable Buffer connective contract.
2//!
3//! Buffer is a connective role rather than a second queue implementation. It owns the generic
4//! retained-sequence bound used by compositions. A compact ring, fixed array, or other physical
5//! layout receives Buffer credit only through a checked view satisfying this contract.
6
7use vstd::prelude::*;
8
9use crate::value_eq::ValueEq;
10
11verus! {
12
13/// A retained logical sequence fits within its admitted capacity.
14pub open spec fn buffer_bounded<T>(values: Seq<T>, capacity: nat) -> bool {
15    values.len() <= capacity
16}
17
18/// A retained sequence at exact capacity remains admitted.
19pub proof fn exact_capacity_admitted<T>(values: Seq<T>, capacity: nat)
20    requires values.len() == capacity,
21    ensures buffer_bounded(values, capacity),
22{
23}
24
25/// Whether `value` occurs in the first `end` retained positions.
26pub open spec fn contains_up_to<T>(values: Seq<T>, end: int, value: T) -> bool {
27    exists|index: int| 0 <= index < end && index < values.len() && values[index] == value
28}
29
30/// Whether `value` is retained by a Buffer.
31pub open spec fn contains_value<T>(values: Seq<T>, value: T) -> bool {
32    contains_up_to(values, values.len() as int, value)
33}
34
35/// Whether every retained value is distinct.
36pub open spec fn all_distinct<T>(values: Seq<T>) -> bool {
37    forall|left: int, right: int|
38        0 <= left < values.len() && 0 <= right < values.len() && left != right
39            ==> #[trigger] values[left] != #[trigger] values[right]
40}
41
42/// Extending a considered prefix exposes exactly its new final value.
43pub proof fn lemma_contains_extend<T>(values: Seq<T>, end: int, value: T)
44    requires 0 <= end < values.len(),
45    ensures contains_up_to(values, end + 1, value)
46        == (contains_up_to(values, end, value) || values[end] == value),
47{
48    if contains_up_to(values, end + 1, value) {
49        let index = choose|index: int|
50            0 <= index < end + 1 && index < values.len() && values[index] == value;
51        assert(index < end || index == end);
52    }
53    if contains_up_to(values, end, value) {
54        let index = choose|index: int|
55            0 <= index < end && index < values.len() && values[index] == value;
56        assert(0 <= index < end + 1 && index < values.len());
57    }
58    if values[end] == value {
59        assert(0 <= end < end + 1 && end < values.len());
60    }
61}
62
63/// Appending one value extends membership by exactly that value.
64pub proof fn lemma_push_contains<T>(values: Seq<T>, added: T, value: T)
65    ensures contains_value(values.push(added), value)
66        == (contains_value(values, value) || value == added),
67{
68    let pushed = values.push(added);
69    if contains_value(pushed, value) {
70        let index = choose|index: int| 0 <= index < pushed.len() && pushed[index] == value;
71        if index < values.len() {
72            assert(pushed[index] == values[index]);
73        } else {
74            assert(index == values.len());
75        }
76    }
77    if contains_value(values, value) {
78        let index = choose|index: int| 0 <= index < values.len() && values[index] == value;
79        assert(pushed[index] == values[index]);
80    }
81    if value == added {
82        assert(pushed[values.len() as int] == value);
83    }
84}
85
86/// Every in-range retained position witnesses membership of its value.
87pub proof fn indexed_value_contained<T>(values: Seq<T>, index: int)
88    requires 0 <= index < values.len(),
89    ensures contains_value(values, values[index]),
90{
91    assert(0 <= index < values.len() && values[index] == values[index]);
92}
93
94/// A bounded FIFO connective.
95pub struct Buffer<T> {
96    /// Maximum number of retained values.
97    pub capacity: usize,
98    /// Retained values in FIFO order.
99    pub values: Vec<T>,
100}
101
102impl<T> Buffer<T> {
103    /// Whether the retained FIFO contents fit within the configured capacity.
104    pub closed spec fn well_formed(&self) -> bool {
105        buffer_bounded(self.values@, self.capacity as nat)
106    }
107
108    /// Construct an empty FIFO with a fixed capacity.
109    pub fn new(capacity: usize) -> (buffer: Self)
110        ensures
111            buffer.well_formed(),
112            buffer.capacity == capacity,
113            buffer.values@.len() == 0,
114    {
115        Self { capacity, values: Vec::new() }
116    }
117
118    /// Fixed FIFO capacity.
119    pub fn capacity(&self) -> (capacity: usize)
120        ensures capacity == self.capacity,
121    {
122        self.capacity
123    }
124
125    /// Number of retained values.
126    pub fn len(&self) -> (length: usize)
127        ensures length == self.values@.len(),
128    {
129        self.values.len()
130    }
131
132    /// Whether no values are retained.
133    pub fn is_empty(&self) -> (empty: bool)
134        ensures empty == (self.values@.len() == 0),
135    {
136        self.values.is_empty()
137    }
138
139    /// Whether the FIFO is at capacity.
140    pub fn is_full(&self) -> (full: bool)
141        ensures full == (self.values@.len() == self.capacity),
142    {
143        self.values.len() == self.capacity
144    }
145
146    /// Push one value, returning it unchanged when the FIFO is full.
147    ///
148    /// # Errors
149    ///
150    /// Returns the supplied value when the buffer is full.
151    pub fn push(&mut self, value: T) -> (result: Result<(), T>)
152        requires old(self).well_formed(),
153        ensures
154            final(self).well_formed(),
155            final(self).capacity == old(self).capacity,
156            old(self).values@.len() < old(self).capacity ==>
157                final(self).values@ == old(self).values@.push(value),
158            old(self).values@.len() >= old(self).capacity ==>
159                final(self).values@ == old(self).values@,
160    {
161        if self.values.len() >= self.capacity { return Err(value); }
162        self.values.push(value);
163        Ok(())
164    }
165
166    /// Remove and return the oldest retained value.
167    pub fn pop(&mut self) -> (value: Option<T>)
168        requires old(self).well_formed(),
169        ensures
170            final(self).well_formed(),
171            final(self).capacity == old(self).capacity,
172            old(self).values@.len() == 0 ==> final(self).values@ == old(self).values@,
173            old(self).values@.len() > 0 ==>
174                final(self).values@ == old(self).values@.skip(1),
175            all_distinct(old(self).values@) ==> all_distinct(final(self).values@),
176    {
177        if self.values.is_empty() { None } else { Some(self.values.remove(0)) }
178    }
179}
180
181/// Query membership in a retained sequence using its verified equality adapter.
182pub fn retained_contains<T: ValueEq + Copy>(values: &Vec<T>, value: T) -> (present: bool)
183    ensures present == contains_value(values@, value),
184{
185        let mut index: usize = 0;
186        while index < values.len()
187            invariant
188                index <= values.len(),
189                !contains_up_to(values@, index as int, value),
190            decreases values.len() - index,
191        {
192            if values[index].value_eq(&value) {
193                assert(contains_value(values@, value));
194                return true;
195            }
196            proof { lemma_contains_extend(values@, index as int, value); }
197            index = index + 1;
198        }
199        false
200    }
201
202impl<T: ValueEq + Copy> Buffer<T> {
203    /// Query retained membership through the Buffer owner.
204    pub fn contains(&self, value: T) -> (present: bool)
205        ensures present == contains_value(self.values@, value),
206    {
207        retained_contains(&self.values, value)
208    }
209
210    fn without_value(values: &Vec<T>, value: T) -> (out: Vec<T>)
211        requires all_distinct(values@),
212        ensures
213            all_distinct(out@),
214            out@.len() <= values@.len(),
215            forall|candidate: T| #[trigger] contains_value(out@, candidate)
216                == (contains_value(values@, candidate) && candidate != value),
217    {
218        let mut out = Vec::new();
219        let mut index: usize = 0;
220        while index < values.len()
221            invariant
222                index <= values.len(),
223                all_distinct(values@),
224                all_distinct(out@),
225                out@.len() <= index,
226                forall|candidate: T| #[trigger] contains_value(out@, candidate)
227                    == (contains_up_to(values@, index as int, candidate)
228                        && candidate != value),
229            decreases values.len() - index,
230        {
231            let current = values[index];
232            let ghost before = out@;
233            if !current.value_eq(&value) {
234                assert(!contains_value(before, current)) by {
235                    if contains_value(before, current) {
236                        assert(contains_up_to(values@, index as int, current));
237                        let prior = choose|prior: int|
238                            0 <= prior < index as int
239                                && prior < values@.len()
240                                && values@[prior] == current;
241                        assert(values@[prior] != values@[index as int]);
242                    }
243                }
244                out.push(current);
245                assert(all_distinct(out@)) by {
246                    assert forall|left: int, right: int|
247                        0 <= left < out@.len()
248                            && 0 <= right < out@.len()
249                            && left != right
250                        implies #[trigger] out@[left] != #[trigger] out@[right] by {
251                        if left < before.len() && right < before.len() {
252                        } else if left == before.len() && right < before.len() {
253                            assert(out@[right] == before[right]);
254                            assert(contains_value(before, before[right]));
255                        } else if right == before.len() && left < before.len() {
256                            assert(out@[left] == before[left]);
257                            assert(contains_value(before, before[left]));
258                        }
259                    }
260                }
261            }
262            assert forall|candidate: T| #[trigger] contains_value(out@, candidate)
263                == (contains_up_to(values@, index as int + 1, candidate)
264                    && candidate != value) by {
265                lemma_contains_extend(values@, index as int, candidate);
266                if current != value {
267                    if out@ != before {
268                        lemma_push_contains(before, current, candidate);
269                    }
270                }
271            }
272            index = index + 1;
273        }
274        out
275    }
276
277    /// Append a value only when it is absent and capacity remains.
278    pub fn push_unique(&mut self, value: T) -> (accepted: bool)
279        requires
280            old(self).well_formed(),
281            all_distinct(old(self).values@),
282        ensures
283            final(self).well_formed(),
284            all_distinct(final(self).values@),
285            final(self).capacity == old(self).capacity,
286            accepted == (old(self).values@.len() < old(self).capacity
287                && !contains_value(old(self).values@, value)),
288            accepted ==> final(self).values@ == old(self).values@.push(value),
289            !accepted ==> final(self).values@ == old(self).values@,
290    {
291        if self.values.len() >= self.capacity || self.contains(value) {
292            return false;
293        }
294        let ghost before = self.values@;
295        self.values.push(value);
296        proof {
297            assert(all_distinct(self.values@)) by {
298                assert forall|left: int, right: int|
299                    0 <= left < self.values@.len()
300                        && 0 <= right < self.values@.len()
301                        && left != right
302                    implies #[trigger] self.values@[left] != #[trigger] self.values@[right] by {
303                    if left < before.len() && right < before.len() {
304                    } else if left == before.len() && right < before.len() {
305                        assert(self.values@[right] == before[right]);
306                        assert(contains_value(before, before[right]));
307                    } else if right == before.len() && left < before.len() {
308                        assert(self.values@[left] == before[left]);
309                        assert(contains_value(before, before[left]));
310                    }
311                }
312            }
313        }
314        true
315    }
316
317    /// Remove one distinct retained value wherever it occurs.
318    pub fn remove_value(&mut self, value: T) -> (removed: bool)
319        requires
320            old(self).well_formed(),
321            all_distinct(old(self).values@),
322        ensures
323            final(self).well_formed(),
324            all_distinct(final(self).values@),
325            final(self).capacity == old(self).capacity,
326            removed == contains_value(old(self).values@, value),
327            forall|candidate: T| #[trigger] contains_value(final(self).values@, candidate)
328                == (contains_value(old(self).values@, candidate) && candidate != value),
329    {
330        let removed = self.contains(value);
331        if removed {
332            self.values = Self::without_value(&self.values, value);
333        }
334        removed
335    }
336}
337
338}
339
340impl<T: core::fmt::Debug> core::fmt::Debug for Buffer<T> {
341    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
342        formatter
343            .debug_struct("Buffer")
344            .field("capacity", &self.capacity)
345            .field("values", &self.values)
346            .finish()
347    }
348}