|
| 1 | +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| 2 | +// SPDX-License-Identifier: Apache-2.0 OR MIT |
| 3 | +use crate::{assume, nondet}; |
| 4 | +use core::ops::{Deref, DerefMut}; |
| 5 | + |
| 6 | +/// Given an array `arr` of length `LENGTH`, this function returns a **valid** |
| 7 | +/// slice of `arr` with non-deterministic start and end points. This is useful |
| 8 | +/// in situations where one wants to verify that all possible slices of a given |
| 9 | +/// array satisfy some property. |
| 10 | +/// |
| 11 | +/// # Example: |
| 12 | +/// |
| 13 | +/// ```rust |
| 14 | +/// let arr = [1, 2, 3]; |
| 15 | +/// let slice = rmc::slice::nondet_slice_of_array(&arr); |
| 16 | +/// foo(slice); // where foo is a function that takes a slice and verifies a property about it |
| 17 | +/// ``` |
| 18 | +pub fn nondet_slice_of_array<T, const LENGTH: usize>(arr: &[T; LENGTH]) -> &[T] { |
| 19 | + let (from, to) = nondet_range::<LENGTH>(); |
| 20 | + &arr[from..to] |
| 21 | +} |
| 22 | + |
| 23 | +/// A mutable version of the previous function |
| 24 | +pub fn nondet_slice_of_array_mut<T, const LENGTH: usize>(arr: &mut [T; LENGTH]) -> &mut [T] { |
| 25 | + let (from, to) = nondet_range::<LENGTH>(); |
| 26 | + &mut arr[from..to] |
| 27 | +} |
| 28 | + |
| 29 | +fn nondet_range<const LENGTH: usize>() -> (usize, usize) { |
| 30 | + let from: usize = nondet(); |
| 31 | + let to: usize = nondet(); |
| 32 | + assume(to <= LENGTH); |
| 33 | + assume(from <= to); |
| 34 | + (from, to) |
| 35 | +} |
| 36 | + |
| 37 | +/// A struct that stores a slice of type `T` with a non-deterministic length |
| 38 | +/// between `0..=MAX_SLICE_LENGTH` and with non-deterministic content. This is |
| 39 | +/// useful in situations where one wants to verify that all slices with any |
| 40 | +/// content and with a length up to `MAX_SLICE_LENGTH` satisfy a certain |
| 41 | +/// property. Use `nondet_slice` to create an instance of this struct. |
| 42 | +/// |
| 43 | +/// # Example: |
| 44 | +/// |
| 45 | +/// ```rust |
| 46 | +/// let slice: rmc::slice::NonDetSlice<u8, 5> = rmc::slice::nondet_slice(); |
| 47 | +/// foo(&slice); // where foo is a function that takes a slice and verifies a property about it |
| 48 | +/// ``` |
| 49 | +pub struct NonDetSlice<T, const MAX_SLICE_LENGTH: usize> { |
| 50 | + arr: [T; MAX_SLICE_LENGTH], |
| 51 | + slice_len: usize, |
| 52 | +} |
| 53 | + |
| 54 | +impl<T, const MAX_SLICE_LENGTH: usize> NonDetSlice<T, MAX_SLICE_LENGTH> { |
| 55 | + fn new() -> Self { |
| 56 | + let arr: [T; MAX_SLICE_LENGTH] = nondet(); |
| 57 | + let slice_len: usize = nondet(); |
| 58 | + assume(slice_len <= MAX_SLICE_LENGTH); |
| 59 | + Self { arr, slice_len } |
| 60 | + } |
| 61 | + |
| 62 | + pub fn get_slice(&self) -> &[T] { |
| 63 | + &self.arr[..self.slice_len] |
| 64 | + } |
| 65 | + |
| 66 | + pub fn get_slice_mut(&mut self) -> &mut [T] { |
| 67 | + &mut self.arr[..self.slice_len] |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +impl<T, const MAX_SLICE_LENGTH: usize> Deref for NonDetSlice<T, MAX_SLICE_LENGTH> { |
| 72 | + type Target = [T]; |
| 73 | + |
| 74 | + fn deref(&self) -> &Self::Target { |
| 75 | + self.get_slice() |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | +impl<T, const MAX_SLICE_LENGTH: usize> DerefMut for NonDetSlice<T, MAX_SLICE_LENGTH> { |
| 80 | + fn deref_mut(&mut self) -> &mut Self::Target { |
| 81 | + self.get_slice_mut() |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +pub fn nondet_slice<T, const MAX_SLICE_LENGTH: usize>() -> NonDetSlice<T, MAX_SLICE_LENGTH> { |
| 86 | + NonDetSlice::<T, MAX_SLICE_LENGTH>::new() |
| 87 | +} |
0 commit comments