Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions include/boost/mysql/impl/internal/next_power_of_two.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// Copyright (c) 2026 Vladislav Soulgard (vsoulgard at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef BOOST_MYSQL_IMPL_INTERNAL_NEXT_POWER_OF_TWO_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_NEXT_POWER_OF_TWO_HPP

#include <boost/assert.hpp>

#include <type_traits>
#include <limits>

namespace boost {
namespace mysql {
namespace detail {

template<int Shift, class UnsignedInt, bool Continue>
struct recursive_shift_or;

// Apply shift and continue to the next power of 2
template<int Shift, class UnsignedInt>
struct recursive_shift_or<Shift, UnsignedInt, true>
{
static void apply(UnsignedInt& n)
{
n |= n >> Shift;
recursive_shift_or<Shift * 2, UnsignedInt, (Shift * 2 < sizeof(UnsignedInt) * 8)>::apply(n);
}
};

// Stop recursion when shift exceeds type width
template<int Shift, class UnsignedInt>
struct recursive_shift_or<Shift, UnsignedInt, false>
{
static void apply(UnsignedInt&) {}
};

// Returns the smallest power of two greater than or equal to n.
// Precondition: n must not exceed the largest power of two that fits
// in UnsignedInt. For example:
// - uint8_t: n <= 128 (2^7)
// - uint16_t: n <= 32768 (2^15)
// - uint32_t: n <= 2147483648 (2^31)
// - uint64_t: n <= 9223372036854775808 (2^63)
//
// Passing a larger value results in undefined behavior (overflow).
// In debug builds, this is caught by BOOST_ASSERT.
template<class UnsignedInt>
UnsignedInt next_power_of_two(UnsignedInt n) noexcept
{
static_assert(std::is_unsigned<UnsignedInt>::value, "");
// Assert overflow (if value is bigger than maximum power)
BOOST_ASSERT(!(n > (std::numeric_limits<UnsignedInt>::max() >> 1) + 1));
if (n == 0) return 1;
n--;
// Fill all lower bits
recursive_shift_or<1, UnsignedInt, (1 < sizeof(UnsignedInt) * 8)>::apply(n);
return n + 1;
}

} // namespace detail
} // namespace mysql
} // namespace boost

#endif
12 changes: 11 additions & 1 deletion include/boost/mysql/impl/internal/sansio/message_reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,17 @@ class message_reader
BOOST_ATTRIBUTE_NODISCARD
error_code prepare_buffer()
{
buffer_.remove_reserved();
constexpr std::size_t small_move_threshold = 1024;
const std::size_t occupied_space = buffer_.pending_size() + buffer_.current_message_size();
// Compact the buffer (remove reserved area) if one of the following holds:
// 1. The cost of memmove is low: active data (current_message + pending)
// is small enough to make the copy cheap.
// 2. Compaction could prevent a reallocation.
if (occupied_space <= small_move_threshold ||
(state_.required_size > buffer_.free_size()))
{
buffer_.remove_reserved();
}
auto ec = buffer_.grow_to_fit(state_.required_size);
if (ec)
return ec;
Expand Down
12 changes: 10 additions & 2 deletions include/boost/mysql/impl/internal/sansio/read_buffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include <boost/mysql/client_errc.hpp>
#include <boost/mysql/error_code.hpp>

#include <boost/mysql/impl/internal/next_power_of_two.hpp>

#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/core/span.hpp>
Expand Down Expand Up @@ -138,14 +140,20 @@ class read_buffer
}

// Makes sure the free size is at least n bytes long; resizes the buffer if required
// Buffer grows to power of two, unless limited by max_size
BOOST_ATTRIBUTE_NODISCARD
error_code grow_to_fit(std::size_t n)
{
if (free_size() < n)
{
std::size_t new_size = buffer_.size() + n - free_size();
std::size_t required_size = buffer_.size() + n - free_size();
std::size_t new_size = next_power_of_two<std::size_t>(required_size);
if (new_size > max_size_)
return client_errc::max_buffer_size_exceeded;
{
new_size = required_size;
if (new_size > max_size_)
return client_errc::max_buffer_size_exceeded;
Comment thread
anarthal marked this conversation as resolved.
}
buffer_.resize(new_size);
}
return error_code();
Expand Down
1 change: 1 addition & 0 deletions test/unit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ add_executable(

test/impl/dt_to_string.cpp
test/impl/ssl_context_with_default.cpp
test/impl/next_power_of_two.cpp
test/impl/variant_stream.cpp

test/spotchecks/connection_use_after_move.cpp
Expand Down
1 change: 1 addition & 0 deletions test/unit/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ run

test/impl/dt_to_string.cpp
test/impl/ssl_context_with_default.cpp
test/impl/next_power_of_two.cpp
test/impl/variant_stream.cpp

test/spotchecks/connection_use_after_move.cpp
Expand Down
90 changes: 90 additions & 0 deletions test/unit/test/impl/next_power_of_two.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//
// Copyright (c) 2026 Vladislav Soulgard (vsoulgard at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <boost/mysql/impl/internal/next_power_of_two.hpp>

#include <boost/test/unit_test.hpp>

#include <cstdint>

using namespace boost::mysql::detail;

BOOST_AUTO_TEST_SUITE(test_next_power_of_two)

BOOST_AUTO_TEST_CASE(basic)
{
// n = 0 (special case)
BOOST_TEST(next_power_of_two<std::size_t>(0u) == 1u);

// n is already power of two
BOOST_TEST(next_power_of_two<std::size_t>(1u) == 1u);
BOOST_TEST(next_power_of_two<std::size_t>(2u) == 2u);
BOOST_TEST(next_power_of_two<std::size_t>(4u) == 4u);
BOOST_TEST(next_power_of_two<std::size_t>(8u) == 8u);
BOOST_TEST(next_power_of_two<std::size_t>(16u) == 16u);
BOOST_TEST(next_power_of_two<std::size_t>(32u) == 32u);
BOOST_TEST(next_power_of_two<std::size_t>(64u) == 64u);
BOOST_TEST(next_power_of_two<std::size_t>(128u) == 128u);

// n just below power of two
BOOST_TEST(next_power_of_two<std::size_t>(3u) == 4u);
BOOST_TEST(next_power_of_two<std::size_t>(7u) == 8u);
BOOST_TEST(next_power_of_two<std::size_t>(15u) == 16u);
BOOST_TEST(next_power_of_two<std::size_t>(31u) == 32u);
BOOST_TEST(next_power_of_two<std::size_t>(63u) == 64u);
BOOST_TEST(next_power_of_two<std::size_t>(127u) == 128u);

// n just above power of two
BOOST_TEST(next_power_of_two<std::size_t>(5u) == 8u);
BOOST_TEST(next_power_of_two<std::size_t>(9u) == 16u);
BOOST_TEST(next_power_of_two<std::size_t>(17u) == 32u);
BOOST_TEST(next_power_of_two<std::size_t>(33u) == 64u);
BOOST_TEST(next_power_of_two<std::size_t>(65u) == 128u);
BOOST_TEST(next_power_of_two<std::size_t>(129u) == 256u);

// n is random value
BOOST_TEST(next_power_of_two<std::size_t>(6u) == 8u);
BOOST_TEST(next_power_of_two<std::size_t>(13u) == 16u);
BOOST_TEST(next_power_of_two<std::size_t>(21u) == 32u);
BOOST_TEST(next_power_of_two<std::size_t>(45u) == 64u);
BOOST_TEST(next_power_of_two<std::size_t>(89u) == 128u);
BOOST_TEST(next_power_of_two<std::size_t>(200u) == 256u);
BOOST_TEST(next_power_of_two<std::size_t>(300u) == 512u);
BOOST_TEST(next_power_of_two<std::size_t>(400u) == 512u);
BOOST_TEST(next_power_of_two<std::size_t>(505u) == 512u);
BOOST_TEST(next_power_of_two<std::size_t>(888u) == 1024u);
}
Comment thread
anarthal marked this conversation as resolved.

BOOST_AUTO_TEST_CASE(different_types)
{
// uint8_t
BOOST_TEST(next_power_of_two<std::uint8_t>(0u) == 1u);
BOOST_TEST(next_power_of_two<std::uint8_t>(62u) == 64u);
BOOST_TEST(next_power_of_two<std::uint8_t>(100u) == 128u);
BOOST_TEST(next_power_of_two<std::uint8_t>(128u) == 128u);

// uint16_t
BOOST_TEST(next_power_of_two<std::uint16_t>(0u) == 1u);
BOOST_TEST(next_power_of_two<std::uint16_t>(1000u) == 1024u);
BOOST_TEST(next_power_of_two<std::uint16_t>(16383u) == 16384u);
BOOST_TEST(next_power_of_two<std::uint16_t>(32768u) == 32768u);

// uint32_t
BOOST_TEST(next_power_of_two<std::uint32_t>(0u) == 1u);
BOOST_TEST(next_power_of_two<std::uint32_t>(100000u) == 131072u);
BOOST_TEST(next_power_of_two<std::uint32_t>(1u << 30) == 1u << 30);
BOOST_TEST(next_power_of_two<std::uint32_t>((1u << 30) + 1) == 1u << 31);

// uint64_t
BOOST_TEST(next_power_of_two<std::uint64_t>(0u) == 1u);
BOOST_TEST(next_power_of_two<std::uint64_t>(1ull << 40) == 1ull << 40);
BOOST_TEST(next_power_of_two<std::uint64_t>((1ull << 40) + 1) == 1ull << 41);
BOOST_TEST(next_power_of_two<std::uint64_t>(1ull << 62) == 1ull << 62);
BOOST_TEST(next_power_of_two<std::uint64_t>((1ull << 62) + 1) == 1ull << 63);
}

BOOST_AUTO_TEST_SUITE_END()
Loading
Loading