Commit 4ea9f181 authored by grunell@chromium.org's avatar grunell@chromium.org

Adding partially circular memory buffer wrapper.

Will be used by WebRTC logging.


BUG=229829

Review URL: https://chromiumcodereview.appspot.com/13473005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@195137 0039d316-1c4b-4281-b951-d872f2087c98
parent d19f5f10
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/common/partial_circular_buffer.h"
#include <algorithm>
#include "base/logging.h"
namespace content {
namespace {
inline uint32 Min3(uint32 a, uint32 b, uint32 c) {
return std::min(a, std::min(b, c));
}
} // namespace
PartialCircularBuffer::PartialCircularBuffer(void* buffer,
uint32 buffer_size)
: buffer_data_(reinterpret_cast<BufferData*>(buffer)),
memory_buffer_size_(buffer_size),
data_size_(0),
position_(0),
total_read_(0) {
uint32 header_size =
buffer_data_->data - reinterpret_cast<uint8*>(buffer_data_);
data_size_ = memory_buffer_size_ - header_size;
DCHECK(buffer_data_);
DCHECK_GE(memory_buffer_size_, header_size);
DCHECK_LE(buffer_data_->total_written, data_size_);
DCHECK_LT(buffer_data_->wrap_position, data_size_);
DCHECK_LT(buffer_data_->end_position, data_size_);
}
PartialCircularBuffer::PartialCircularBuffer(void* buffer,
uint32 buffer_size,
uint32 wrap_position)
: buffer_data_(reinterpret_cast<BufferData*>(buffer)),
memory_buffer_size_(buffer_size),
data_size_(0),
position_(0),
total_read_(0) {
uint32 header_size =
buffer_data_->data - reinterpret_cast<uint8*>(buffer_data_);
data_size_ = memory_buffer_size_ - header_size;
DCHECK(buffer_data_);
DCHECK_GE(memory_buffer_size_, header_size);
DCHECK_LT(wrap_position, data_size_);
buffer_data_->total_written = 0;
buffer_data_->wrap_position = wrap_position;
buffer_data_->end_position = 0;
}
uint32 PartialCircularBuffer::Read(void* buffer, uint32 buffer_size) {
DCHECK(buffer_data_);
if (total_read_ >= buffer_data_->total_written)
return 0;
uint8* buffer_uint8 = reinterpret_cast<uint8*>(buffer);
uint32 read = 0;
// Read from beginning part.
if (position_ < buffer_data_->wrap_position) {
uint32 to_wrap_pos = buffer_data_->wrap_position - position_;
uint32 to_eow = buffer_data_->total_written - total_read_;
uint32 to_read = Min3(buffer_size, to_wrap_pos, to_eow);
memcpy(buffer_uint8, buffer_data_->data + position_, to_read);
position_ += to_read;
total_read_ += to_read;
read += to_read;
if (position_ == buffer_data_->wrap_position &&
buffer_data_->total_written == data_size_) {
// We've read all the beginning part, set the position to the middle part.
// (The second condition above checks if the wrapping part is filled, i.e.
// writing has wrapped.)
position_ = buffer_data_->end_position;
}
if (read >= buffer_size) {
DCHECK_EQ(read, buffer_size);
return read;
}
if (read >= to_eow) {
DCHECK_EQ(read, to_eow);
DCHECK_EQ(total_read_, buffer_data_->total_written);
return read;
}
}
// Read from middle part.
DCHECK_GE(position_, buffer_data_->wrap_position);
if (position_ >= buffer_data_->end_position) {
uint32 remaining_buffer_size = buffer_size - read;
uint32 to_eof = data_size_ - position_;
uint32 to_eow = buffer_data_->total_written - total_read_;
uint32 to_read = Min3(remaining_buffer_size, to_eof, to_eow);
memcpy(buffer_uint8 + read, buffer_data_->data + position_, to_read);
position_ += to_read;
total_read_ += to_read;
read += to_read;
if (position_ == data_size_) {
// We've read all the middle part, set position to the end part.
position_ = buffer_data_->wrap_position;
}
if (read >= buffer_size) {
DCHECK_EQ(read, buffer_size);
return read;
}
if (total_read_ >= buffer_data_->total_written) {
DCHECK_EQ(total_read_, buffer_data_->total_written);
return read;
}
}
// Read from end part.
DCHECK_GE(position_, buffer_data_->wrap_position);
DCHECK_LT(position_, buffer_data_->end_position);
uint32 remaining_buffer_size = buffer_size - read;
uint32 to_eob = buffer_data_->end_position - position_;
uint32 to_eow = buffer_data_->total_written - total_read_;
uint32 to_read = Min3(remaining_buffer_size, to_eob, to_eow);
memcpy(buffer_uint8 + read, buffer_data_->data + position_, to_read);
position_ += to_read;
total_read_ += to_read;
read += to_read;
DCHECK_LE(read, buffer_size);
DCHECK_LE(total_read_, buffer_data_->total_written);
return read;
}
void PartialCircularBuffer::Write(const void* buffer, uint32 buffer_size) {
DCHECK(buffer_data_);
uint32 position_before_write = position_;
uint32 to_eof = data_size_ - position_;
uint32 to_write = std::min(buffer_size, to_eof);
DoWrite(buffer_data_->data + position_, buffer, to_write);
if (position_ >= data_size_) {
DCHECK_EQ(position_, data_size_);
position_ = buffer_data_->wrap_position;
}
if (to_write < buffer_size) {
uint32 remainder_to_write = buffer_size - to_write;
DCHECK_LT(position_, position_before_write);
DCHECK_LE(position_ + remainder_to_write, position_before_write);
DoWrite(buffer_data_->data + position_,
reinterpret_cast<const uint8*>(buffer) + to_write,
remainder_to_write);
}
}
void PartialCircularBuffer::DoWrite(void* dest, const void* src, uint32 num) {
memcpy(dest, src, num);
position_ += num;
buffer_data_->total_written =
std::min(buffer_data_->total_written + num, data_size_);
buffer_data_->end_position = position_;
}
} // namespace content
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_COMMON_PARTIAL_CIRCULAR_BUFFER_H_
#define CONTENT_COMMON_PARTIAL_CIRCULAR_BUFFER_H_
#include "base/basictypes.h"
#include "base/gtest_prod_util.h"
#include "content/common/content_export.h"
namespace content {
// A wrapper around a memory buffer that allows circular read and write with a
// selectable wrapping position. Buffer layout (after wrap; H is header):
// -----------------------------------------------------------
// | H | Beginning | End | Middle |
// -----------------------------------------------------------
// ^---- Non-wrapping -----^ ^--------- Wrapping ----------^
// The non-wrapping part is never overwritten. The wrapping part will be
// circular. The very first part is the header (see the BufferData struct
// below). It consists of the following information:
// - Length written to the buffer (not including header).
// - Wrapping position.
// - End position of buffer. (If the last byte is at x, this will be x + 1.)
// Users of wrappers around the same underlying buffer must ensure that writing
// is finished before reading is started.
class CONTENT_EXPORT PartialCircularBuffer {
public:
// Use for reading. |buffer_size| is in bytes and must be larger than the
// header size (see above).
PartialCircularBuffer(void* buffer, uint32 buffer_size);
// Use for writing. |buffer_size| is in bytes and must be larger than the
// header size (see above).
PartialCircularBuffer(void* buffer,
uint32 buffer_size,
uint32 wrap_position);
uint32 Read(void* buffer, uint32 buffer_size);
void Write(const void* buffer, uint32 buffer_size);
private:
friend class PartialCircularBufferTest;
#pragma pack(push)
#pragma pack(4)
struct BufferData {
uint32 total_written;
uint32 wrap_position;
uint32 end_position;
uint8 data[1];
};
#pragma pack(pop)
void DoWrite(void* dest, const void* src, uint32 num);
// Used for reading and writing.
BufferData* buffer_data_;
uint32 memory_buffer_size_;
uint32 data_size_;
uint32 position_;
// Used for reading.
uint32 total_read_;
};
} // namespace content
#endif // CONTENT_COMMON_PARTIAL_CIRCULAR_BUFFER_H_
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// The test buffer data is 52 bytes, wrap position is set to 20 (this is
// arbitrarily chosen). The total buffer size is allocated dynamically based on
// the actual header size. This gives:
// Header of some size, non-wrapping part 20 bytes, wrapping part 32 bytes.
// As input data, a 14 byte array is used and repeatedly written. It's chosen
// not to be an integer factor smaller than the wrapping part. This ensures that
// the wrapped data isn't repeated at the same position.
// Note that desipte the number of wraps (if one or more), the reference output
// data is the same since the offset at each wrap is always the same.
#include "base/memory/scoped_ptr.h"
#include "content/common/partial_circular_buffer.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
const uint32 kWrapPosition = 20;
const uint8 kInputData[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
const uint8 kOutputRefDataWrap[] =
// The 20 bytes in the non-wrapping part.
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 1, 2, 3, 4, 5, 6,
// The 32 bytes in wrapping part.
11, 12, 13, 14,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
class PartialCircularBufferTest : public testing::Test {
public:
PartialCircularBufferTest() {
PartialCircularBuffer::BufferData test_struct;
buffer_header_size_ =
&test_struct.data[0] - reinterpret_cast<uint8*>(&test_struct);
buffer_.reset(new uint8[buffer_header_size_ + sizeof(kOutputRefDataWrap)]);
pcb_write_.reset(new PartialCircularBuffer(
buffer_.get(),
buffer_header_size_ + sizeof(kOutputRefDataWrap),
kWrapPosition));
}
void WriteToBuffer(int num) {
for (int i = 0; i < num; ++i)
pcb_write_->Write(kInputData, sizeof(kInputData));
}
void InitReadBuffer() {
pcb_read_.reset(new PartialCircularBuffer(
buffer_.get(), buffer_header_size_ + sizeof(kOutputRefDataWrap)));
}
protected:
scoped_ptr<PartialCircularBuffer> pcb_write_;
scoped_ptr<PartialCircularBuffer> pcb_read_;
scoped_ptr<uint8[]> buffer_;
uint32 buffer_header_size_;
DISALLOW_COPY_AND_ASSIGN(PartialCircularBufferTest);
};
TEST_F(PartialCircularBufferTest, NoWrapBeginningPartOnly) {
WriteToBuffer(1);
InitReadBuffer();
uint8 output_data[sizeof(kInputData)] = {0};
EXPECT_EQ(sizeof(output_data),
pcb_read_->Read(output_data, sizeof(output_data)));
EXPECT_EQ(0, memcmp(kInputData, output_data, sizeof(kInputData)));
EXPECT_EQ(0u, pcb_read_->Read(output_data, sizeof(output_data)));
}
TEST_F(PartialCircularBufferTest, NoWrapBeginningAndEndParts) {
WriteToBuffer(2);
InitReadBuffer();
uint8 output_data[2 * sizeof(kInputData)] = {0};
EXPECT_EQ(sizeof(output_data),
pcb_read_->Read(output_data, sizeof(output_data)));
const uint8 output_ref_data[2 * sizeof(kInputData)] =
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
EXPECT_EQ(0, memcmp(output_ref_data, output_data, sizeof(output_data)));
EXPECT_EQ(0u, pcb_read_->Read(output_data, sizeof(output_data)));
}
TEST_F(PartialCircularBufferTest, WrapOnce) {
WriteToBuffer(4);
InitReadBuffer();
uint8 output_data[sizeof(kOutputRefDataWrap)] = {0};
EXPECT_EQ(sizeof(output_data),
pcb_read_->Read(output_data, sizeof(output_data)));
EXPECT_EQ(0, memcmp(kOutputRefDataWrap, output_data, sizeof(output_data)));
EXPECT_EQ(0u, pcb_read_->Read(output_data, sizeof(output_data)));
}
TEST_F(PartialCircularBufferTest, WrapTwice) {
WriteToBuffer(7);
InitReadBuffer();
uint8 output_data[sizeof(kOutputRefDataWrap)] = {0};
EXPECT_EQ(sizeof(output_data),
pcb_read_->Read(output_data, sizeof(output_data)));
EXPECT_EQ(0, memcmp(kOutputRefDataWrap, output_data, sizeof(output_data)));
EXPECT_EQ(0u, pcb_read_->Read(output_data, sizeof(output_data)));
}
TEST_F(PartialCircularBufferTest, WrapOnceSmallerOutputBuffer) {
WriteToBuffer(4);
InitReadBuffer();
uint8 output_data[sizeof(kOutputRefDataWrap)] = {0};
const uint32 size_per_read = 16;
uint32 read = 0;
for (; read + size_per_read <= sizeof(output_data); read += size_per_read) {
EXPECT_EQ(size_per_read,
pcb_read_->Read(output_data + read, size_per_read));
}
EXPECT_EQ(sizeof(output_data) - read,
pcb_read_->Read(output_data + read, size_per_read));
EXPECT_EQ(0, memcmp(kOutputRefDataWrap, output_data, sizeof(output_data)));
EXPECT_EQ(0u, pcb_read_->Read(output_data, sizeof(output_data)));
}
} // namespace content
...@@ -322,6 +322,8 @@ ...@@ -322,6 +322,8 @@
'common/p2p_messages.h', 'common/p2p_messages.h',
'common/p2p_sockets.h', 'common/p2p_sockets.h',
'common/page_zoom.cc', 'common/page_zoom.cc',
'common/partial_circular_buffer.cc',
'common/partial_circular_buffer.h',
'common/pepper_messages.h', 'common/pepper_messages.h',
'common/pepper_plugin_registry.cc', 'common/pepper_plugin_registry.cc',
'common/pepper_plugin_registry.h', 'common/pepper_plugin_registry.h',
......
...@@ -377,6 +377,7 @@ ...@@ -377,6 +377,7 @@
'common/indexed_db/proxy_webidbcursor_impl_unittest.cc', 'common/indexed_db/proxy_webidbcursor_impl_unittest.cc',
'common/inter_process_time_ticks_converter_unittest.cc', 'common/inter_process_time_ticks_converter_unittest.cc',
'common/page_zoom_unittest.cc', 'common/page_zoom_unittest.cc',
'common/partial_circular_buffer_unittest.cc',
'common/resource_dispatcher_unittest.cc', 'common/resource_dispatcher_unittest.cc',
'common/sandbox_mac_diraccess_unittest.mm', 'common/sandbox_mac_diraccess_unittest.mm',
'common/sandbox_mac_fontloading_unittest.mm', 'common/sandbox_mac_fontloading_unittest.mm',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment