rtc::Buffer: Grow capacity by at least 1.5x to prevent quadratic behavior

BUG=webrtc:6009

Review-Url: https://codereview.webrtc.org/2078873005
Cr-Commit-Position: refs/heads/master@{#13214}
This commit is contained in:
kwiberg 2016-06-20 04:47:39 -07:00 committed by Commit bot
parent 504f3351d2
commit c853597598
2 changed files with 28 additions and 12 deletions

View File

@ -219,7 +219,7 @@ class BufferT {
void AppendData(const U* data, size_t size) {
RTC_DCHECK(IsConsistent());
const size_t new_size = size_ + size;
EnsureCapacity(new_size);
EnsureCapacityWithHeadroom(new_size, true);
static_assert(sizeof(T) == sizeof(U), "");
std::memcpy(data_.get() + size_, data, size * sizeof(U));
size_ = new_size;
@ -272,7 +272,7 @@ class BufferT {
// the existing contents will be kept and the new space will be
// uninitialized.
void SetSize(size_t size) {
EnsureCapacity(size);
EnsureCapacityWithHeadroom(size, true);
size_ = size;
}
@ -280,14 +280,9 @@ class BufferT {
// further reallocation. (Of course, this operation might need to reallocate
// the buffer.)
void EnsureCapacity(size_t capacity) {
RTC_DCHECK(IsConsistent());
if (capacity <= capacity_)
return;
std::unique_ptr<T[]> new_data(new T[capacity]);
std::memcpy(new_data.get(), data_.get(), size_ * sizeof(T));
data_ = std::move(new_data);
capacity_ = capacity;
RTC_DCHECK(IsConsistent());
// Don't allocate extra headroom, since the user is asking for a specific
// capacity.
EnsureCapacityWithHeadroom(capacity, false);
}
// Resets the buffer to zero size without altering capacity. Works even if the
@ -306,6 +301,27 @@ class BufferT {
}
private:
void EnsureCapacityWithHeadroom(size_t capacity, bool extra_headroom) {
RTC_DCHECK(IsConsistent());
if (capacity <= capacity_)
return;
// If the caller asks for extra headroom, ensure that the new capacity is
// >= 1.5 times the old capacity. Any constant > 1 is sufficient to prevent
// quadratic behavior; as to why we pick 1.5 in particular, see
// https://github.com/facebook/folly/blob/master/folly/docs/FBVector.md and
// http://www.gahcep.com/cpp-internals-stl-vector-part-1/.
const size_t new_capacity =
extra_headroom ? std::max(capacity, capacity_ + capacity_ / 2)
: capacity;
std::unique_ptr<T[]> new_data(new T[new_capacity]);
std::memcpy(new_data.get(), data_.get(), size_ * sizeof(T));
data_ = std::move(new_data);
capacity_ = new_capacity;
RTC_DCHECK(IsConsistent());
}
// Precondition for all methods except Clear and the destructor.
// Postcondition for all methods except move construction and move
// assignment, which leave the moved-from object in a possibly inconsistent

View File

@ -68,7 +68,7 @@ TEST(BufferTest, TestSetData) {
Buffer buf(kTestData + 4, 7);
buf.SetData(kTestData, 9);
EXPECT_EQ(buf.size(), 9u);
EXPECT_EQ(buf.capacity(), 9u);
EXPECT_EQ(buf.capacity(), 7u * 3 / 2);
EXPECT_EQ(0, memcmp(buf.data(), kTestData, 9));
}
@ -95,7 +95,7 @@ TEST(BufferTest, TestSetSizeLarger) {
EXPECT_EQ(buf.capacity(), 15u);
buf.SetSize(20);
EXPECT_EQ(buf.size(), 20u);
EXPECT_EQ(buf.capacity(), 20u); // Has grown.
EXPECT_EQ(buf.capacity(), 15u * 3 / 2); // Has grown.
EXPECT_EQ(0, memcmp(buf.data(), kTestData, 15));
}