#uninitialized-memory #buffer #owned #byte-buffer #reading #filled #possibly

nightly owned-buf

An owned buffer type for reading into possibly uninitialized memory

3 releases (breaking)

0.3.0 Mar 2, 2023
0.1.0 Nov 2, 2022
0.0.0 Nov 2, 2022

#2 in #possibly


Used in async-io-traits

MIT/Apache

24KB
335 lines

owned-buf

An owned buffer type for reading into possibly uninitialized memory. An owned equivalent of BorrowedBuf.


lib.rs:

An owned, fixed length buffer of bytes (u8s) and helper types.

The primary type is OwnedBuf which is a sort-of abstract wrapper type for an owned sequence of bytes. OwnedSlice and OwnedCursor give read access to the filled part of an OwnedBuf and write access to the unfilled part of an OwnedBuf, respectively.

An OwnedBuf is primarily useful where an IO operation takes ownership of supplied buffers, for example async OwnedRead.

An OwnedBuf's data can be in either filled (i.e., data has been written to a byte) or unfilled. Unfilled data may be with initialized or uninitialized. An OwnedBuf may contain all three kinds of data at the same time and tracks the state of bytes.

Lifecycle

An OwnedBuf is never directly created. Instead, an existing sequence of bytes (e.g., a Vec<u8>) is transformed into an OwnedBuf. Unlike when taking a slice, the previous sequence type is consumed and ownership of its memory is transferred to the OwnedBuf (the memory itself does not move).

The OwnedBuf is then used. It is transformed into an OwnedCursor to write to it and into an OwnedSlice to read from it. These types can be transformed back into an OwnedBuf as needed. With each transformation, ownership is passed to the new type. If needed the OwnedBuf can be reset.

An OwnedBuf can be transformed back into the original sequence type. Any data written to the OwnedBuf can be read. The sequence type can be used in the usual way, including its destruction. If an OwnedBuf is not transformed back into the previous type, then its destructor calls a function supplied when the buffer is created. Typically, that converts the buffer into the original sequence type and calls it destructor.

Destructor safety

An OwnedBuf takes a destructor as a function pointer and calls it from the OwnedBuf's destructor. The function pointer is marked as unsafe and the safety invariant is that the OwnedBuf was created from the collection type expected by the destructor function. This invariant must be ensured when the OwnedBuf is created, thus OwnedBuf::new is unsafe.

Conversion from user types

This module includes functionality to transform an OwnedBuf from and to a Vec<u8> or Vec<MaybeUninit<u8>>. OwnedBuf is designed to be usable with any owned sequence of bytes. To create an OwnedBuf use OwnedBuf::new, you'll supply a data pointer, some metadata, and a destructor function (see below). To transform from an OwnedBuf, you use into_raw_parts to get the internal state of the OwnedBuf and then create the sequence type in the usual way.

An OwnedBuf's destructor function has type &'static dyn Fn(&mut OwnedBuf<A>), it passes a mutable reference to the destructor, however, it is guaranteed that the OwnedBuf will not be accessed after calling the destructor function. Typically, the destructor function will use std::ptr::read to get the OwnedBuf by value, transform it into the original sequence type, and (implicitly) call its destructor.

Fields

The fields of an OwnedBuf are private and cannot be modified directly to help ensure safety invariants. However, creating an OwnedBuf using new or calling into_raw_parts exposes all its fields. These fields are only intended to be used for implementing conversions from and to other collection types, not for end users. We'll summarise the fields here rather than across multiple functions.

  • data: *mut MaybeUninit<u8> a pointer to the data in the buffer
  • dtor: unsafe fn(&mut OwnedBuf) a function pointer to the backing-buffer-dependent destructor function
  • user_data: *const () a pointer to any data, intended to be used by self.dtor
  • capacity: usize the total size of the buffer
  • filled: usize the number of bytes in the buffer known to be filled
  • init: usize the number of bytes in the buffer known to be initialized

Examples

Creating an OwnedBuf from a Vec.

let vec = vec![1u8, 2, 3];
let buf: OwnedBuf = vec.into();

// Use `filled` to view `buf` as an `OwnedSlice` to read from it.
assert_eq!(1, buf.filled()[0]);

// `Vec`'s destructor will be called via `OwnedBuf`'s.

Writing into an OwnedBuf and converting it back into a Vec.

let vec: Vec<u8> = Vec::with_capacity(8);
let buf: OwnedBuf = vec.into();
assert_eq!(0, buf.filled_len());
assert_eq!(8, buf.capacity());

// Get a cursor to write into the `OwnedBuf`.
let mut cursor = buf.unfilled();
cursor.write_slice(&[0, 1, 2, 3]);

// Convert the cursor back into an `OwnedBuf`.
let buf = cursor.into_buf();
assert_eq!(4, buf.filled_len());

let vec = unsafe { buf.into_vec() };
assert_eq!(4, vec.len());
assert_eq!(8, vec.capacity());
assert_eq!(3, vec[3]);

No runtime deps