mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-17 00:36:34 +00:00
[libc++] Slightly simplify max_size and add new tests for vector (#119990)
This PR slightly simplifies the implementation of `vector<bool>::max_size` and adds extensive tests for the `max_size()` function for both `vector<bool>` and `vector<T>`. The main purposes of the new tests include: - Verify correctness of `max_size()` under various `size_type` and `difference_type` definitions: check that `max_size()` works properly with allocators that have custom `size_type` and `difference_type`. This is particularly useful for `vector<bool>`, as different `size_type` lead to different `__storage_type` of different word lengths, resulting in varying `max_size()` values for `vector<bool>`. Additionally, different `difference_type` also sets different upper limit of `max_size()` for both `vector<bool>` and `std::vector`. These tests were previously missing. - Eliminate incorrect implementations: Special tests are added to identify and reject incorrect implementations of `vector<bool>::max_size` that unconditionally return `std::min<size_type>(size-max, __internal_cap_to_external(allocator-max-size))`. This can cause overflow in the `__internal_cap_to_external()` call and lead to incorrect results. The new tests ensure that such incorrect implementations are identified.
This commit is contained in:
parent
4a2a8ed70d
commit
efa287dd8a
@ -533,10 +533,8 @@ template <class _Allocator>
|
||||
_LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<bool, _Allocator>::size_type
|
||||
vector<bool, _Allocator>::max_size() const _NOEXCEPT {
|
||||
size_type __amax = __storage_traits::max_size(__alloc_);
|
||||
size_type __nmax = numeric_limits<size_type>::max() / 2; // end() >= begin(), always
|
||||
if (__nmax / __bits_per_word <= __amax)
|
||||
return __nmax;
|
||||
return __internal_cap_to_external(__amax);
|
||||
size_type __nmax = numeric_limits<difference_type>::max();
|
||||
return __nmax / __bits_per_word <= __amax ? __nmax : __internal_cap_to_external(__amax);
|
||||
}
|
||||
|
||||
// Precondition: __new_size > capacity()
|
||||
|
@ -0,0 +1,105 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// <vector>
|
||||
// vector<bool>
|
||||
|
||||
// size_type max_size() const;
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "min_allocator.h"
|
||||
#include "sized_allocator.h"
|
||||
#include "test_allocator.h"
|
||||
#include "test_macros.h"
|
||||
|
||||
#if TEST_STD_VER >= 11
|
||||
|
||||
template <typename Alloc>
|
||||
TEST_CONSTEXPR_CXX20 void test(const std::vector<bool, Alloc>& v) {
|
||||
using Vector = std::vector<bool, Alloc>;
|
||||
using size_type = typename Vector::size_type;
|
||||
using difference_type = typename Vector::difference_type;
|
||||
const size_type max_dist = static_cast<size_type>(std::numeric_limits<difference_type>::max());
|
||||
assert(v.max_size() <= max_dist);
|
||||
|
||||
// The following check is specific to libc++ implementation details and is not portable to libstdc++
|
||||
// and MSVC STL, as they use different types for the underlying word storage.
|
||||
# if defined(_LIBCPP_VERSION)
|
||||
using storage_type = typename Vector::__storage_type;
|
||||
using storage_alloc = typename std::allocator_traits<Alloc>::template rebind_alloc<storage_type>;
|
||||
using storage_traits = typename std::allocator_traits<Alloc>::template rebind_traits<storage_type>;
|
||||
const size_type max_alloc = storage_traits::max_size(storage_alloc(v.get_allocator()));
|
||||
std::size_t bits_per_word = sizeof(storage_type) * CHAR_BIT;
|
||||
const size_type max_size = max_dist / bits_per_word < max_alloc ? max_dist : max_alloc * bits_per_word;
|
||||
assert(v.max_size() / bits_per_word <= max_alloc); // max_alloc * bits_per_word may overflow
|
||||
assert(v.max_size() == max_size);
|
||||
# endif // defined(_LIBCPP_VERSION)
|
||||
}
|
||||
|
||||
#endif // TEST_STD_VER >= 11
|
||||
|
||||
TEST_CONSTEXPR_CXX20 bool tests() {
|
||||
// The following check is specific to libc++ implementation details and is not portable to libstdc++
|
||||
// and MSVC STL, as they use different types for the underlying word storage.
|
||||
#if defined(_LIBCPP_VERSION)
|
||||
// Test cases where v.max_size() is determined by allocator::max_size()
|
||||
{
|
||||
using Alloc = limited_allocator<bool, 10>;
|
||||
using Vector = std::vector<bool, Alloc>;
|
||||
using storage_type = Vector::__storage_type;
|
||||
Vector v;
|
||||
std::size_t bits_per_word = sizeof(storage_type) * CHAR_BIT;
|
||||
assert(v.max_size() == 10 * bits_per_word);
|
||||
}
|
||||
#endif // defined(_LIBCPP_VERSION)
|
||||
|
||||
#if TEST_STD_VER >= 11
|
||||
|
||||
// Test with various allocators and different `size_type`s
|
||||
{
|
||||
test(std::vector<bool>());
|
||||
test(std::vector<bool, std::allocator<int> >());
|
||||
test(std::vector<bool, min_allocator<bool> >());
|
||||
test(std::vector<bool, test_allocator<bool> >(test_allocator<bool>(1)));
|
||||
test(std::vector<bool, other_allocator<bool> >(other_allocator<bool>(5)));
|
||||
test(std::vector<bool, sized_allocator<bool, std::uint8_t, std::int8_t> >());
|
||||
test(std::vector<bool, sized_allocator<bool, std::uint16_t, std::int16_t> >());
|
||||
test(std::vector<bool, sized_allocator<bool, std::uint32_t, std::int32_t> >());
|
||||
test(std::vector<bool, sized_allocator<bool, std::uint64_t, std::int64_t> >());
|
||||
test(std::vector<bool, limited_allocator<bool, static_cast<std::size_t>(-1)> >());
|
||||
}
|
||||
|
||||
// Test cases to identify incorrect implementations that unconditionally compute an internal-to-external
|
||||
// capacity in a way that can overflow, leading to incorrect results.
|
||||
{
|
||||
test(std::vector<bool, limited_allocator<bool, static_cast<std::size_t>(-1) / 61> >());
|
||||
test(std::vector<bool, limited_allocator<bool, static_cast<std::size_t>(-1) / 63> >());
|
||||
}
|
||||
|
||||
#endif // TEST_STD_VER >= 11
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
tests();
|
||||
|
||||
#if TEST_STD_VER >= 20
|
||||
static_assert(tests());
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
@ -10,16 +10,36 @@
|
||||
|
||||
// size_type max_size() const;
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "min_allocator.h"
|
||||
#include "sized_allocator.h"
|
||||
#include "test_allocator.h"
|
||||
#include "test_macros.h"
|
||||
|
||||
#if TEST_STD_VER >= 11
|
||||
|
||||
TEST_CONSTEXPR_CXX20 bool test() {
|
||||
template <typename T, typename Alloc>
|
||||
TEST_CONSTEXPR_CXX20 void test(const std::vector<T, Alloc>& v) {
|
||||
using Vector = std::vector<T, Alloc>;
|
||||
using alloc_traits = std::allocator_traits<typename Vector::allocator_type>;
|
||||
using size_type = typename Vector::size_type;
|
||||
using difference_type = typename Vector::difference_type;
|
||||
const size_type max_dist = static_cast<size_type>(std::numeric_limits<difference_type>::max());
|
||||
const size_type max_alloc = alloc_traits::max_size(v.get_allocator());
|
||||
assert(v.max_size() <= max_dist);
|
||||
assert(v.max_size() <= max_alloc);
|
||||
LIBCPP_ASSERT(v.max_size() == std::min<size_type>(max_dist, max_alloc));
|
||||
}
|
||||
|
||||
#endif // TEST_STD_VER >= 11
|
||||
|
||||
TEST_CONSTEXPR_CXX20 bool tests() {
|
||||
{
|
||||
typedef limited_allocator<int, 10> A;
|
||||
typedef std::vector<int, A> C;
|
||||
@ -30,29 +50,48 @@ TEST_CONSTEXPR_CXX20 bool test() {
|
||||
{
|
||||
typedef limited_allocator<int, (std::size_t)-1> A;
|
||||
typedef std::vector<int, A> C;
|
||||
const C::size_type max_dist =
|
||||
static_cast<C::size_type>(std::numeric_limits<C::difference_type>::max());
|
||||
const C::size_type max_dist = static_cast<C::size_type>(std::numeric_limits<C::difference_type>::max());
|
||||
C c;
|
||||
assert(c.max_size() <= max_dist);
|
||||
LIBCPP_ASSERT(c.max_size() == max_dist);
|
||||
}
|
||||
{
|
||||
typedef std::vector<char> C;
|
||||
const C::size_type max_dist =
|
||||
static_cast<C::size_type>(std::numeric_limits<C::difference_type>::max());
|
||||
const C::size_type max_dist = static_cast<C::size_type>(std::numeric_limits<C::difference_type>::max());
|
||||
C c;
|
||||
assert(c.max_size() <= max_dist);
|
||||
assert(c.max_size() <= alloc_max_size(c.get_allocator()));
|
||||
LIBCPP_ASSERT(c.max_size() == std::min(max_dist, alloc_max_size(c.get_allocator())));
|
||||
}
|
||||
|
||||
#if TEST_STD_VER >= 11
|
||||
|
||||
// Test with various allocators and diffrent size_type
|
||||
{
|
||||
test(std::vector<int>());
|
||||
test(std::vector<short, std::allocator<short> >());
|
||||
test(std::vector<unsigned, min_allocator<unsigned> >());
|
||||
test(std::vector<char, test_allocator<char> >(test_allocator<char>(1)));
|
||||
test(std::vector<std::size_t, other_allocator<std::size_t> >(other_allocator<std::size_t>(5)));
|
||||
test(std::vector<int, sized_allocator<int, std::uint8_t, std::int8_t> >());
|
||||
test(std::vector<int, sized_allocator<int, std::uint16_t, std::int16_t> >());
|
||||
test(std::vector<int, sized_allocator<int, std::uint32_t, std::int32_t> >());
|
||||
test(std::vector<int, sized_allocator<int, std::uint64_t, std::int64_t> >());
|
||||
test(std::vector<int, limited_allocator<int, static_cast<std::size_t>(-1)> >());
|
||||
test(std::vector<int, limited_allocator<int, static_cast<std::size_t>(-1) / 2> >());
|
||||
test(std::vector<int, limited_allocator<int, static_cast<std::size_t>(-1) / 4> >());
|
||||
}
|
||||
|
||||
#endif // TEST_STD_VER >= 11
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
tests();
|
||||
|
||||
#if TEST_STD_VER > 17
|
||||
static_assert(test());
|
||||
static_assert(tests());
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user