blob: aee65371b79cc5625f25a20db9b7b8d41853f265 [file]
// Copyright 2024, The Android Open Source Project
//
// 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.
//! ImageBuffer is helper to store uninitialized memory buffer. And work with it allowing to read
//! into the buffer and retrieve initialized part.
//!
//! Similar to [ReadBuf](https://docs.rs/tokio/latest/tokio/io/struct.ReadBuf.html) but works in
//! `no_std`.
use core::mem::MaybeUninit;
use liberror::{Error, Result};
/// Wrapper class for buffer received with [get_buffer] function.
///
/// Helps to keep track of allocated/init memory and avoid getting same buffer more than once.
#[derive(Debug)]
pub struct ImageBuffer<'a> {
buffer: Option<&'a mut [MaybeUninit<u8>]>,
// number of initialized and filled bytes.
used_bytes: usize,
}
// Unstable MaybeUninit API
// feature = "maybe_uninit_slice", issue = "63569"
// Assuming all the elements are initialized, get a mutable slice to them.
//
// # Safety
//
// It is up to the caller to guarantee that the `MaybeUninit<T>` elements
// really are in an initialized state.
// Calling this when the content is not yet fully initialized causes undefined behavior.
#[inline(always)]
unsafe fn slice_assume_init_mut<T>(slice: &mut [MaybeUninit<T>]) -> &mut [T] {
// SAFETY: Caller must make sure provided data is initialized.
unsafe { &mut *(slice as *mut [MaybeUninit<T>] as *mut [T]) }
}
// Assuming all the elements are initialized, get a slice of them.
//
// # Safety
//
// It is up to the caller to guarantee that the `MaybeUninit<T>` elements
// really are in an initialized state.
// Calling this when the content is not yet fully initialized causes undefined behavior.
#[inline(always)]
unsafe fn slice_assume_init_ref<T>(slice: &[MaybeUninit<T>]) -> &[T] {
// SAFETY: Caller must make sure provided data is initialized.
unsafe { &*(slice as *const [MaybeUninit<T>] as *const [T]) }
}
impl ImageBuffer<'_> {
/// Create new ImageBuffer from buffer and used_bytes
pub fn new(buffer: &mut [MaybeUninit<u8>]) -> ImageBuffer {
ImageBuffer { buffer: Some(buffer), used_bytes: 0 }
}
/// Total buffer capacity.
pub fn capacity(&self) -> usize {
self.buffer.as_ref().unwrap().len()
}
/// Increase used part of the buffer by `len`
///
/// Return:
/// Error() - if current used_bytes + len > capacity, or causes arithmetic overflow.
/// Ok(()) - on success
///
/// SAFETY:
/// It is up to the user to guarantee that `len` bytes for tail was initialized and filled.
pub unsafe fn advance_used(&mut self, len: usize) -> Result<()> {
let Some(new_len) = self.used_bytes.checked_add(len) else {
return Err(Error::Other(Some("Used bytes overflow")));
};
if new_len > self.capacity() {
return Err(Error::BufferTooSmall(Some(new_len)));
}
self.used_bytes = new_len;
Ok(())
}
/// Return used and tail parts of the buffer
pub fn get_split(&self) -> (&[u8], &[MaybeUninit<u8>]) {
let (used, tail) = self.buffer.as_ref().unwrap().split_at(self.used_bytes);
// SAFETY:
//
// ImageBuffer user guarantees that changing used elements means they were initialized.
// And object assumes initialized only for slice [..used_bytes]
let initialized = unsafe { slice_assume_init_ref(used) };
(initialized, tail)
}
/// Return used and tail parts of the buffer
pub fn get_split_mut(&mut self) -> (&mut [u8], &mut [MaybeUninit<u8>]) {
let (used, tail) = self.buffer.as_mut().unwrap().split_at_mut(self.used_bytes);
// SAFETY:
//
// ImageBuffer user guaranties that changing used elements means they were initialized.
// And object assumes initialized only for slice [..used_bytes]
let initialized = unsafe { slice_assume_init_mut(used) };
(initialized, tail)
}
/// Slice of the buffer that is used
pub fn used(&self) -> &[u8] {
let (used, _) = self.get_split();
used
}
/// Slice of the buffer that is used
pub fn used_mut(&mut self) -> &mut [u8] {
let (used, _) = self.get_split_mut();
used
}
/// Return part of the buffer that is not used
pub fn tail(&mut self) -> &mut [MaybeUninit<u8>] {
let (_, tail) = self.get_split_mut();
tail
}
}
impl AsRef<[MaybeUninit<u8>]> for ImageBuffer<'_> {
fn as_ref(&self) -> &[MaybeUninit<u8>] {
self.buffer.as_ref().unwrap()
}
}
impl AsMut<[MaybeUninit<u8>]> for ImageBuffer<'_> {
fn as_mut(&mut self) -> &mut [MaybeUninit<u8>] {
self.buffer.as_mut().unwrap()
}
}
impl PartialEq for ImageBuffer<'_> {
fn eq(&self, other: &Self) -> bool {
self.used() == other.used()
}
}
#[cfg(test)]
/// Helper to create ImageBuffers from Vec<u8>
pub struct ImageBufferVec {
buf: Vec<u8>,
}
#[cfg(test)]
impl ImageBufferVec {
pub fn new(buf: Vec<u8>) -> Self {
Self { buf }
}
pub fn get(&mut self) -> ImageBuffer {
ImageBuffer::new(Self::slice_assume_not_init_mut(self.buf.as_mut_slice()))
}
fn slice_assume_not_init_mut<T>(slice: &mut [T]) -> &mut [MaybeUninit<T>] {
// SAFETY: similar to safety notes for `slice_get_ref`, but we have a
// mutable reference which is also guaranteed to be valid for writes.
unsafe { &mut *(slice as *mut [T] as *mut [MaybeUninit<T>]) }
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_image_buffer_capacity() {
assert_eq!(ImageBufferVec::new(vec![0u8; 0]).get().capacity(), 0);
assert_eq!(ImageBufferVec::new(vec![0u8; 0]).get().capacity(), 0);
assert_eq!(ImageBufferVec::new(vec![0u8; 1]).get().capacity(), 1);
assert_eq!(ImageBufferVec::new(vec![0u8; 100]).get().capacity(), 100);
assert_eq!(
ImageBufferVec::new(vec![0u8; 128 * 1024 * 1024]).get().capacity(),
128 * 1024 * 1024
);
}
#[test]
fn test_image_buffer_used() {
let mut img_buf_vec = ImageBufferVec::new(vec![0u8; 100]);
let mut img_buf = img_buf_vec.get();
assert_eq!(img_buf.used().len(), 0);
assert_eq!(img_buf.used_mut().len(), 0);
// SAFETY:
// All data in img_buf is initialized since it was created from vec
unsafe { img_buf.advance_used(1).unwrap() };
assert_eq!(img_buf.used().len(), 1);
assert_eq!(img_buf.used_mut().len(), 1);
// SAFETY:
// All data in img_buf is initialized since it was created from vec
unsafe { img_buf.advance_used(3).unwrap() };
assert_eq!(img_buf.used().len(), 4);
assert_eq!(img_buf.used_mut().len(), 4);
assert_eq!(
// SAFETY:
// All data in img_buf is initialized since it was created from vec
unsafe { img_buf.advance_used(1024) },
Err(Error::BufferTooSmall(Some(1028)))
);
assert_eq!(img_buf.used().len(), 4);
assert_eq!(img_buf.used_mut().len(), 4);
}
#[test]
fn test_image_buffer_get_split() {
let mut img_buf_vec = ImageBufferVec::new(vec![0u8, 1, 2, 3]);
let mut img_buf = img_buf_vec.get();
assert_eq!(img_buf.used(), [].as_slice());
assert_eq!(img_buf.used_mut(), [].as_mut_slice());
assert_eq!(img_buf.tail().len(), 4);
let (used, tail) = img_buf.get_split();
assert_eq!(used, [].as_slice());
assert_eq!(tail.len(), 4);
let (used, tail) = img_buf.get_split_mut();
assert_eq!(used, [].as_mut_slice());
assert_eq!(tail.len(), 4);
// SAFETY:
// All data in img_buf is initialized since it was created from vec
unsafe { img_buf.advance_used(2).unwrap() };
assert_eq!(img_buf.used(), [0, 1].as_slice());
assert_eq!(img_buf.used_mut(), [0, 1].as_mut_slice());
assert_eq!(img_buf.tail().len(), 2);
let (used, tail) = img_buf.get_split();
assert_eq!(used, [0, 1].as_slice());
assert_eq!(tail.len(), 2);
let (used, tail) = img_buf.get_split_mut();
assert_eq!(used, [0, 1].as_mut_slice());
assert_eq!(tail.len(), 2);
// SAFETY:
// All data in img_buf is initialized since it was created from vec
unsafe { img_buf.advance_used(2).unwrap() };
assert_eq!(img_buf.used(), [0, 1, 2, 3].as_slice());
assert_eq!(img_buf.used_mut(), [0, 1, 2, 3].as_mut_slice());
assert_eq!(img_buf.tail().len(), 0);
let (used, tail) = img_buf.get_split();
assert_eq!(used, [0, 1, 2, 3].as_slice());
assert_eq!(tail.len(), 0);
let (used, tail) = img_buf.get_split_mut();
assert_eq!(used, [0, 1, 2, 3].as_mut_slice());
assert_eq!(tail.len(), 0);
}
#[test]
fn test_image_buffer_eq_not_init() {
assert_eq!(
ImageBufferVec::new(vec![0u8, 1, 2]).get(),
ImageBufferVec::new(vec![0u8, 1, 2]).get()
);
}
#[test]
fn test_image_buffer_eq_init_same() {
let mut v1 = ImageBufferVec::new(vec![0u8, 1, 2]);
let mut v2 = ImageBufferVec::new(vec![0u8, 1, 2]);
let mut image_buffer_1 = v1.get();
let mut image_buffer_2 = v2.get();
// SAFETY:
// Buffers initialised on creation.
unsafe {
image_buffer_1.advance_used(3).unwrap();
image_buffer_2.advance_used(3).unwrap();
}
assert_eq!(image_buffer_1, image_buffer_2);
}
#[test]
fn test_image_buffer_eq_diff_capacity() {
let mut v1 = ImageBufferVec::new(vec![0u8, 1, 2]);
let mut v2 = ImageBufferVec::new(vec![0u8, 1, 2, 3]);
let mut image_buffer_1 = v1.get();
let mut image_buffer_2 = v2.get();
// SAFETY:
// Buffers initialised on creation.
unsafe {
image_buffer_1.advance_used(2).unwrap();
image_buffer_2.advance_used(2).unwrap();
}
assert_eq!(image_buffer_1, image_buffer_2);
}
}