webrtc_m130/webrtc/base/copyonwritebuffer_unittest.cc
jbauch eec21bdae3 Reland Use CopyOnWriteBuffer instead of Buffer to avoid unnecessary copies.
This CL removes copy and assign support from Buffer and changes various
parameters from Buffer to CopyOnWriteBuffer so they can be passed along
and copied without actually copying the underlying data.

With this changed some parameters to be "const" and fixed an issue when
creating a CopyOnWriteBuffer with empty data.

BUG=webrtc:5155

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

Cr-Commit-Position: refs/heads/master@{#12062}
2016-03-20 13:15:48 +00:00

261 lines
7.7 KiB
C++

/*
* Copyright 2016 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/base/copyonwritebuffer.h"
#include "webrtc/base/gunit.h"
namespace rtc {
namespace {
// clang-format off
const uint8_t kTestData[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
// clang-format on
} // namespace
void EnsureBuffersShareData(const CopyOnWriteBuffer& buf1,
const CopyOnWriteBuffer& buf2) {
// Data is shared between buffers.
EXPECT_EQ(buf1.size(), buf2.size());
EXPECT_EQ(buf1.capacity(), buf2.capacity());
const uint8_t* data1 = buf1.data();
const uint8_t* data2 = buf2.data();
EXPECT_EQ(data1, data2);
EXPECT_EQ(buf1, buf2);
}
void EnsureBuffersDontShareData(const CopyOnWriteBuffer& buf1,
const CopyOnWriteBuffer& buf2) {
// Data is not shared between buffers.
const uint8_t* data1 = buf1.cdata();
const uint8_t* data2 = buf2.cdata();
EXPECT_NE(data1, data2);
}
TEST(CopyOnWriteBufferTest, TestCreateEmptyData) {
CopyOnWriteBuffer buf(static_cast<const uint8_t*>(nullptr), 0);
EXPECT_EQ(buf.size(), 0u);
EXPECT_EQ(buf.capacity(), 0u);
EXPECT_EQ(buf.data(), nullptr);
}
TEST(CopyOnWriteBufferTest, TestMoveConstruct) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
size_t buf1_size = buf1.size();
size_t buf1_capacity = buf1.capacity();
const uint8_t* buf1_data = buf1.cdata();
CopyOnWriteBuffer buf2(std::move(buf1));
EXPECT_EQ(buf1.size(), 0u);
EXPECT_EQ(buf1.capacity(), 0u);
EXPECT_EQ(buf1.data(), nullptr);
EXPECT_EQ(buf2.size(), buf1_size);
EXPECT_EQ(buf2.capacity(), buf1_capacity);
EXPECT_EQ(buf2.data(), buf1_data);
}
TEST(CopyOnWriteBufferTest, TestMoveAssign) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
size_t buf1_size = buf1.size();
size_t buf1_capacity = buf1.capacity();
const uint8_t* buf1_data = buf1.cdata();
CopyOnWriteBuffer buf2;
buf2 = std::move(buf1);
EXPECT_EQ(buf1.size(), 0u);
EXPECT_EQ(buf1.capacity(), 0u);
EXPECT_EQ(buf1.data(), nullptr);
EXPECT_EQ(buf2.size(), buf1_size);
EXPECT_EQ(buf2.capacity(), buf1_capacity);
EXPECT_EQ(buf2.data(), buf1_data);
}
TEST(CopyOnWriteBufferTest, TestSwap) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
size_t buf1_size = buf1.size();
size_t buf1_capacity = buf1.capacity();
const uint8_t* buf1_data = buf1.cdata();
CopyOnWriteBuffer buf2(kTestData, 6, 20);
size_t buf2_size = buf2.size();
size_t buf2_capacity = buf2.capacity();
const uint8_t* buf2_data = buf2.cdata();
std::swap(buf1, buf2);
EXPECT_EQ(buf1.size(), buf2_size);
EXPECT_EQ(buf1.capacity(), buf2_capacity);
EXPECT_EQ(buf1.data(), buf2_data);
EXPECT_EQ(buf2.size(), buf1_size);
EXPECT_EQ(buf2.capacity(), buf1_capacity);
EXPECT_EQ(buf2.data(), buf1_data);
}
TEST(CopyOnWriteBufferTest, TestAppendData) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
EnsureBuffersShareData(buf1, buf2);
// AppendData copies the underlying buffer.
buf2.AppendData("foo");
EXPECT_EQ(buf2.size(), buf1.size() + 4); // "foo" + trailing 0x00
EXPECT_EQ(buf2.capacity(), buf1.capacity());
EXPECT_NE(buf2.data(), buf1.data());
EXPECT_EQ(buf1, CopyOnWriteBuffer(kTestData, 3));
const int8_t exp[] = {0x0, 0x1, 0x2, 'f', 'o', 'o', 0x0};
EXPECT_EQ(buf2, CopyOnWriteBuffer(exp));
}
TEST(CopyOnWriteBufferTest, TestSetData) {
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, TestEnsureCapacity) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
// Smaller than existing capacity -> no change and still same contents.
buf2.EnsureCapacity(8);
EnsureBuffersShareData(buf1, buf2);
EXPECT_EQ(buf1.size(), 3u);
EXPECT_EQ(buf1.capacity(), 10u);
EXPECT_EQ(buf2.size(), 3u);
EXPECT_EQ(buf2.capacity(), 10u);
// Lager than existing capacity -> data is cloned.
buf2.EnsureCapacity(16);
EnsureBuffersDontShareData(buf1, buf2);
EXPECT_EQ(buf1.size(), 3u);
EXPECT_EQ(buf1.capacity(), 10u);
EXPECT_EQ(buf2.size(), 3u);
EXPECT_EQ(buf2.capacity(), 16u);
// The size and contents are still the same.
EXPECT_EQ(buf1, buf2);
}
TEST(CopyOnWriteBufferTest, TestSetSize) {
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) {
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);
}
TEST(CopyOnWriteBufferTest, TestConstDataAccessor) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
// .cdata() doesn't clone data.
const uint8_t* cdata1 = buf1.cdata();
const uint8_t* cdata2 = buf2.cdata();
EXPECT_EQ(cdata1, cdata2);
// Non-const .data() clones data if shared.
const uint8_t* data1 = buf1.data();
const uint8_t* data2 = buf2.data();
EXPECT_NE(data1, data2);
// buf1 was cloned above.
EXPECT_NE(data1, cdata1);
// Therefore buf2 was no longer sharing data and was not cloned.
EXPECT_EQ(data2, cdata1);
}
TEST(CopyOnWriteBufferTest, TestBacketRead) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
EnsureBuffersShareData(buf1, buf2);
// Non-const reads clone the data if shared.
for (size_t i = 0; i != 3u; ++i) {
EXPECT_EQ(buf1[i], kTestData[i]);
}
EnsureBuffersDontShareData(buf1, buf2);
}
TEST(CopyOnWriteBufferTest, TestBacketReadConst) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
EnsureBuffersShareData(buf1, buf2);
const CopyOnWriteBuffer& cbuf1 = buf1;
for (size_t i = 0; i != 3u; ++i) {
EXPECT_EQ(cbuf1[i], kTestData[i]);
}
EnsureBuffersShareData(buf1, buf2);
}
TEST(CopyOnWriteBufferTest, TestBacketWrite) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
EnsureBuffersShareData(buf1, buf2);
for (size_t i = 0; i != 3u; ++i) {
buf1[i] = kTestData[i] + 1;
}
EXPECT_EQ(buf1.size(), 3u);
EXPECT_EQ(buf1.capacity(), 10u);
EXPECT_EQ(buf2.size(), 3u);
EXPECT_EQ(buf2.capacity(), 10u);
EXPECT_EQ(0, memcmp(buf2.cdata(), kTestData, 3));
}
} // namespace rtc