Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-04-19 08:33:52

0001 //
0002 // Copyright (c) 2019-2024 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
0003 //
0004 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0005 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 
0008 #ifndef BOOST_MYSQL_IMPL_INTERNAL_SANSIO_READ_BUFFER_HPP
0009 #define BOOST_MYSQL_IMPL_INTERNAL_SANSIO_READ_BUFFER_HPP
0010 
0011 #include <boost/mysql/client_errc.hpp>
0012 #include <boost/mysql/error_code.hpp>
0013 
0014 #include <boost/assert.hpp>
0015 #include <boost/config.hpp>
0016 #include <boost/core/span.hpp>
0017 
0018 #include <cstddef>
0019 #include <cstdint>
0020 #include <cstring>
0021 #include <vector>
0022 
0023 namespace boost {
0024 namespace mysql {
0025 namespace detail {
0026 
0027 // Custom buffer type optimized for read operations performed in the MySQL protocol.
0028 // The buffer is a single, resizable chunk of memory with four areas:
0029 //   - Reserved area: messages that have already been read but are kept alive,
0030 //     either because we need them or because we haven't cleaned them yet.
0031 //   - Current message area: delimits the message we are currently parsing.
0032 //   - Pending bytes area: bytes we've read but haven't been parsed into a message yet.
0033 //   - Free area: free space for more bytes to be read.
0034 class read_buffer
0035 {
0036     std::vector<std::uint8_t> buffer_;
0037     std::size_t current_message_offset_{0};
0038     std::size_t pending_offset_{0};
0039     std::size_t free_offset_{0};
0040     std::size_t max_size_;
0041 
0042 public:
0043     read_buffer(std::size_t size, std::size_t max_size = static_cast<std::size_t>(-1))
0044         : buffer_(size), max_size_(max_size)
0045     {
0046         BOOST_ASSERT(size <= max_size_);
0047     }
0048 
0049     void reset() noexcept
0050     {
0051         current_message_offset_ = 0;
0052         pending_offset_ = 0;
0053         free_offset_ = 0;
0054     }
0055 
0056     // Whole buffer accessors
0057     const std::uint8_t* first() const noexcept { return buffer_.data(); }
0058     std::size_t size() const noexcept { return buffer_.size(); }
0059     std::size_t max_size() const { return max_size_; }
0060 
0061     // Area accessors
0062     std::uint8_t* reserved_first() noexcept { return buffer_.data(); }
0063     const std::uint8_t* reserved_first() const noexcept { return buffer_.data(); }
0064     std::uint8_t* current_message_first() noexcept { return buffer_.data() + current_message_offset_; }
0065     const std::uint8_t* current_message_first() const noexcept
0066     {
0067         return buffer_.data() + current_message_offset_;
0068     }
0069     std::uint8_t* pending_first() noexcept { return buffer_.data() + pending_offset_; }
0070     const std::uint8_t* pending_first() const noexcept { return buffer_.data() + pending_offset_; }
0071     std::uint8_t* free_first() noexcept { return buffer_.data() + free_offset_; }
0072     const std::uint8_t* free_first() const noexcept { return buffer_.data() + free_offset_; }
0073 
0074     std::size_t reserved_size() const noexcept { return current_message_offset_; }
0075     std::size_t current_message_size() const noexcept { return pending_offset_ - current_message_offset_; }
0076     std::size_t pending_size() const noexcept { return free_offset_ - pending_offset_; }
0077     std::size_t free_size() const noexcept { return buffer_.size() - free_offset_; }
0078 
0079     span<const std::uint8_t> reserved_area() const noexcept { return {reserved_first(), reserved_size()}; }
0080     span<const std::uint8_t> current_message() const noexcept
0081     {
0082         return {current_message_first(), current_message_size()};
0083     }
0084     span<const std::uint8_t> pending_area() const noexcept { return {pending_first(), pending_size()}; }
0085     span<std::uint8_t> free_area() noexcept { return {free_first(), free_size()}; }
0086 
0087     // Moves n bytes from the free to the processing area (e.g. they've been read)
0088     void move_to_pending(std::size_t length) noexcept
0089     {
0090         BOOST_ASSERT(length <= free_size());
0091         free_offset_ += length;
0092     }
0093 
0094     // Moves n bytes from the processing to the current message area
0095     void move_to_current_message(std::size_t length) noexcept
0096     {
0097         BOOST_ASSERT(length <= pending_size());
0098         pending_offset_ += length;
0099     }
0100 
0101     // Removes the last length bytes from the current message area,
0102     // effectively memmove'ing all subsequent bytes backwards.
0103     // Used to remove intermediate headers. length must be > 0
0104     void remove_current_message_last(std::size_t length) noexcept
0105     {
0106         BOOST_ASSERT(length <= current_message_size());
0107         BOOST_ASSERT(length > 0);
0108         std::memmove(pending_first() - length, pending_first(), pending_size());
0109         pending_offset_ -= length;
0110         free_offset_ -= length;
0111     }
0112 
0113     // Moves length bytes from the current message area to the reserved area
0114     // Used to move entire parsed messages or message headers
0115     void move_to_reserved(std::size_t length) noexcept
0116     {
0117         BOOST_ASSERT(length <= current_message_size());
0118         current_message_offset_ += length;
0119     }
0120 
0121     // Removes the reserved area, effectively memmove'ing evth backwards
0122     void remove_reserved() noexcept
0123     {
0124         if (reserved_size() > 0)
0125         {
0126             // If reserved_size() > 0, these ptrs should never be NULL
0127             void* to = buffer_.data();
0128             const void* from = current_message_first();
0129             BOOST_ASSERT(to != nullptr);
0130             BOOST_ASSERT(from != nullptr);
0131             std::size_t currmsg_size = current_message_size();
0132             std::size_t pend_size = pending_size();
0133             std::memmove(to, from, currmsg_size + pend_size);
0134             current_message_offset_ = 0;
0135             pending_offset_ = currmsg_size;
0136             free_offset_ = currmsg_size + pend_size;
0137         }
0138     }
0139 
0140     // Makes sure the free size is at least n bytes long; resizes the buffer if required
0141     BOOST_ATTRIBUTE_NODISCARD
0142     error_code grow_to_fit(std::size_t n)
0143     {
0144         if (free_size() < n)
0145         {
0146             std::size_t new_size = buffer_.size() + n - free_size();
0147             if (new_size > max_size_)
0148                 return client_errc::max_buffer_size_exceeded;
0149             buffer_.resize(new_size);
0150         }
0151         return error_code();
0152     }
0153 };
0154 
0155 }  // namespace detail
0156 }  // namespace mysql
0157 }  // namespace boost
0158 
0159 #endif