forked from boostorg/static_string
Fix handling of precondition violations in insert and replace
This commit is contained in:
@ -1465,7 +1465,13 @@ public:
|
||||
insert(
|
||||
size_type index,
|
||||
const_pointer s,
|
||||
size_type count);
|
||||
size_type count)
|
||||
{
|
||||
BOOST_STATIC_STRING_THROW_IF(
|
||||
index > size(), std::out_of_range{"index > size()"});
|
||||
insert(data() + index, s, s + count);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Insert a string.
|
||||
|
||||
@ -1734,8 +1740,7 @@ public:
|
||||
const_iterator pos,
|
||||
std::initializer_list<value_type> ilist)
|
||||
{
|
||||
const auto offset = pos - begin();
|
||||
return insert_unchecked(offset, ilist.begin(), ilist.size()).begin() + offset;
|
||||
return insert_unchecked(pos, ilist.begin(), ilist.size());
|
||||
}
|
||||
|
||||
/** Insert characters from an object convertible to `string_view_type`.
|
||||
@ -2688,8 +2693,12 @@ public:
|
||||
size_type pos,
|
||||
size_type n1,
|
||||
const_pointer s,
|
||||
size_type n2);
|
||||
|
||||
size_type n2)
|
||||
{
|
||||
BOOST_STATIC_STRING_THROW_IF(
|
||||
pos > size(), std::out_of_range{"pos > size()"});
|
||||
return replace(data() + pos, data() + pos + (std::min)(n1, size() - pos), s, n2);
|
||||
}
|
||||
|
||||
/** Replace a substring with a string.
|
||||
|
||||
@ -2753,7 +2762,12 @@ public:
|
||||
size_type pos,
|
||||
size_type n1,
|
||||
size_type n2,
|
||||
value_type c);
|
||||
value_type c)
|
||||
{
|
||||
BOOST_STATIC_STRING_THROW_IF(
|
||||
pos > size(), std::out_of_range{"pos > size()"});
|
||||
return replace(data() + pos, data() + pos + (std::min)(n1, size() - pos), n2, c);
|
||||
}
|
||||
|
||||
/** Replace a range with a string.
|
||||
|
||||
@ -2768,7 +2782,11 @@ public:
|
||||
|
||||
Strong guarantee.
|
||||
|
||||
@note All references, pointers, or iterators
|
||||
@note The replacement is done unchecked when
|
||||
the capacity of `str` differs from that of the
|
||||
string the function is called on.
|
||||
|
||||
All references, pointers, or iterators
|
||||
referring to contained elements are invalidated. Any
|
||||
past-the-end iterators are also invalidated.
|
||||
|
||||
@ -2790,9 +2808,21 @@ public:
|
||||
const_iterator i1,
|
||||
const_iterator i2,
|
||||
const basic_static_string<M, CharT, Traits>& str)
|
||||
{
|
||||
return replace_unchecked(i1, i2, str.data(), str.size());
|
||||
}
|
||||
|
||||
#ifndef GENERATING_DOCUMENTATION
|
||||
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
||||
basic_static_string&
|
||||
replace(
|
||||
const_iterator i1,
|
||||
const_iterator i2,
|
||||
const basic_static_string& str)
|
||||
{
|
||||
return replace(i1, i2, str.data(), str.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Replace a range with an object convertible to `string_view_type`.
|
||||
|
||||
@ -2844,7 +2874,7 @@ public:
|
||||
const T& t)
|
||||
{
|
||||
string_view_type sv = t;
|
||||
return replace(i1 - begin(), i2 - i1, sv.data(), sv.size());
|
||||
return replace(i1, i2, sv.begin(), sv.end());
|
||||
}
|
||||
|
||||
/** Replace a range with a string.
|
||||
@ -2882,7 +2912,7 @@ public:
|
||||
const_pointer s,
|
||||
size_type n)
|
||||
{
|
||||
return replace(i1 - begin(), i2 - i1, s, n);
|
||||
return replace(i1, i2, s, s + n);
|
||||
}
|
||||
|
||||
/** Replace a range with a string.
|
||||
@ -2954,10 +2984,7 @@ public:
|
||||
const_iterator i1,
|
||||
const_iterator i2,
|
||||
size_type n,
|
||||
value_type c)
|
||||
{
|
||||
return replace(i1 - begin(), i2 - i1, n, c);
|
||||
}
|
||||
value_type c);
|
||||
|
||||
/** Replace a range with a range.
|
||||
|
||||
@ -3061,7 +3088,7 @@ public:
|
||||
const_iterator i2,
|
||||
std::initializer_list<value_type> il)
|
||||
{
|
||||
return replace_unchecked(i1 - begin(), i2 - i1, il.begin(), il.size());
|
||||
return replace_unchecked(i1, i2, il.begin(), il.size());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@ -4040,6 +4067,19 @@ private:
|
||||
size_type pos,
|
||||
size_type n1,
|
||||
const_pointer s,
|
||||
size_type n2)
|
||||
{
|
||||
BOOST_STATIC_STRING_THROW_IF(
|
||||
pos > size(), std::out_of_range{"pos > size()"});
|
||||
return replace_unchecked(data() + pos, data() + pos + n1, s, n2);
|
||||
}
|
||||
|
||||
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
||||
basic_static_string&
|
||||
replace_unchecked(
|
||||
const_iterator i1,
|
||||
const_iterator i2,
|
||||
const_pointer s,
|
||||
size_type n2);
|
||||
|
||||
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
||||
@ -4047,6 +4087,19 @@ private:
|
||||
insert_unchecked(
|
||||
size_type index,
|
||||
const_pointer s,
|
||||
size_type count)
|
||||
{
|
||||
BOOST_STATIC_STRING_THROW_IF(
|
||||
index > size(), std::out_of_range{"index > size()"});
|
||||
insert_unchecked(data() + index, s, count);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
||||
iterator
|
||||
insert_unchecked(
|
||||
const_iterator pos,
|
||||
const_pointer s,
|
||||
size_type count);
|
||||
};
|
||||
|
||||
@ -4394,7 +4447,7 @@ operator<<(
|
||||
std::basic_ostream<CharT, Traits>& os,
|
||||
const basic_static_string<N, CharT, Traits>& s)
|
||||
{
|
||||
return os << basic_string_view<CharT, Traits>(s.data(), s.size());
|
||||
return os << typename basic_static_string<N, CharT, Traits>::string_view_type(s.data(), s.size());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -4891,24 +4944,6 @@ clear() noexcept
|
||||
term();
|
||||
}
|
||||
|
||||
template<std::size_t N, typename CharT, typename Traits>
|
||||
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
||||
auto
|
||||
basic_static_string<N, CharT, Traits>::
|
||||
insert(
|
||||
size_type index,
|
||||
const_pointer s,
|
||||
size_type count) ->
|
||||
basic_static_string&
|
||||
{
|
||||
const auto curr_size = size();
|
||||
const auto curr_data = data();
|
||||
BOOST_STATIC_STRING_THROW_IF(
|
||||
index > curr_size, std::out_of_range{"index > size()"});
|
||||
insert(curr_data + index, s, s + count);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, typename CharT, typename Traits>
|
||||
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
||||
auto
|
||||
@ -4924,10 +4959,9 @@ insert(
|
||||
BOOST_STATIC_STRING_THROW_IF(
|
||||
count > max_size() - curr_size, std::length_error{"count() > max_size() - size()"});
|
||||
const auto index = pos - curr_data;
|
||||
traits_type::move(&curr_data[index + count], &curr_data[index], curr_size - index);
|
||||
traits_type::move(&curr_data[index + count], &curr_data[index], curr_size - index + 1);
|
||||
traits_type::assign(&curr_data[index], count, ch);
|
||||
this->set_size(curr_size + count);
|
||||
term();
|
||||
return &curr_data[index];
|
||||
}
|
||||
|
||||
@ -4994,9 +5028,7 @@ insert(
|
||||
const auto curr_size = size();
|
||||
const auto curr_data = data();
|
||||
const auto count = read_back(first, last);
|
||||
const auto index = pos - curr_data;
|
||||
BOOST_STATIC_STRING_THROW_IF(
|
||||
index > curr_size, std::out_of_range{"index > size()"});
|
||||
const std::size_t index = pos - curr_data;
|
||||
std::rotate(&curr_data[index], &curr_data[curr_size + 1], &curr_data[curr_size + count + 1]);
|
||||
this->set_size(curr_size + count);
|
||||
return curr_data + index;
|
||||
@ -5015,9 +5047,9 @@ erase(
|
||||
const auto curr_data = data();
|
||||
BOOST_STATIC_STRING_THROW_IF(
|
||||
index > curr_size, std::out_of_range{"index > size()"});
|
||||
const auto n = (std::min)(count, curr_size - index);
|
||||
traits_type::move(&curr_data[index], &curr_data[index + n], curr_size - (index + n) + 1);
|
||||
this->set_size(curr_size - n);
|
||||
count = (std::min)(count, curr_size - index);
|
||||
traits_type::move(&curr_data[index], &curr_data[index + count], curr_size - (index + count) + 1);
|
||||
this->set_size(curr_size - count);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -5025,8 +5057,7 @@ template<std::size_t N, typename CharT, typename Traits>
|
||||
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
||||
auto
|
||||
basic_static_string<N, CharT, Traits>::
|
||||
erase(
|
||||
const_iterator pos) ->
|
||||
erase(const_iterator pos) ->
|
||||
iterator
|
||||
{
|
||||
erase(pos - begin(), 1);
|
||||
@ -5184,39 +5215,23 @@ BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
||||
auto
|
||||
basic_static_string<N, CharT, Traits>::
|
||||
replace(
|
||||
size_type pos,
|
||||
size_type n1,
|
||||
const_pointer s,
|
||||
size_type n2) ->
|
||||
basic_static_string<N, CharT, Traits>&
|
||||
{
|
||||
BOOST_STATIC_STRING_THROW_IF(
|
||||
pos > size(), std::out_of_range{"pos > size()"});
|
||||
return replace(data() + pos, data() + pos + n1, s, s + n2);
|
||||
}
|
||||
|
||||
template<std::size_t N, typename CharT, typename Traits>
|
||||
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
||||
auto
|
||||
basic_static_string<N, CharT, Traits>::
|
||||
replace(
|
||||
size_type pos,
|
||||
size_type n1,
|
||||
size_type n2,
|
||||
const_iterator i1,
|
||||
const_iterator i2,
|
||||
size_type n,
|
||||
value_type c) ->
|
||||
basic_static_string<N, CharT, Traits>&
|
||||
{
|
||||
const auto curr_size = size();
|
||||
const auto curr_data = data();
|
||||
const std::size_t n1 = i2 - i1;
|
||||
BOOST_STATIC_STRING_THROW_IF(
|
||||
pos > curr_size, std::out_of_range{"pos > size()"});
|
||||
BOOST_STATIC_STRING_THROW_IF(
|
||||
curr_size - (std::min)(n1, curr_size - pos) >= max_size() - n2,
|
||||
n > max_size() ||
|
||||
curr_size - n1 >= max_size() - n,
|
||||
std::length_error{"replaced string exceeds max_size()"});
|
||||
n1 = (std::min)(n1, curr_size - pos);
|
||||
traits_type::move(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1);
|
||||
traits_type::assign(&curr_data[pos], n2, c);
|
||||
this->set_size((curr_size - n1) + n2);
|
||||
const auto pos = i1 - curr_data;
|
||||
traits_type::move(&curr_data[pos + n], i2, (end() - i2) + 1);
|
||||
traits_type::assign(&curr_data[pos], n, c);
|
||||
this->set_size((curr_size - n1) + n);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -5236,14 +5251,14 @@ replace(
|
||||
{
|
||||
const auto curr_size = size();
|
||||
const auto curr_data = data();
|
||||
std::size_t n1 = detail::distance(i1, i2);
|
||||
const auto first_addr = &*j1;
|
||||
const std::size_t n1 = i2 - i1;
|
||||
const std::size_t n2 = detail::distance(j1, j2);
|
||||
const std::size_t pos = i1 - curr_data;
|
||||
const auto first_addr = &*j1;
|
||||
BOOST_STATIC_STRING_THROW_IF(
|
||||
n2 > max_size() ||
|
||||
curr_size - (std::min)(n1, curr_size - pos) >= max_size() - n2,
|
||||
std::length_error{"replaced string exceeds max_size()"});
|
||||
n1 = (std::min)(n1, curr_size - pos);
|
||||
const bool inside = detail::ptr_in_range(curr_data, curr_data + curr_size, first_addr);
|
||||
// due to short circuit evaluation, the second operand of the logical
|
||||
// AND expression will only be evaluated if s is within the string,
|
||||
@ -5303,19 +5318,18 @@ replace(
|
||||
{
|
||||
const auto curr_size = size();
|
||||
const auto curr_data = data();
|
||||
std::size_t n1 = detail::distance(i1, i2);
|
||||
const std::size_t n1 = detail::distance(i1, i2);
|
||||
const std::size_t n2 = read_back(j1, j2);
|
||||
const std::size_t pos = i1 - curr_data;
|
||||
BOOST_STATIC_STRING_THROW_IF(
|
||||
pos > curr_size, std::out_of_range{"pos > size()"});
|
||||
BOOST_STATIC_STRING_THROW_IF(
|
||||
n2 > max_size() ||
|
||||
curr_size - (std::min)(n1, curr_size - pos) >= max_size() - n2,
|
||||
std::length_error{"replaced string exceeds max_size()"});
|
||||
// Rotate to the correct order. [i2, end] will now start with the replaced string, continue to the existing string not being replaced, and end with a null terminator
|
||||
// Rotate to the correct order. [i2, end] will now start with the replaced string,
|
||||
// continue to the existing string not being replaced, and end with a null terminator
|
||||
std::rotate(&curr_data[pos], &curr_data[curr_size + 1], &curr_data[curr_size + n2 + 1]);
|
||||
// Cap the size
|
||||
n1 = (std::min)(n1, curr_size - pos);
|
||||
// Move everything from the end of the splice point to the end of the rotated string to the begining of the splice point
|
||||
// Move everything from the end of the splice point to the end of the rotated string to
|
||||
// the begining of the splice point
|
||||
traits_type::move(&curr_data[pos + n2], &curr_data[pos + n2 + n1], ((curr_size - n1) + n2) - pos);
|
||||
this->set_size((curr_size - n1) + n2);
|
||||
return *this;
|
||||
@ -5489,22 +5503,21 @@ BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
||||
auto
|
||||
basic_static_string<N, CharT, Traits>::
|
||||
replace_unchecked(
|
||||
size_type pos,
|
||||
size_type n1,
|
||||
const_iterator i1,
|
||||
const_iterator i2,
|
||||
const_pointer s,
|
||||
size_type n2) ->
|
||||
basic_static_string&
|
||||
{
|
||||
const auto curr_data = data();
|
||||
const auto curr_size = size();
|
||||
const std::size_t pos = i1 - curr_data;
|
||||
const std::size_t n1 = i2 - i1;
|
||||
BOOST_STATIC_STRING_THROW_IF(
|
||||
pos > curr_size, std::out_of_range{"pos > size()"});
|
||||
BOOST_STATIC_STRING_THROW_IF(
|
||||
n2 > max_size() ||
|
||||
curr_size - (std::min)(n1, curr_size - pos) >= max_size() - n2,
|
||||
std::length_error{"replaced string exceeds max_size()"});
|
||||
if (pos + n1 >= curr_size)
|
||||
n1 = curr_size - pos;
|
||||
traits_type::move(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1);
|
||||
traits_type::move(&curr_data[pos + n2], i2, (end() - i2) + 1);
|
||||
traits_type::copy(&curr_data[pos], s, n2);
|
||||
this->set_size((curr_size - n1) + n2);
|
||||
return *this;
|
||||
@ -5515,22 +5528,21 @@ BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
||||
auto
|
||||
basic_static_string<N, CharT, Traits>::
|
||||
insert_unchecked(
|
||||
size_type index,
|
||||
const_iterator pos,
|
||||
const_pointer s,
|
||||
size_type count) ->
|
||||
basic_static_string<N, CharT, Traits>&
|
||||
iterator
|
||||
{
|
||||
const auto curr_data = data();
|
||||
const auto curr_size = size();
|
||||
BOOST_STATIC_STRING_THROW_IF(
|
||||
index > curr_size, std::out_of_range{"index > size()"});
|
||||
BOOST_STATIC_STRING_THROW_IF(
|
||||
count > max_size() - size(),
|
||||
count > max_size() - curr_size,
|
||||
std::length_error{"count > max_size() - size()"});
|
||||
traits_type::move(&curr_data[index + count], &curr_data[index], curr_size - index + 1);
|
||||
const std::size_t index = pos - curr_data;
|
||||
traits_type::move(&curr_data[index + count], pos, (end() - pos) + 1);
|
||||
traits_type::copy(&curr_data[index], s, count);
|
||||
this->set_size(curr_size + count);
|
||||
return *this;
|
||||
return curr_data + index;
|
||||
}
|
||||
} // static_string
|
||||
} // boost
|
||||
|
@ -210,9 +210,12 @@ testR(S s, typename S::size_type pos, typename S::size_type n1, const typename S
|
||||
s.size();
|
||||
if (pos <= old_size)
|
||||
{
|
||||
s.replace(pos, n1, str, n2);
|
||||
s0.replace(s0.begin() + pos, s0.begin() + pos + n1, str, str + n2);
|
||||
return s == expected && s0 == expected;
|
||||
if (pos + n1 > s0.size())
|
||||
// this is a precondition violation for the const_iterator overload
|
||||
return s.replace(pos, n1, str, n2) == expected;
|
||||
else
|
||||
return s.replace(pos, n1, str, n2) == expected &&
|
||||
s0.replace(s0.begin() + pos, s0.begin() + pos + n1, str, str + n2) == expected;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3595,6 +3598,7 @@ void
|
||||
testToStaticString()
|
||||
{
|
||||
BOOST_TEST(to_static_string(0) == "0");
|
||||
BOOST_TEST(to_static_string(0u) == "0");
|
||||
BOOST_TEST(to_static_string(1) == "1");
|
||||
BOOST_TEST(to_static_string(0xffff) == "65535");
|
||||
BOOST_TEST(to_static_string(0x10000) == "65536");
|
||||
@ -5668,6 +5672,8 @@ testFind()
|
||||
BOOST_TEST(testFLN(S("hnbrcplsjfgiktoedmaq"), "qprlsfojamgndekthibc", 21, 20, S::npos));
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
|
||||
// done
|
||||
void
|
||||
testReplace()
|
||||
@ -6635,6 +6641,10 @@ testReplace()
|
||||
BOOST_TEST(testR(S("abcdefghijklmnopqrst"), 20, 1, "12345678901234567890", 10, S("abcdefghijklmnopqrst1234567890")));
|
||||
BOOST_TEST(testR(S("abcdefghijklmnopqrst"), 20, 1, "12345678901234567890", 19, S("abcdefghijklmnopqrst1234567890123456789")));
|
||||
BOOST_TEST(testR(S("abcdefghijklmnopqrst"), 20, 1, "12345678901234567890", 20, S("abcdefghijklmnopqrst12345678901234567890")));
|
||||
|
||||
using T = static_string<10>;
|
||||
BOOST_TEST_THROWS(T("12345").replace(0, 1, 500, 'a'), std::length_error);
|
||||
BOOST_TEST_THROWS(T("12345").replace(0, 1, "aaaaaaaaaaaaaa"), std::length_error);
|
||||
}
|
||||
|
||||
// done
|
||||
@ -6901,6 +6911,13 @@ testStartsEnds()
|
||||
BOOST_TEST(!S("1234567890").ends_with("234"));
|
||||
BOOST_TEST(!S("1234567890").ends_with("12345678900"));
|
||||
BOOST_TEST(S("1234567890").ends_with(string_view("1234567890")));
|
||||
|
||||
BOOST_TEST(!S().starts_with('0'));
|
||||
BOOST_TEST(!S().starts_with("0"));
|
||||
BOOST_TEST(!S().starts_with(string_view("0")));
|
||||
BOOST_TEST(!S().ends_with('0'));
|
||||
BOOST_TEST(!S().ends_with("0"));
|
||||
BOOST_TEST(!S().ends_with(string_view("0")));
|
||||
}
|
||||
|
||||
void
|
||||
|
Reference in New Issue
Block a user