Skip to content
Snippets Groups Projects
buffer.hh 6.71 KiB
Newer Older
  • Learn to ignore specific revisions
  • Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
    #ifndef __VEREIGN_BYTES_BUFFER_HH
    #define __VEREIGN_BYTES_BUFFER_HH
    
    #include <vereign/bytes/view.hh>
    #include <cstring>
    
    namespace vereign::bytes {
    
    
    /**
     * Dynamically expandable memory buffer.
     *
     * The buffer is a 3-tuple - pointer, size and capacity.
     * Typically used in functions for output parameters and return values.
     * Provides API that is easy to use with C APIs.
     *
     * The buffer is move only.
     */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
    class Buffer {
    public:
    
      /**
       * Creates empty buffer.
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      Buffer() noexcept;
    
    
      /**
       * Creates a buffer with reserved memory capacity.
       *
       * The size of the buffer is zero.
       *
       * @param cap The capacity of the buffer.
       *
       * @throws std::bad_alloc when memory reservation fails.
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      Buffer(std::size_t cap);
    
    
      /**
       * Creates a buffer by copying from source bytes view.
       *
       * @param src The source that will be copied from.
       *
       * @throws std::bad_alloc when memory reservation fails.
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      Buffer(View src);
    
    
      /**
       * The buffer is movable.
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      Buffer(Buffer&& other) noexcept;
      auto operator=(Buffer&& other) noexcept -> Buffer&;
    
      // disable copying
      Buffer(const Buffer&) = delete;
      auto operator=(const Buffer&) -> Buffer& = delete;
    
    
      /**
       * Frees the buffer memory.
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      ~Buffer();
    
    
      /**
       * Returns a pointer to the first byte of the buffer.
       *
       * Buffer::begin(), Buffer::end() pair is useful for range loops.
       *
       * Example:
       * @code
       * auto buf = bytes::Buffer{bytes::View("foo")};
       * for (const auto& byte : buf) {
       *   byte = 'x';
       * }
       *
       * assert(buf.View().String() == "xxx");
       * @endcode
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      auto begin() noexcept -> uint8_t*;
    
    
      /**
       * Returns a pointer to the first byte of the buffer.
       *
       * Buffer::begin(), Buffer::end() pair is useful for range loops.
       *
       * Example:
       * @code
       * auto buf = bytes::Buffer{bytes::View("foo bar")};
       * std::string s;
       *
       * for (const auto& byte : buf) {
       *   s += byte;
       * }
       *
       * assert(s == "foo bar");
       * @endcode
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      auto begin() const noexcept -> const uint8_t*;
    
    
      /**
       * Returns a pointer to the byte following the last byte in the buffer.
       *
       * Note that this is the last byte in the range [0, size).
       * It is often used when calling C APIs.
       *
       * Example:
       * @code
       * auto buf = bytes::Buffer{bytes::View("foo bar")};
       *
       * buf.Reserve(4);
       * std::strncpy((char*)buf.end(), " baz", 4);
       * buf.IncSize(4);
       *
       * assert(buf.View().String() == "foo bar baz");
       * @endcode
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      auto end() noexcept -> uint8_t*;
    
    
      /**
       * Returns a read only pointer to the byte following the last byte in the buffer.
       *
       * Note that this is the last byte in the range [0, size).
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      auto end() const noexcept -> const uint8_t*;
    
    
      /**
       * Access a byte in the range of [0, cap).
       *
       * @param index The index of the byte to access.
       *
       * @throws std::runtime_error when the passed index is out of bounds.
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      auto operator[](std::size_t index) -> uint8_t&;
    
    
      /**
       * Read only access a byte in the range of [0, cap).
       *
       * @param index The index of the byte to access.
       *
       * @throws std::runtime_error when the passed index is out of bounds.
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      auto operator[](std::size_t index) const -> const uint8_t&;
    
    
      /**
       * Retrieve buffer size.
       *
       * @returns the buffer size.
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      auto Size() const noexcept -> std::size_t;
    
    
      /**
       * Retrieve buffer capacity.
       *
       * @returns the buffer capacity.
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      auto Cap() const noexcept -> std::size_t;
    
    
      /**
       * Reserve memory, so that there is at least `size` free capacity.
       *
       * If there is already enough free capacity, no memory allocation is done.
       * The allocated memory may be bigger than what is needed.
       *
       * If the call is successful then it is guaranteed that `this->FreeCap() >= size`.
       *
       * Example:
       * @code
       * auto buf = bytes::Buffer{bytes::View("foo bar")};
       *
       * buf.Reserve(4); // ensure there will be a free capacity for 4 bytes
       * std::strncpy((char*)buf.end(), " baz", 4); // copy the bytes
       * buf.IncSize(4); // update the buffer size with the newly written bytes
       *
       * assert(buf.View().String() == "foo bar baz");
       * @endcode
       *
       * @param size The desired free capacity.
       *
       * @throws std::bad_alloc when memory allocation fails.
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      void Reserve(std::size_t size);
    
    
      /**
       * Sets the buffer size to zero.
       *
       * This does not free any memory, so the capacity stays the intact, and the buffer can be reused.
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      void Reset();
    
    
      /**
       * Increments the size of the buffer.
       *
       * It is typically used after some function has written bytes to the end of the buffer.
       *
       * @param val The value that will be added to the current size.
       */
      void IncSize(std::size_t val);
    
      /**
       * Retrieve the buffer free capacity.
       *
       * This is equal to `this->Cap() - this->Size()`.
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      auto FreeCap() const noexcept -> std::size_t;
    
    
      /**
       * Adds bytes up to the currently available buffer capacity.
       *
       * After the operation succeeds, the buffer size will be incremented with the number of bytes
       * that have been copied.
       *
       * Example:
       * @code
       * auto buf = bytes::Buffer{3};
       * buf.WriteWithinCap(bytes::View("foo bar"));
       *
       * // only 3 bytes are written
       * assert(buf.View.String() == "foo");
       * @endcode
       *
       * @param src The source that will be appended to the buffer.
       * @returns The amount of bytes that were actually copied into the buffer.
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      auto WriteWithinCap(bytes::View src) noexcept -> std::size_t;
    
    
      /**
       * Adds a source view of bytes to the buffer.
       *
       * If the buffer does not have enough capacity, it will be expanded.
       *
       * Example:
       * @code
       * auto buf = bytes::Buffer{3};
       * buf.WriteWithinCap(bytes::View("foo bar"));
       *
       * // all bytes are written
       * assert(buf.View.String() == "foo bar");
       * @endcode
       *
       * @param The source that will be appended to the buffer.
       * @returns The amount of bytes that were copied into the buffer. That is equal to src.Size().
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      auto Write(bytes::View src) -> std::size_t;
    
    
      /**
       * Retrieve a read only view of the buffer.
       *
       * Example:
       * @code
       * auto buf = bytes::Buffer{bytes::View("123")};
       * assert(buf.View().String() == "123");
       * @endcode
       *
       * @returns a read only view range [0, this->Size()).
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      auto View() const noexcept -> bytes::View;
    
    
      /**
       * Retrieve a read only view of the buffer staring from a given offset.
       *
       * Example:
       * @code
       * auto buf = bytes::Buffer{bytes::View("123")};
       * assert(buf.View(1).String() == "23");
       * @endcode
       *
       * @returns a read only view range [start, this->Size()).
       */
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      auto View(std::size_t start) const noexcept -> bytes::View;
    
    private:
      std::size_t cap_;
      std::size_t size_;
      uint8_t* data_;
    };
    
    } // namespace vereign::bytes
    
    #endif // __VEREIGN_BYTES_BUFFER_HH