/* 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 #include #include "libcamera/internal/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() method. * * 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() method. */ /** * \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 method 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(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(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 method 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 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 int ByteStreamBuffer::read(const Span &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 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 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 int ByteStreamBuffer::write(const Span &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 */