Make the whole code base consistent by placing the doxygen \file block above the libcamera namespace opening. There's a single offender, and two locations in which a white line is missing. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Umang Jain <email@uajain.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
344 lines
9.6 KiB
C++
344 lines
9.6 KiB
C++
/* 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/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<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 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<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 */
|