Commit 68c381e3 authored by Steve Anton's avatar Steve Anton Committed by Commit Bot

Add a ByteBufferQueue data structure

A ByteBufferQueue is internally a queue of byte buffers. Clients
can append entire byte buffers then copy data out across buffer
boundaries.

Bug: 874296
Change-Id: I64e8a7e9f080aa73e818a81c1a42241ac9ac4e26
Reviewed-on: https://chromium-review.googlesource.com/c/1289152
Commit-Queue: Steve Anton <steveanton@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarHenrik Boström <hbos@chromium.org>
Cr-Commit-Position: refs/heads/master@{#607627}
parent 539ef7dd
...@@ -313,6 +313,7 @@ jumbo_source_set("unit_tests") { ...@@ -313,6 +313,7 @@ jumbo_source_set("unit_tests") {
"payments/payments_validators_test.cc", "payments/payments_validators_test.cc",
"peerconnection/adapters/p2p_quic_stream_unittest.cc", "peerconnection/adapters/p2p_quic_stream_unittest.cc",
"peerconnection/adapters/p2p_quic_transport_test.cc", "peerconnection/adapters/p2p_quic_transport_test.cc",
"peerconnection/byte_buffer_queue_test.cc",
"peerconnection/rtc_data_channel_test.cc", "peerconnection/rtc_data_channel_test.cc",
"peerconnection/rtc_ice_transport_test.cc", "peerconnection/rtc_ice_transport_test.cc",
"peerconnection/rtc_ice_transport_test.h", "peerconnection/rtc_ice_transport_test.h",
......
...@@ -34,6 +34,8 @@ blink_modules_sources("peerconnection") { ...@@ -34,6 +34,8 @@ blink_modules_sources("peerconnection") {
"adapters/quic_transport_proxy.h", "adapters/quic_transport_proxy.h",
"adapters/web_rtc_cross_thread_copier.cc", "adapters/web_rtc_cross_thread_copier.cc",
"adapters/web_rtc_cross_thread_copier.h", "adapters/web_rtc_cross_thread_copier.h",
"byte_buffer_queue.cc",
"byte_buffer_queue.h",
"rtc_certificate.cc", "rtc_certificate.cc",
"rtc_certificate.h", "rtc_certificate.h",
"rtc_data_channel.cc", "rtc_data_channel.cc",
......
// Copyright 2018 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 "third_party/blink/renderer/modules/peerconnection/byte_buffer_queue.h"
namespace blink {
wtf_size_t ByteBufferQueue::ReadInto(base::span<uint8_t> buffer_out) {
wtf_size_t read_amount = 0;
while (!buffer_out.empty() && !deque_of_buffers_.empty()) {
base::span<const uint8_t> front_buffer =
base::make_span(deque_of_buffers_.front())
.subspan(front_buffer_offset_);
DCHECK_GT(front_buffer.size(), 0u);
wtf_size_t buffer_read_amount =
std::min(buffer_out.size(), front_buffer.size());
memcpy(buffer_out.data(), front_buffer.data(), buffer_read_amount);
read_amount += buffer_read_amount;
buffer_out = buffer_out.subspan(buffer_read_amount);
if (buffer_read_amount < front_buffer.size()) {
front_buffer_offset_ += buffer_read_amount;
} else {
deque_of_buffers_.pop_front();
front_buffer_offset_ = 0;
}
}
size_ -= read_amount;
#if DCHECK_IS_ON()
CheckInvariants();
#endif
return read_amount;
}
void ByteBufferQueue::Append(Vector<uint8_t> buffer) {
if (buffer.IsEmpty()) {
return;
}
size_ += buffer.size();
deque_of_buffers_.push_back(std::move(buffer));
#if DCHECK_IS_ON()
CheckInvariants();
#endif
}
void ByteBufferQueue::Clear() {
deque_of_buffers_.clear();
front_buffer_offset_ = 0;
size_ = 0;
#if DCHECK_IS_ON()
CheckInvariants();
#endif
}
#if DCHECK_IS_ON()
void ByteBufferQueue::CheckInvariants() const {
wtf_size_t buffer_size_sum = 0;
for (const auto& buffer : deque_of_buffers_) {
DCHECK(!buffer.IsEmpty());
buffer_size_sum += buffer.size();
}
DCHECK_EQ(size_, buffer_size_sum - front_buffer_offset_);
if (deque_of_buffers_.empty()) {
DCHECK_EQ(front_buffer_offset_, 0u);
} else {
DCHECK_LT(front_buffer_offset_, deque_of_buffers_.front().size());
}
}
#endif
} // namespace blink
// Copyright 2018 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 THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_BYTE_BUFFER_QUEUE_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_BYTE_BUFFER_QUEUE_H_
#include "base/containers/span.h"
#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/platform/wtf/deque.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
// A ByteBufferQueue is a byte buffer with O(1) append and O(n) read operations.
// Clients can append entire byte buffers then copy data out across buffer
// boundaries using |ReadInto|.
class MODULES_EXPORT ByteBufferQueue final {
public:
// Number of bytes that can be read.
wtf_size_t size() const { return size_; }
// True if size() == 0.
bool empty() const { return size_ == 0; }
// Copies data into the given byte span. This will cause bytes to be consumed
// so that the next call to ReadInto will return different bytes.
// Returns the number of bytes written to |buffer_out|.
wtf_size_t ReadInto(base::span<uint8_t> buffer_out);
// Appends the contents of a byte buffer. This takes ownership of the buffer.
void Append(Vector<uint8_t> buffer);
// Clear stored buffers.
void Clear();
private:
#if DCHECK_IS_ON()
void CheckInvariants() const;
#endif
// Number of bytes that can be read.
// |Append()| adds to this number.
// |ReadInto()| subtracts from this number.
// Invariant: |size_| = sum of |deque_of_buffers_| element sizes -
// |front_buffer_offset_|.
wtf_size_t size_ = 0;
// Double-ended queue of byte buffers.
// |Append()| pushes to the right.
// |ReadInto()| pops from the left (if an entire buffer has been read).
// Invariant: No element in |deque_of_buffers_| is empty.
Deque<Vector<uint8_t>> deque_of_buffers_;
// The offset from which to start reading the buffer at the front of
// |deque_of_buffers_|.
// Invariants:
// - If |deque_of_buffers_| is empty, |front_buffer_offset_| = 0.
// - Otherwise, |front_buffer_offset_| < |deque_of_buffers_|.front().size().
wtf_size_t front_buffer_offset_ = 0;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_BYTE_BUFFER_QUEUE_H_
// Copyright 2018 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 "third_party/blink/renderer/modules/peerconnection/byte_buffer_queue.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace blink {
using testing::ElementsAre;
TEST(ByteBufferQueueTest, DefaultConstructor) {
ByteBufferQueue buffer_queue;
EXPECT_EQ(0u, buffer_queue.size());
EXPECT_TRUE(buffer_queue.empty());
}
TEST(ByteBufferQueueTest, AppendEmpty) {
ByteBufferQueue buffer_queue;
buffer_queue.Append({});
EXPECT_TRUE(buffer_queue.empty());
}
TEST(ByteBufferQueueTest, AppendOneSegment) {
ByteBufferQueue buffer_queue;
buffer_queue.Append({1, 2, 3});
EXPECT_EQ(3u, buffer_queue.size());
}
TEST(ByteBufferQueueTest, AppendTwoSegments) {
ByteBufferQueue buffer_queue;
buffer_queue.Append({1, 2, 3});
buffer_queue.Append({4, 5});
EXPECT_EQ(5u, buffer_queue.size());
}
TEST(ByteBufferQueueTest, ReadIntoEmpty) {
ByteBufferQueue buffer_queue;
Vector<uint8_t> data(100);
EXPECT_EQ(0u, buffer_queue.ReadInto(base::make_span(data)));
}
TEST(ByteBufferQueueTest, ReadIntoLessThanOneSegment) {
ByteBufferQueue buffer_queue;
buffer_queue.Append({1, 2, 3});
Vector<uint8_t> data(2);
EXPECT_EQ(2u, buffer_queue.ReadInto(base::make_span(data)));
EXPECT_EQ(1u, buffer_queue.size());
EXPECT_THAT(data, ElementsAre(1, 2));
}
TEST(ByteBufferQueueTest, ReadIntoExactOneSegmentSize) {
ByteBufferQueue buffer_queue;
buffer_queue.Append({1, 2, 3});
Vector<uint8_t> data(3);
EXPECT_EQ(3u, buffer_queue.ReadInto(base::make_span(data)));
EXPECT_EQ(0u, buffer_queue.size());
EXPECT_THAT(data, ElementsAre(1, 2, 3));
}
TEST(ByteBufferQueueTest, ReadIntoOverOneSegmentSize) {
ByteBufferQueue buffer_queue;
buffer_queue.Append({1, 2, 3});
Vector<uint8_t> data(5);
EXPECT_EQ(3u, buffer_queue.ReadInto(base::make_span(data)));
EXPECT_EQ(0u, buffer_queue.size());
EXPECT_THAT(data, ElementsAre(1, 2, 3, 0, 0));
}
TEST(ByteBufferQueueTest, ReadIntoEmptyData) {
ByteBufferQueue buffer_queue;
buffer_queue.Append({1, 2, 3});
Vector<uint8_t> data;
EXPECT_EQ(0u, buffer_queue.ReadInto(base::make_span(data)));
EXPECT_EQ(3u, buffer_queue.size());
}
TEST(ByteBufferQueueTest, ReadIntoExactlyTwoSegments) {
ByteBufferQueue buffer_queue;
buffer_queue.Append({1, 2, 3});
buffer_queue.Append({4, 5});
Vector<uint8_t> data(5);
EXPECT_EQ(5u, buffer_queue.ReadInto(base::make_span(data)));
EXPECT_EQ(0u, buffer_queue.size());
EXPECT_THAT(data, ElementsAre(1, 2, 3, 4, 5));
}
TEST(ByteBufferQueueTest, ReadIntoAcrossTwoSegmentsMisaligned) {
ByteBufferQueue buffer_queue;
buffer_queue.Append({1, 2, 3});
buffer_queue.Append({4, 5});
Vector<uint8_t> data(2);
EXPECT_EQ(2u, buffer_queue.ReadInto(base::make_span(data)));
EXPECT_THAT(data, ElementsAre(1, 2));
EXPECT_EQ(2u, buffer_queue.ReadInto(base::make_span(data)));
EXPECT_THAT(data, ElementsAre(3, 4));
EXPECT_EQ(1u, buffer_queue.ReadInto(base::make_span(data)));
EXPECT_THAT(data, ElementsAre(5, 4));
}
TEST(ByteBufferQueueTest, ClearEmptyBuffer) {
ByteBufferQueue buffer_queue;
buffer_queue.Clear();
EXPECT_EQ(0u, buffer_queue.size());
EXPECT_TRUE(buffer_queue.empty());
}
TEST(ByteBufferQueueTest, ReadIntoAfterClearThenAppend) {
ByteBufferQueue buffer_queue;
buffer_queue.Append({1, 2, 3});
Vector<uint8_t> data(2);
buffer_queue.ReadInto(base::make_span(data));
buffer_queue.Clear();
EXPECT_EQ(0u, buffer_queue.size());
EXPECT_EQ(0u, buffer_queue.ReadInto(base::make_span(data)));
buffer_queue.Append({4, 5});
EXPECT_EQ(2u, buffer_queue.ReadInto(base::make_span(data)));
EXPECT_THAT(data, ElementsAre(4, 5));
}
} // namespace blink
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