/// An error occurred while writing bytes. #[derive(Clone, Debug, thiserror::Error)] pubenum ByteWriterError { /// Provided relative offset would move the writer offset out of bounds. #[error( "Relative offset out of bounds: relative-offset={relative_offset}, offset={offset}, length={length}"
)]
InvalidRelativeOffset { /// Requested relative offset to move within the data buffer.
relative_offset: isize, /// Current offset in the underlying data buffer.
offset: usize, /// Total length of the underlying data buffer.
length: usize,
},
/// Insufficient space left for the desired operation #[error( "Insufficient space left: required-space={required_space}, available-space={available_space}, \
offset={offset}"
)]
InsufficientSpace { /// Required amount of space for the write operation.
required_space: usize, /// Available amount of space left for writing.
available_space: String, /// Current offset in the underlying data buffer.
offset: usize,
},
}
pub(crate) trait ByteWriter { /// Writer type used for [`ByteWriter::run`]. type RunWriter<'run>: ByteWriter;
/// Writer type used for [`ByteWriter::run_at`]. type RunAtWriter<'run_at>: ByteWriter;
/// Return the current offset of the writer. fn offset(&self) -> usize;
/// Run an arbitrary operation on the writer. /// /// This is useful when chaining multiple write operations that can be mapped to a single error. /// /// # Error /// /// Returns any [`ByteWriterError`] returned by the operation. fn run<T, F: FnOnce(&mutSelf::RunWriter<'_>) -> Result<T, ByteWriterError>>(
&mutself,
op: F,
) -> Result<T, ByteWriterError>;
/// Run an arbitrary operation on the writer at a relative offset to the current offset in a /// child reader instance. The parent reader will maintain the current offset. /// /// If the underlying buffer is extendable and not large enough to facilitiate `offset`, newly /// allocated space up until `offset` will be zerofilled. /// /// # Error /// /// Returns [`ByteWriterError::InsufficientSpace`] if the `offset` would move outside of the /// underlying buffer boundary. /// /// Returns any [`ByteWriterError`] returned by the operation. fn run_at<T, F: FnOnce(Self::RunAtWriter<'_>) -> Result<T, ByteWriterError>>(
&mutself,
relative_offset: isize,
op: F,
) -> Result<T, ByteWriterError>;
/// Write a sequence of bytes. /// /// # Error /// /// Returns [`ByteWriterError::InsufficientSpace`] if the writer offset would move outside /// of the underlying buffer boundary. fn write(&mutself, bytes: &[u8]) -> Result<(), ByteWriterError>;
/// Borrow a mutable slice for direct in-place writing. /// /// # Error /// /// Returns [`ByteWriterError::InsufficientSpace`] if the `offset` would move outside of the underlying /// buffer boundary. fn write_in_place(&mutself, length: usize) -> Result<&mut [u8], ByteWriterError>;
/// Write a u8. /// /// # Error /// /// Returns [`ByteWriterError::InsufficientSpace`] if the writer offset would move outside /// of the underlying buffer boundary. #[inline] fn write_u8(&mutself, value: u8) -> Result<(), ByteWriterError> { self.write(&[value])
}
/// Write a u16 (little endian). /// /// # Error /// /// Returns [`ByteWriterError::InsufficientSpace`] if the writer offset would move outside /// of the underlying buffer boundary. #[inline] fn write_u16_le(&mutself, value: u16) -> Result<(), ByteWriterError> { self.write(&value.to_le_bytes())
}
/// Write a u32 (little endian). /// /// # Error /// /// Returns [`ByteWriterError::InsufficientSpace`] if the writer offset would move outside /// of the underlying buffer boundary. #[inline] fn write_u32_le(&mutself, value: u32) -> Result<(), ByteWriterError> { self.write(&value.to_le_bytes())
}
/// Write a u64 (little endian). /// /// # Error /// /// Returns [`ByteWriterError::InsufficientSpace`] if the writer offset would move outside /// of the underlying buffer boundary. #[inline] fn write_u64_le(&mutself, value: u64) -> Result<(), ByteWriterError> { self.write(&value.to_le_bytes())
}
}
#[duplicate_item(
buffer_type;
[ &'buffer mut [u8] ];
[ Vec<u8> ];
[ &'buffer mut Vec<u8> ];
)] #[expect(clippy::allow_attributes, reason = "duplicate shenanigans")] #[allow(clippy::extra_unused_lifetimes, reason = "Abstracted duplicate")] impl<'buffer> ByteWriterContainer<buffer_type> { /// Create a new byte writer starting at the **beginning** of the `buffer`. #[inline] #[allow(dead_code, reason = "Will use later")] pub(crate) fn new(buffer: buffer_type) -> Self { Self { buffer, offset: 0 }
}
/// Consume this writer, returning the underlying buffer. #[inline] #[allow(dead_code, reason = "Will use later")] pub(crate) fn into_inner(self) -> buffer_type { self.buffer
}
}
#[duplicate_item(
buffer_type;
[ &mut [u8] ];
)] impl ByteWriterContainer<buffer_type> { /// Return the amount of remaining space left in the underlying buffer of the writer. #[inline] pub(crate) fn space(&self) -> usize { self.buffer
.len()
.checked_sub(self.offset)
.expect("length must be >= offset and therefore usize")
}
// Calculate new offset let updated_offset: usize = relative_offset
.checked_add(self.offset.try_into().map_err(|_| invalid_offset())?)
.ok_or_else(invalid_offset)?
.try_into()
.map_err(|_| invalid_offset())?; if updated_offset > self.buffer.len() { return Err(invalid_offset());
}
// Run
op(Self::RunAtWriter {
buffer: self.buffer,
offset: updated_offset,
})
}
#[inline] fn write(&mutself, bytes: &[u8]) -> Result<(), ByteWriterError> { let length = bytes.len(); let insufficient_space = || self.insufficient_space(length);
// Calculate new offset let updated_offset = self.offset.checked_add(length).ok_or_else(insufficient_space)?;
// Check if there's sufficient space if updated_offset > self.buffer.len() { return Err(insufficient_space());
}
// Write bytes self.buffer
.get_mut(self.offset..updated_offset)
.expect("[offset..updated_offset] must be valid after space check")
.copy_from_slice(bytes); self.offset = updated_offset;
Ok(())
}
// Calculate new offset let updated_offset = self.offset.checked_add(length).ok_or_else(insufficient_space)?;
// Check if there's sufficient space if updated_offset > self.buffer.len() { return Err(insufficient_space());
}
// Update offset and return borrowed mutable buffer let buffer = self
.buffer
.get_mut(self.offset..updated_offset)
.expect("[offset..updated_offset] must be valid after space check"); self.offset = updated_offset;
Ok(buffer)
}
}
#[duplicate_item(
buffer_type;
[ Vec<u8> ];
[ &'buffer mut Vec<u8> ];
)] #[expect(clippy::allow_attributes, reason = "duplicate shenanigans")] #[allow(clippy::extra_unused_lifetimes, reason = "Abstracted duplicate")] impl<'buffer> ByteWriterContainer<buffer_type> { /// Create a new byte writer starting at the **end** of the `buffer`. #[inline] #[expect(dead_code, reason = "Will use later")] pub(crate) fn new_extending(buffer: buffer_type) -> Self { let offset = buffer.len(); Self { buffer, offset }
}
impl ByteWriterContainer<Vec<u8>> { /// Create a new byte writer with an empty buffer without any pre-allocated capacity. #[inline] pub(crate) fn new_empty() -> Self { Self::new(Vec::new())
}
/// Create a new byte writer with an empty buffer pre-allocated with `capacity`. #[inline] pub(crate) fn new_with_capacity(capacity: usize) -> Self { Self::new(Vec::with_capacity(capacity))
}
}
// Calculate new offset let updated_offset = self
.offset
.checked_add(length)
.ok_or_else(|| self.insufficient_space(length))?;
// Check if we can take a shortcut (extending at the end of the underlying buffer) ifself.offset == self.buffer.len() { // Extend from bytes slice self.buffer.extend_from_slice(bytes);
} else { // Extend (zerofill) first if we need to (but don't truncate). // // Note: We need to do this, otherwise writing to the mutable slice would fail. if updated_offset > self.buffer.len() { self.buffer.resize(updated_offset, 0x00);
}
// Write bytes self.buffer
.get_mut(self.offset..updated_offset)
.expect("[offset..updated_offset] must be valid after resizing")
.copy_from_slice(bytes);
}
// Extend (zerofill) first if we need to (but don't truncate). if updated_offset > self.buffer.len() { self.buffer.resize(updated_offset, 0x00);
}
// Update offset and return borrowed mutable buffer let buffer = self
.buffer
.get_mut(self.offset..updated_offset)
.expect("[offset..updated_offset] must be valid after space check"); self.offset = updated_offset;
Ok(buffer)
}
}
/// Wraps a [`&mut [u8]`] and allows to apply write operations safely within the constrained space. #[expect(dead_code, reason = "Will use later")] pub(crate) type SliceByteWriter<'buffer> = ByteWriterContainer<&'buffer mut [u8]>;
/// Wraps or creates a [`Vec<u8>`] and allows to apply write operations. The [`Vec<u8>`] will be /// extended automatically whenever needed. It is however a good idea to initialize it with its /// expected final length to mitigate unnecessary re-allocations. pub(crate) type OwnedVecByteWriter = ByteWriterContainer<Vec<u8>>;
/// Wraps a [`&mut Vec<u8>`] and is otherwise identical to the [`OwnedVecByteWriter`]. #[expect(dead_code, reason = "Will use later")] pub(crate) type BorrowedVecByteWriter<'buffer> = ByteWriterContainer<&'buffer mut Vec<u8>>;
// TODO(LIB-31): Should be unnecessary pub(crate) trait InsertSlice { fn insert_at(&mutself, offset: usize, bytes: &[u8]);
} impl InsertSlice for Vec<u8> { /// Inserts a [`u8`] slice at a specific offset. /// /// This is an *O*(*n*) operation and usually less efficient than concatenating items to a /// [`Vec<u8>`]. /// /// Note: This is stolen from [`String::insert_str`]. /// /// # Panics /// /// Panics if `offset` is larger than the `Vec<u8>`'s length. #[inline] fn insert_at(&mutself, offset: usize, bytes: &[u8]) { let current_length = self.len(); let additional_length = bytes.len();
// Make room for the additional bytes self.reserve(additional_length);
#[expect(
clippy::arithmetic_side_effects,
clippy::multiple_unsafe_ops_per_block,
reason = "TODO(LIB-16)"
)] // # Safety: The underlying vector MUST contain bytes. unsafe { // Move the existing bytes at the specific offset by the amount of bytes to add
ptr::copy( self.as_ptr().add(offset), self.as_mut_ptr().add(offset + additional_length),
current_length - offset,
);
// Copy the bytes to the correct offset
ptr::copy_nonoverlapping(bytes.as_ptr(), self.as_mut_ptr().add(offset), additional_length);
/// Test byte writing for an implementation of [`ByteWriter`]. /// /// `extendable` indicates whether the inner buffer of `writer` can extend itself by allocating more /// space. /// /// The tests fail if the passed writer is not zero-filled initially. fn test_byte_writer<TWriter: ByteWriter, TUnwrapFn: Fn(&TWriter) -> &[u8]>( mut writer: TWriter,
extendable: bool,
inner: TUnwrapFn,
) {
{ // Sanity check: The passed writer is zero-filled let buffer = inner(&writer);
assert!(buffer.iter().all(|byte| *byte == 0));
}
// Write multiple groups of bytes (the written bytes are in increasing order to facilitate testing)
writer.write_u8(1).unwrap();
writer.write_u16_le(u16::from_le_bytes([2, 3])).unwrap();
writer.write_u32_le(u32::from_le_bytes([4, 5, 6, 7])).unwrap();
writer
.write_u64_le(u64::from_le_bytes([8, 9, 10, 11, 12, 13, 14, 15]))
.unwrap();
writer.write(&[16, 17, 18]).unwrap();
{ let buffer = writer.write_in_place(3).unwrap();
assert_eq!(buffer.len(), 3);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.