Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  writer.rs   Sprache: unbekannt

 
Spracherkennung für: .rs vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

//! Byte writer.
use core::ptr;

use duplicate::duplicate_item;

/// An error occurred while writing bytes.
#[derive(Clone, Debug, thiserror::Error)]
pub enum 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(&mut Self::RunWriter<'_>) -> Result<T, ByteWriterError>>(
        &mut self,
        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>>(
        &mut self,
        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(&mut self, 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(&mut self, 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(&mut self, 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(&mut self, 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(&mut self, 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(&mut self, value: u64) -> Result<(), ByteWriterError> {
        self.write(&value.to_le_bytes())
    }
}

pub(crate) struct ByteWriterContainer<T> {
    buffer: T,
    offset: usize,
}

#[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")
    }

    #[inline]
    fn insufficient_space(&self, length: usize) -> ByteWriterError {
        ByteWriterError::InsufficientSpace {
            required_space: length,
            available_space: self.space().to_string(),
            offset: self.offset,
        }
    }
}

#[duplicate_item(
    buffer_type   run_writer_type    run_at_writer_type;
    [ &mut [u8] ] [ &'run mut [u8] ] [ &'run_at mut [u8] ];
)]
impl ByteWriter for ByteWriterContainer<buffer_type> {
    type RunAtWriter<'run_at> = ByteWriterContainer<run_at_writer_type>;
    type RunWriter<'run> = ByteWriterContainer<run_writer_type>;

    #[inline]
    fn offset(&self) -> usize {
        self.offset
    }

    #[inline]
    fn run<T, F: FnOnce(&mut Self::RunWriter<'_>) -> Result<T, ByteWriterError>>(
        &mut self,
        op: F,
    ) -> Result<T, ByteWriterError> {
        op(self)
    }

    #[inline]
    fn run_at<T, F: FnOnce(Self::RunAtWriter<'_>) -> Result<T, ByteWriterError>>(
        &mut self,
        relative_offset: isize,
        op: F,
    ) -> Result<T, ByteWriterError> {
        let invalid_offset = || ByteWriterError::InvalidRelativeOffset {
            relative_offset,
            offset: self.offset,
            length: self.buffer.len(),
        };

        // 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(&mut self, 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(())
    }

    #[inline]
    fn write_in_place(&mut self, length: usize) -> Result<&mut [u8], ByteWriterError> {
        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());
        }

        // 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 }
    }

    #[inline]
    fn insufficient_space(&self, length: usize) -> ByteWriterError {
        ByteWriterError::InsufficientSpace {
            required_space: length,
            available_space: "unlimited".to_owned(),
            offset: self.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))
    }
}

#[duplicate_item(
    buffer_type      run_writer_type       run_at_writer_type;
    [ &mut Vec<u8> ] [ &'run mut Vec<u8> ] [ &'run_at mut Vec<u8> ];
    [ Vec<u8> ]      [ Vec<u8> ]           [ &'run_at mut Vec<u8> ];
)]
impl ByteWriter for ByteWriterContainer<buffer_type> {
    type RunAtWriter<'run_at> = ByteWriterContainer<run_at_writer_type>;
    type RunWriter<'run> = ByteWriterContainer<run_writer_type>;

    #[inline]
    fn offset(&self) -> usize {
        self.offset
    }

    #[inline]
    fn run<T, F: FnOnce(&mut Self::RunWriter<'_>) -> Result<T, ByteWriterError>>(
        &mut self,
        op: F,
    ) -> Result<T, ByteWriterError> {
        op(self)
    }

    #[inline]
    fn run_at<T, F: FnOnce(Self::RunAtWriter<'_>) -> Result<T, ByteWriterError>>(
        &mut self,
        relative_offset: isize,
        op: F,
    ) -> Result<T, ByteWriterError> {
        let invalid_offset = || ByteWriterError::InvalidRelativeOffset {
            relative_offset,
            offset: self.offset,
            length: self.buffer.len(),
        };

        // 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())?;

        // Extend (zerofill) first if we need to (but don't truncate).
        if updated_offset > self.buffer.len() {
            self.buffer.resize(updated_offset, 0x00);
        }

        // Run
        op(Self::RunAtWriter {
            buffer: &mut self.buffer,
            offset: updated_offset,
        })
    }

    #[inline]
    fn write(&mut self, bytes: &[u8]) -> Result<(), ByteWriterError> {
        let length = bytes.len();

        // 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)
        if self.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);
        }

        // Update offset
        self.offset = updated_offset;
        Ok(())
    }

    #[inline]
    fn write_in_place(&mut self, length: usize) -> Result<&mut [u8], ByteWriterError> {
        // Calculate new offset
        let updated_offset = self
            .offset
            .checked_add(length)
            .ok_or_else(|| self.insufficient_space(length))?;

        // 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(&mut self, 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(&mut self, 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);

            self.set_len(current_length + additional_length);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    /// 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);

            buffer.copy_from_slice(&[19, 20, 21]);
        }
        assert_eq!(inner(&writer)[..21], (1..=21).collect::<Vec<_>>());

        // Catch out of bounds writing
        assert_eq!(writer.offset(), 21);
        assert!(writer.run_at(-22, |mut writer| writer.write(&[0, 0])).is_err());

        if extendable {
            let offset = writer.offset();
            assert_eq!(offset, 21);

            // Extendable: We should be able to add more bytes
            writer.write_u8(22).unwrap();
            writer.write(&[23]).unwrap();

            let buffer = inner(&writer);
            assert_eq!(buffer[offset..], [22, 23]);
        } else {
            // Not extendable: Moving beyond the offset should fail
            assert!(writer.write_u8(1).is_err());
            assert!(writer.write(&[0]).is_err());
        }
    }

    #[test]
    fn slice_byte_writer() {
        let mut buffer: [u8; 21] = [0; 21];
        let writer = SliceByteWriter::new(&mut buffer);

        test_byte_writer(writer, false, |writer| writer.buffer);
    }

    #[test]
    fn owned_vec_byte_writer() {
        let writer = OwnedVecByteWriter::new_with_capacity(21);
        test_byte_writer(writer, true, |writer| &writer.buffer);

        let writer = OwnedVecByteWriter::new_empty();
        test_byte_writer(writer, true, |writer| &writer.buffer);
    }

    #[test]
    fn borrowed_vec_byte_writer() {
        let mut buffer: Vec<u8> = vec![];
        let writer = BorrowedVecByteWriter::new(&mut buffer);
        test_byte_writer(writer, true, |writer| writer.buffer);

        let mut buffer: Vec<u8> = Vec::with_capacity(21);
        let writer = BorrowedVecByteWriter::new(&mut buffer);
        test_byte_writer(writer, true, |writer| writer.buffer);
    }
}

[Dauer der Verarbeitung: 0.19 Sekunden, vorverarbeitet 2026-04-27]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge