blob: 9ba21b1ae4445991f725b72e9bb674dc4adff139 [file] [log] [blame]
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! A no_std friendly array wrapper to expose a variable length prefix of the array.
#![no_std]
#![forbid(unsafe_code)]
#![deny(
missing_docs,
clippy::indexing_slicing,
clippy::unwrap_used,
clippy::panic,
clippy::expect_used
)]
#[cfg(feature = "std")]
extern crate std;
use core::{borrow, fmt};
/// A view into the first `len` elements of an array.
///
/// Useful when you have a fixed size array but variable length data that fits in it.
#[derive(PartialEq, Eq, Clone)]
pub struct ArrayView<T, const N: usize> {
array: [T; N],
len: usize,
}
// manual impl to avoid showing parts of the buffer beyond len
impl<T: fmt::Debug, const N: usize> fmt::Debug for ArrayView<T, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_slice().fmt(f)
}
}
impl<T, const N: usize> ArrayView<T, N> {
/// A version of [`ArrayView#try_from_array`] which panics if `len > buffer.len()`,
/// suitable for usage in `const` contexts.
pub const fn const_from_array(array: [T; N], len: usize) -> ArrayView<T, N> {
if N < len {
panic!("Invalid const ArrayView");
} else {
ArrayView { array, len }
}
}
/// Create an [ArrayView] of the first `len` elements of `buffer`.
///
/// Returns `None` if `len > buffer.len()`.
pub fn try_from_array(array: [T; N], len: usize) -> Option<ArrayView<T, N>> {
if N < len {
None
} else {
Some(ArrayView { array, len })
}
}
/// Returns the prefix of the array as a slice.
pub fn as_slice(&self) -> &[T] {
&self.array[..self.len]
}
/// The length of the data in the view
pub fn len(&self) -> usize {
self.len
}
/// Returns true if the length is 0
pub fn is_empty(&self) -> bool {
self.len == 0
}
}
impl<T: Default + Copy, const N: usize> ArrayView<T, N> {
/// Create an `ArrayView` containing the data from the provided slice, assuming the slice can
/// fit in the array size.
pub fn try_from_slice(slice: &[T]) -> Option<ArrayView<T, N>> {
if N < slice.len() {
None
} else {
let mut array = [T::default(); N];
array[..slice.len()].copy_from_slice(slice);
Some(ArrayView { array, len: slice.len() })
}
}
}
impl<T, const N: usize> AsRef<[T]> for ArrayView<T, N> {
fn as_ref(&self) -> &[T] {
self.as_slice()
}
}
impl<T, const N: usize> borrow::Borrow<[T]> for ArrayView<T, N> {
fn borrow(&self) -> &[T] {
self.as_slice()
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
extern crate std;
use crate::ArrayView;
use std::format;
#[test]
fn debug_only_shows_len_elements() {
assert_eq!(
"[1, 2]",
&format!("{:?}", ArrayView::try_from_array([1, 2, 3, 4, 5], 2).unwrap())
);
}
#[test]
fn try_from_slice_too_long() {
assert_eq!(None, ArrayView::<u8, 3>::try_from_slice(&[1, 2, 3, 4, 5]));
}
#[test]
fn try_from_slice_ok() {
let view = ArrayView::<u8, 10>::try_from_slice(&[1, 2, 3, 4, 5]).unwrap();
assert_eq!(&[1, 2, 3, 4, 5], view.as_slice())
}
}