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}
261 lines
7.7 KiB
C++
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
|