| /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
| /* |
| * Copyright (C) 2019, Google Inc. |
| * |
| * byte_stream_buffer.cpp - Byte stream buffer |
| */ |
| |
| #include "libcamera/internal/byte_stream_buffer.h" |
| |
| #include <stdint.h> |
| #include <string.h> |
| |
| #include <libcamera/base/log.h> |
| |
| /** |
| * \file byte_stream_buffer.h |
| * \brief Managed memory container for serialized data |
| */ |
| |
| namespace libcamera { |
| |
| LOG_DEFINE_CATEGORY(Serialization) |
| |
| /** |
| * \class ByteStreamBuffer |
| * \brief Wrap a memory buffer and provide sequential data read and write |
| * |
| * The ByteStreamBuffer class wraps a memory buffer and exposes sequential read |
| * and write operation with integrated boundary checks. Access beyond the end |
| * of the buffer are blocked and logged, allowing error checks to take place at |
| * the of of access operations instead of at each access. This simplifies |
| * serialization and deserialization of data. |
| * |
| * A byte stream buffer is created with a base memory pointer and a size. If the |
| * memory pointer is const, the buffer operates in read-only mode, and write |
| * operations are denied. Otherwise the buffer operates in write-only mode, and |
| * read operations are denied. |
| * |
| * Once a buffer is created, data is read or written with read() and write() |
| * respectively. Access is strictly sequential, the buffer keeps track of the |
| * current access location and advances it automatically. Reading or writing |
| * the same location multiple times is thus not possible. Bytes may also be |
| * skipped with the skip() function. |
| * |
| * |
| * The ByteStreamBuffer also supports carving out pieces of memory into other |
| * ByteStreamBuffer instances. Like a read or write operation, a carveOut() |
| * advances the internal access location, but allows the carved out memory to |
| * be accessed at a later time. |
| * |
| * All accesses beyond the end of the buffer (read, write, skip or carve out) |
| * are blocked. The first of such accesses causes a message to be logged, and |
| * the buffer being marked as having overflown. If the buffer has been carved |
| * out from a parent buffer, the parent buffer is also marked as having |
| * overflown. Any later access on an overflown buffer is blocked. The buffer |
| * overflow status can be checked with the overflow() function. |
| */ |
| |
| /** |
| * \brief Construct a read ByteStreamBuffer from the memory area \a base |
| * of \a size |
| * \param[in] base The address of the memory area to wrap |
| * \param[in] size The size of the memory area to wrap |
| */ |
| ByteStreamBuffer::ByteStreamBuffer(const uint8_t *base, size_t size) |
| : parent_(nullptr), base_(base), size_(size), overflow_(false), |
| read_(base), write_(nullptr) |
| { |
| } |
| |
| /** |
| * \brief Construct a write ByteStreamBuffer from the memory area \a base |
| * of \a size |
| * \param[in] base The address of the memory area to wrap |
| * \param[in] size The size of the memory area to wrap |
| */ |
| ByteStreamBuffer::ByteStreamBuffer(uint8_t *base, size_t size) |
| : parent_(nullptr), base_(base), size_(size), overflow_(false), |
| read_(nullptr), write_(base) |
| { |
| } |
| |
| /** |
| * \brief Construct a ByteStreamBuffer from the contents of \a other using move |
| * semantics |
| * \param[in] other The other buffer |
| * |
| * After the move construction the \a other buffer is invalidated. Any attempt |
| * to access its contents will be considered as an overflow. |
| */ |
| ByteStreamBuffer::ByteStreamBuffer(ByteStreamBuffer &&other) |
| { |
| *this = std::move(other); |
| } |
| |
| /** |
| * \brief Replace the contents of the buffer with those of \a other using move |
| * semantics |
| * \param[in] other The other buffer |
| * |
| * After the assignment the \a other buffer is invalidated. Any attempt to |
| * access its contents will be considered as an overflow. |
| */ |
| ByteStreamBuffer &ByteStreamBuffer::operator=(ByteStreamBuffer &&other) |
| { |
| parent_ = other.parent_; |
| base_ = other.base_; |
| size_ = other.size_; |
| overflow_ = other.overflow_; |
| read_ = other.read_; |
| write_ = other.write_; |
| |
| other.parent_ = nullptr; |
| other.base_ = nullptr; |
| other.size_ = 0; |
| other.overflow_ = false; |
| other.read_ = nullptr; |
| other.write_ = nullptr; |
| |
| return *this; |
| } |
| |
| /** |
| * \fn ByteStreamBuffer::base() |
| * \brief Retrieve a pointer to the start location of the managed memory buffer |
| * \return A pointer to the managed memory buffer |
| */ |
| |
| /** |
| * \fn ByteStreamBuffer::offset() |
| * \brief Retrieve the offset of the current access location from the base |
| * \return The offset in bytes |
| */ |
| |
| /** |
| * \fn ByteStreamBuffer::size() |
| * \brief Retrieve the size of the managed memory buffer |
| * \return The size of managed memory buffer |
| */ |
| |
| /** |
| * \fn ByteStreamBuffer::overflow() |
| * \brief Check if the buffer has overflown |
| * \return True if the buffer has overflow, false otherwise |
| */ |
| |
| void ByteStreamBuffer::setOverflow() |
| { |
| if (parent_) |
| parent_->setOverflow(); |
| |
| overflow_ = true; |
| } |
| |
| /** |
| * \brief Carve out an area of \a size bytes into a new ByteStreamBuffer |
| * \param[in] size The size of the newly created memory buffer |
| * |
| * This function carves out an area of \a size bytes from the buffer into a new |
| * ByteStreamBuffer, and returns the new buffer. It operates identically to a |
| * read or write access from the point of view of the current buffer, but allows |
| * the new buffer to be read or written at a later time after other read or |
| * write accesses on the current buffer. |
| * |
| * \return A newly created ByteStreamBuffer of \a size |
| */ |
| ByteStreamBuffer ByteStreamBuffer::carveOut(size_t size) |
| { |
| if (!size_ || overflow_) |
| return ByteStreamBuffer(static_cast<const uint8_t *>(nullptr), 0); |
| |
| const uint8_t *curr = read_ ? read_ : write_; |
| if (curr + size > base_ + size_) { |
| LOG(Serialization, Error) |
| << "Unable to reserve " << size << " bytes"; |
| setOverflow(); |
| |
| return ByteStreamBuffer(static_cast<const uint8_t *>(nullptr), 0); |
| } |
| |
| if (read_) { |
| ByteStreamBuffer b(read_, size); |
| b.parent_ = this; |
| read_ += size; |
| return b; |
| } else { |
| ByteStreamBuffer b(write_, size); |
| b.parent_ = this; |
| write_ += size; |
| return b; |
| } |
| } |
| |
| /** |
| * \brief Skip \a size bytes from the buffer |
| * \param[in] size The number of bytes to skip |
| * |
| * This function skips the next \a size bytes from the buffer. |
| * |
| * \return 0 on success, a negative error code otherwise |
| * \retval -ENOSPC no more space is available in the managed memory buffer |
| */ |
| int ByteStreamBuffer::skip(size_t size) |
| { |
| if (overflow_) |
| return -ENOSPC; |
| |
| const uint8_t *curr = read_ ? read_ : write_; |
| if (curr + size > base_ + size_) { |
| LOG(Serialization, Error) |
| << "Unable to skip " << size << " bytes"; |
| setOverflow(); |
| |
| return -ENOSPC; |
| } |
| |
| if (read_) { |
| read_ += size; |
| } else { |
| memset(write_, 0, size); |
| write_ += size; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * \fn template<typename T> int ByteStreamBuffer::read(T *t) |
| * \brief Read data from the managed memory buffer into \a t |
| * \param[out] t Pointer to the memory containing the read data |
| * \return 0 on success, a negative error code otherwise |
| * \retval -EACCES attempting to read from a write buffer |
| * \retval -ENOSPC no more space is available in the managed memory buffer |
| */ |
| |
| /** |
| * \fn template<typename T> int ByteStreamBuffer::read(const Span<T> &data) |
| * \brief Read data from the managed memory buffer into Span \a data |
| * \param[out] data Span representing the destination memory |
| * \return 0 on success, a negative error code otherwise |
| * \retval -EACCES attempting to read from a write buffer |
| * \retval -ENOSPC no more space is available in the managed memory buffer |
| */ |
| |
| /** |
| * \fn template<typename T> const T *ByteStreamBuffer::read(size_t count) |
| * \brief Read data from the managed memory buffer without performing a copy |
| * \param[in] count Number of data items to read |
| * |
| * This function reads \a count elements of type \a T from the buffer. Unlike |
| * the other read variants, it doesn't copy the data but returns a pointer to |
| * the first element. If data can't be read for any reason (usually due to |
| * reading more data than available), the function returns nullptr. |
| * |
| * \return A pointer to the data on success, or nullptr otherwise |
| */ |
| |
| /** |
| * \fn template<typename T> int ByteStreamBuffer::write(const T *t) |
| * \brief Write \a t to the managed memory buffer |
| * \param[in] t The data to write to memory |
| * \return 0 on success, a negative error code otherwise |
| * \retval -EACCES attempting to write to a read buffer |
| * \retval -ENOSPC no more space is available in the managed memory buffer |
| */ |
| |
| /** |
| * \fn template<typename T> int ByteStreamBuffer::write(const Span<T> &data) |
| * \brief Write \a data to the managed memory buffer |
| * \param[in] data The data to write to memory |
| * \return 0 on success, a negative error code otherwise |
| * \retval -EACCES attempting to write to a read buffer |
| * \retval -ENOSPC no more space is available in the managed memory buffer |
| */ |
| |
| const uint8_t *ByteStreamBuffer::read(size_t size, size_t count) |
| { |
| if (!read_) |
| return nullptr; |
| |
| if (overflow_) |
| return nullptr; |
| |
| size_t bytes; |
| if (__builtin_mul_overflow(size, count, &bytes)) { |
| setOverflow(); |
| return nullptr; |
| } |
| |
| if (read_ + bytes > base_ + size_) { |
| LOG(Serialization, Error) |
| << "Unable to read " << bytes << " bytes: out of bounds"; |
| setOverflow(); |
| return nullptr; |
| } |
| |
| const uint8_t *data = read_; |
| read_ += bytes; |
| return data; |
| } |
| |
| int ByteStreamBuffer::read(uint8_t *data, size_t size) |
| { |
| if (!read_) |
| return -EACCES; |
| |
| if (overflow_) |
| return -ENOSPC; |
| |
| if (read_ + size > base_ + size_) { |
| LOG(Serialization, Error) |
| << "Unable to read " << size << " bytes: out of bounds"; |
| setOverflow(); |
| return -ENOSPC; |
| } |
| |
| memcpy(data, read_, size); |
| read_ += size; |
| |
| return 0; |
| } |
| |
| int ByteStreamBuffer::write(const uint8_t *data, size_t size) |
| { |
| if (!write_) |
| return -EACCES; |
| |
| if (overflow_) |
| return -ENOSPC; |
| |
| if (write_ + size > base_ + size_) { |
| LOG(Serialization, Error) |
| << "Unable to write " << size << " bytes: no space left"; |
| setOverflow(); |
| return -ENOSPC; |
| } |
| |
| memcpy(write_, data, size); |
| write_ += size; |
| |
| return 0; |
| } |
| |
| } /* namespace libcamera */ |