Make CopyOnWriteBuffer keep capacity

for SetData and Clear functions too.

This way result of all functions is same
for shared and non-shared buffer cases

R=kwiberg@webrtc.org

Review URL: https://codereview.webrtc.org/2328553002 .

Cr-Commit-Position: refs/heads/master@{#14196}
This commit is contained in:
Danil Chapovalov 2016-09-13 14:15:10 +02:00
parent 9708884c31
commit 2b2779f3f1
2 changed files with 107 additions and 44 deletions

View File

@ -153,9 +153,10 @@ class CopyOnWriteBuffer {
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
void SetData(const T* data, size_t size) {
RTC_DCHECK(IsConsistent());
if (!buffer_ || !buffer_->HasOneRef()) {
buffer_ = size > 0 ? new RefCountedObject<Buffer>(data, size)
: nullptr;
if (!buffer_) {
buffer_ = size > 0 ? new RefCountedObject<Buffer>(data, size) : nullptr;
} else if (!buffer_->HasOneRef()) {
buffer_ = new RefCountedObject<Buffer>(data, size, buffer_->capacity());
} else {
buffer_->SetData(data, size);
}
@ -253,13 +254,16 @@ class CopyOnWriteBuffer {
RTC_DCHECK(IsConsistent());
}
// Resets the buffer to zero size and capacity.
// Resets the buffer to zero size without altering capacity. Works even if the
// buffer has been moved from.
void Clear() {
RTC_DCHECK(IsConsistent());
if (!buffer_ || !buffer_->HasOneRef()) {
buffer_ = nullptr;
} else {
if (!buffer_)
return;
if (buffer_->HasOneRef()) {
buffer_->Clear();
} else {
buffer_ = new RefCountedObject<Buffer>(0, buffer_->capacity());
}
RTC_DCHECK(IsConsistent());
}

View File

@ -116,37 +116,57 @@ TEST(CopyOnWriteBufferTest, TestAppendData) {
EXPECT_EQ(buf2, CopyOnWriteBuffer(exp));
}
TEST(CopyOnWriteBufferTest, TestSetData) {
TEST(CopyOnWriteBufferTest, SetEmptyData) {
CopyOnWriteBuffer buf(10);
buf.SetData<uint8_t>(nullptr, 0);
EXPECT_EQ(0u, buf.size());
}
TEST(CopyOnWriteBufferTest, SetDataNoMoreThanCapacityDoesntCauseReallocation) {
CopyOnWriteBuffer buf1(3, 10);
const uint8_t* const original_allocation = buf1.cdata();
buf1.SetData(kTestData, 10);
EXPECT_EQ(original_allocation, buf1.cdata());
EXPECT_EQ(buf1, CopyOnWriteBuffer(kTestData, 10));
}
TEST(CopyOnWriteBufferTest, SetDataMakeReferenceCopy) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2;
buf2.SetData(buf1);
// buf2 shares the same data as buf1 now.
EnsureBuffersShareData(buf1, buf2);
CopyOnWriteBuffer buf3(buf1);
// buf3 is re-allocated with new data, existing buffers are not modified.
buf3.SetData("foo");
EXPECT_EQ(buf1, CopyOnWriteBuffer(kTestData, 3));
EnsureBuffersShareData(buf1, buf2);
EnsureBuffersDontShareData(buf1, buf3);
const int8_t exp[] = {'f', 'o', 'o', 0x0};
EXPECT_EQ(buf3, CopyOnWriteBuffer(exp));
buf2.SetData(static_cast<const uint8_t*>(nullptr), 0u);
EnsureBuffersDontShareData(buf1, buf2);
EXPECT_EQ(buf1.size(), 3u);
EXPECT_EQ(buf1.capacity(), 10u);
EXPECT_EQ(buf2.size(), 0u);
EXPECT_EQ(buf2.capacity(), 0u);
}
TEST(CopyOnWriteBufferTest, TestSetDataEmpty) {
CopyOnWriteBuffer buf;
buf.SetData(static_cast<const uint8_t*>(nullptr), 0u);
EXPECT_EQ(buf.size(), 0u);
EXPECT_EQ(buf.capacity(), 0u);
EXPECT_EQ(buf.data(), nullptr);
TEST(CopyOnWriteBufferTest, SetDataOnSharedKeepsOriginal) {
const uint8_t data[] = "foo";
CopyOnWriteBuffer buf1(kTestData, 3, 10);
const uint8_t* const original_allocation = buf1.cdata();
CopyOnWriteBuffer buf2(buf1);
buf2.SetData(data);
EnsureBuffersDontShareData(buf1, buf2);
EXPECT_EQ(original_allocation, buf1.cdata());
EXPECT_EQ(buf1, CopyOnWriteBuffer(kTestData, 3));
EXPECT_EQ(buf2, CopyOnWriteBuffer(data));
}
TEST(CopyOnWriteBufferTest, SetDataOnSharedKeepsCapacity) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
EnsureBuffersShareData(buf1, buf2);
buf2.SetData(kTestData, 2);
EnsureBuffersDontShareData(buf1, buf2);
EXPECT_EQ(2u, buf2.size());
EXPECT_EQ(10u, buf2.capacity());
}
TEST(CopyOnWriteBufferTest, TestEnsureCapacity) {
@ -172,31 +192,70 @@ TEST(CopyOnWriteBufferTest, TestEnsureCapacity) {
EXPECT_EQ(buf1, buf2);
}
TEST(CopyOnWriteBufferTest, TestSetSize) {
TEST(CopyOnWriteBufferTest, SetSizeDoesntChangeOriginal) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
const uint8_t* const original_allocation = buf1.cdata();
CopyOnWriteBuffer buf2(buf1);
buf2.SetSize(16);
EnsureBuffersDontShareData(buf1, buf2);
EXPECT_EQ(original_allocation, buf1.cdata());
EXPECT_EQ(3u, buf1.size());
EXPECT_EQ(10u, buf1.capacity());
}
TEST(CopyOnWriteBufferTest, SetSizeCloneContent) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
buf2.SetSize(16);
EnsureBuffersDontShareData(buf1, buf2);
EXPECT_EQ(buf1.size(), 3u);
EXPECT_EQ(buf1.capacity(), 10u);
EXPECT_EQ(buf2.size(), 16u);
EXPECT_EQ(buf2.capacity(), 16u);
// The contents got cloned.
EXPECT_EQ(0, memcmp(buf2.data(), kTestData, 3));
}
TEST(CopyOnWriteBufferTest, TestClear) {
TEST(CopyOnWriteBufferTest, SetSizeMayIncreaseCapacity) {
CopyOnWriteBuffer buf(kTestData, 3, 10);
buf.SetSize(16);
EXPECT_EQ(16u, buf.size());
EXPECT_EQ(16u, buf.capacity());
}
TEST(CopyOnWriteBufferTest, SetSizeDoesntDecreaseCapacity) {
CopyOnWriteBuffer buf1(kTestData, 5, 10);
CopyOnWriteBuffer buf2(buf1);
buf2.SetSize(2);
EXPECT_EQ(2u, buf2.size());
EXPECT_EQ(10u, buf2.capacity());
}
TEST(CopyOnWriteBufferTest, ClearDoesntChangeOriginal) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
const uint8_t* const original_allocation = buf1.cdata();
CopyOnWriteBuffer buf2(buf1);
buf2.Clear();
EnsureBuffersDontShareData(buf1, buf2);
EXPECT_EQ(3u, buf1.size());
EXPECT_EQ(10u, buf1.capacity());
EXPECT_EQ(original_allocation, buf1.cdata());
EXPECT_EQ(0u, buf2.size());
}
TEST(CopyOnWriteBufferTest, ClearDoesntChangeCapacity) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
buf2.Clear();
EnsureBuffersDontShareData(buf1, buf2);
EXPECT_EQ(buf1.size(), 3u);
EXPECT_EQ(buf1.capacity(), 10u);
EXPECT_EQ(0, memcmp(buf1.data(), kTestData, 3));
EXPECT_EQ(buf2.size(), 0u);
EXPECT_EQ(buf2.capacity(), 0u);
EXPECT_EQ(0u, buf2.size());
EXPECT_EQ(10u, buf2.capacity());
}
TEST(CopyOnWriteBufferTest, TestConstDataAccessor) {