Lift generic functionality from QuicDataReader/QuicDataWriter into Quiche
The QUIC-specific functions stay in QuicDataReader/QuicDataWriter. Generally useful functions are moved into QuicheDataReader/QuicheDataWriter.
gfe-relnote: n/a, no functional change
PiperOrigin-RevId: 288584295
Change-Id: I53b102f56f43019a89db0aa624898ed44bb6ec11
diff --git a/common/quiche_data_reader.cc b/common/quiche_data_reader.cc
new file mode 100644
index 0000000..9842add
--- /dev/null
+++ b/common/quiche_data_reader.cc
@@ -0,0 +1,189 @@
+// Copyright (c) 2020 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 "net/third_party/quiche/src/common/quiche_data_reader.h"
+
+#include "net/third_party/quiche/src/common/platform/api/quiche_endian.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_logging.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h"
+
+namespace quiche {
+
+QuicheDataReader::QuicheDataReader(quiche::QuicheStringPiece data)
+ : QuicheDataReader(data.data(), data.length(), quiche::NETWORK_BYTE_ORDER) {
+}
+
+QuicheDataReader::QuicheDataReader(const char* data, const size_t len)
+ : QuicheDataReader(data, len, quiche::NETWORK_BYTE_ORDER) {}
+
+QuicheDataReader::QuicheDataReader(const char* data,
+ const size_t len,
+ quiche::Endianness endianness)
+ : data_(data), len_(len), pos_(0), endianness_(endianness) {}
+
+bool QuicheDataReader::ReadUInt8(uint8_t* result) {
+ return ReadBytes(result, sizeof(*result));
+}
+
+bool QuicheDataReader::ReadUInt16(uint16_t* result) {
+ if (!ReadBytes(result, sizeof(*result))) {
+ return false;
+ }
+ if (endianness_ == quiche::NETWORK_BYTE_ORDER) {
+ *result = quiche::QuicheEndian::NetToHost16(*result);
+ }
+ return true;
+}
+
+bool QuicheDataReader::ReadUInt32(uint32_t* result) {
+ if (!ReadBytes(result, sizeof(*result))) {
+ return false;
+ }
+ if (endianness_ == quiche::NETWORK_BYTE_ORDER) {
+ *result = quiche::QuicheEndian::NetToHost32(*result);
+ }
+ return true;
+}
+
+bool QuicheDataReader::ReadUInt64(uint64_t* result) {
+ if (!ReadBytes(result, sizeof(*result))) {
+ return false;
+ }
+ if (endianness_ == quiche::NETWORK_BYTE_ORDER) {
+ *result = quiche::QuicheEndian::NetToHost64(*result);
+ }
+ return true;
+}
+
+bool QuicheDataReader::ReadBytesToUInt64(size_t num_bytes, uint64_t* result) {
+ *result = 0u;
+ if (num_bytes > sizeof(*result)) {
+ return false;
+ }
+ if (endianness_ == quiche::HOST_BYTE_ORDER) {
+ return ReadBytes(result, num_bytes);
+ }
+
+ if (!ReadBytes(reinterpret_cast<char*>(result) + sizeof(*result) - num_bytes,
+ num_bytes)) {
+ return false;
+ }
+ *result = quiche::QuicheEndian::NetToHost64(*result);
+ return true;
+}
+
+bool QuicheDataReader::ReadStringPiece16(quiche::QuicheStringPiece* result) {
+ // Read resultant length.
+ uint16_t result_len;
+ if (!ReadUInt16(&result_len)) {
+ // OnFailure() already called.
+ return false;
+ }
+
+ return ReadStringPiece(result, result_len);
+}
+
+bool QuicheDataReader::ReadStringPiece(quiche::QuicheStringPiece* result,
+ size_t size) {
+ // Make sure that we have enough data to read.
+ if (!CanRead(size)) {
+ OnFailure();
+ return false;
+ }
+
+ // Set result.
+ *result = quiche::QuicheStringPiece(data_ + pos_, size);
+
+ // Iterate.
+ pos_ += size;
+
+ return true;
+}
+
+bool QuicheDataReader::ReadTag(uint32_t* tag) {
+ return ReadBytes(tag, sizeof(*tag));
+}
+
+quiche::QuicheStringPiece QuicheDataReader::ReadRemainingPayload() {
+ quiche::QuicheStringPiece payload = PeekRemainingPayload();
+ pos_ = len_;
+ return payload;
+}
+
+quiche::QuicheStringPiece QuicheDataReader::PeekRemainingPayload() const {
+ return quiche::QuicheStringPiece(data_ + pos_, len_ - pos_);
+}
+
+quiche::QuicheStringPiece QuicheDataReader::FullPayload() const {
+ return quiche::QuicheStringPiece(data_, len_);
+}
+
+bool QuicheDataReader::ReadBytes(void* result, size_t size) {
+ // Make sure that we have enough data to read.
+ if (!CanRead(size)) {
+ OnFailure();
+ return false;
+ }
+
+ // Read into result.
+ memcpy(result, data_ + pos_, size);
+
+ // Iterate.
+ pos_ += size;
+
+ return true;
+}
+
+bool QuicheDataReader::Seek(size_t size) {
+ if (!CanRead(size)) {
+ OnFailure();
+ return false;
+ }
+ pos_ += size;
+ return true;
+}
+
+bool QuicheDataReader::IsDoneReading() const {
+ return len_ == pos_;
+}
+
+size_t QuicheDataReader::BytesRemaining() const {
+ return len_ - pos_;
+}
+
+bool QuicheDataReader::TruncateRemaining(size_t truncation_length) {
+ if (truncation_length > BytesRemaining()) {
+ return false;
+ }
+ len_ = pos_ + truncation_length;
+ return true;
+}
+
+bool QuicheDataReader::CanRead(size_t bytes) const {
+ return bytes <= (len_ - pos_);
+}
+
+void QuicheDataReader::OnFailure() {
+ // Set our iterator to the end of the buffer so that further reads fail
+ // immediately.
+ pos_ = len_;
+}
+
+uint8_t QuicheDataReader::PeekByte() const {
+ if (pos_ >= len_) {
+ QUICHE_LOG(FATAL)
+ << "Reading is done, cannot peek next byte. Tried to read pos = "
+ << pos_ << " buffer length = " << len_;
+ return 0;
+ }
+ return data_[pos_];
+}
+
+std::string QuicheDataReader::DebugString() const {
+ return quiche::QuicheStrCat(" { length: ", len_, ", position: ", pos_, " }");
+}
+
+#undef ENDPOINT // undef for jumbo builds
+} // namespace quiche
diff --git a/common/quiche_data_reader.h b/common/quiche_data_reader.h
new file mode 100644
index 0000000..45c0961
--- /dev/null
+++ b/common/quiche_data_reader.h
@@ -0,0 +1,173 @@
+// Copyright (c) 2020 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 QUICHE_COMMON_QUICHE_DATA_READER_H_
+#define QUICHE_COMMON_QUICHE_DATA_READER_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+
+#include "net/third_party/quiche/src/common/platform/api/quiche_endian.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_export.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h"
+
+namespace quiche {
+
+// To use, simply construct a QuicheDataReader using the underlying buffer that
+// you'd like to read fields from, then call one of the Read*() methods to
+// actually do some reading.
+//
+// This class keeps an internal iterator to keep track of what's already been
+// read and each successive Read*() call automatically increments said iterator
+// on success. On failure, internal state of the QuicheDataReader should not be
+// trusted and it is up to the caller to throw away the failed instance and
+// handle the error as appropriate. None of the Read*() methods should ever be
+// called after failure, as they will also fail immediately.
+class QUICHE_EXPORT_PRIVATE QuicheDataReader {
+ public:
+ // Constructs a reader using NETWORK_BYTE_ORDER endianness.
+ // Caller must provide an underlying buffer to work on.
+ explicit QuicheDataReader(quiche::QuicheStringPiece data);
+ // Constructs a reader using NETWORK_BYTE_ORDER endianness.
+ // Caller must provide an underlying buffer to work on.
+ QuicheDataReader(const char* data, const size_t len);
+ // Constructs a reader using the specified endianness.
+ // Caller must provide an underlying buffer to work on.
+ QuicheDataReader(const char* data,
+ const size_t len,
+ quiche::Endianness endianness);
+ QuicheDataReader(const QuicheDataReader&) = delete;
+ QuicheDataReader& operator=(const QuicheDataReader&) = delete;
+
+ // Empty destructor.
+ ~QuicheDataReader() {}
+
+ // Reads an 8/16/32/64-bit unsigned integer into the given output
+ // parameter. Forwards the internal iterator on success. Returns true on
+ // success, false otherwise.
+ bool ReadUInt8(uint8_t* result);
+ bool ReadUInt16(uint16_t* result);
+ bool ReadUInt32(uint32_t* result);
+ bool ReadUInt64(uint64_t* result);
+
+ // Set |result| to 0, then read |num_bytes| bytes in the correct byte order
+ // into least significant bytes of |result|.
+ bool ReadBytesToUInt64(size_t num_bytes, uint64_t* result);
+
+ // Reads a string prefixed with 16-bit length into the given output parameter.
+ //
+ // NOTE: Does not copy but rather references strings in the underlying buffer.
+ // This should be kept in mind when handling memory management!
+ //
+ // Forwards the internal iterator on success.
+ // Returns true on success, false otherwise.
+ bool ReadStringPiece16(quiche::QuicheStringPiece* result);
+
+ // Reads a given number of bytes into the given buffer. The buffer
+ // must be of adequate size.
+ // Forwards the internal iterator on success.
+ // Returns true on success, false otherwise.
+ bool ReadStringPiece(quiche::QuicheStringPiece* result, size_t size);
+
+ // Reads tag represented as 32-bit unsigned integer into given output
+ // parameter. Tags are in big endian on the wire (e.g., CHLO is
+ // 'C','H','L','O') and are read in byte order, so tags in memory are in big
+ // endian.
+ bool ReadTag(uint32_t* tag);
+
+ // Returns the remaining payload as a quiche::QuicheStringPiece.
+ //
+ // NOTE: Does not copy but rather references strings in the underlying buffer.
+ // This should be kept in mind when handling memory management!
+ //
+ // Forwards the internal iterator.
+ quiche::QuicheStringPiece ReadRemainingPayload();
+
+ // Returns the remaining payload as a quiche::QuicheStringPiece.
+ //
+ // NOTE: Does not copy but rather references strings in the underlying buffer.
+ // This should be kept in mind when handling memory management!
+ //
+ // DOES NOT forward the internal iterator.
+ quiche::QuicheStringPiece PeekRemainingPayload() const;
+
+ // Returns the entire payload as a quiche::QuicheStringPiece.
+ //
+ // NOTE: Does not copy but rather references strings in the underlying buffer.
+ // This should be kept in mind when handling memory management!
+ //
+ // DOES NOT forward the internal iterator.
+ quiche::QuicheStringPiece FullPayload() const;
+
+ // Reads a given number of bytes into the given buffer. The buffer
+ // must be of adequate size.
+ // Forwards the internal iterator on success.
+ // Returns true on success, false otherwise.
+ bool ReadBytes(void* result, size_t size);
+
+ // Skips over |size| bytes from the buffer and forwards the internal iterator.
+ // Returns true if there are at least |size| bytes remaining to read, false
+ // otherwise.
+ bool Seek(size_t size);
+
+ // Returns true if the entirety of the underlying buffer has been read via
+ // Read*() calls.
+ bool IsDoneReading() const;
+
+ // Returns the number of bytes remaining to be read.
+ size_t BytesRemaining() const;
+
+ // Truncates the reader down by reducing its internal length.
+ // If called immediately after calling this, BytesRemaining will
+ // return |truncation_length|. If truncation_length is less than the
+ // current value of BytesRemaining, this does nothing and returns false.
+ bool TruncateRemaining(size_t truncation_length);
+
+ // Returns the next byte that to be read. Must not be called when there are no
+ // bytes to be read.
+ //
+ // DOES NOT forward the internal iterator.
+ uint8_t PeekByte() const;
+
+ std::string DebugString() const;
+
+ protected:
+ // Returns true if the underlying buffer has enough room to read the given
+ // amount of bytes.
+ bool CanRead(size_t bytes) const;
+
+ // To be called when a read fails for any reason.
+ void OnFailure();
+
+ const char* data() const { return data_; }
+
+ size_t pos() const { return pos_; }
+
+ void AdvancePos(size_t amount) {
+ DCHECK_LE(pos_, std::numeric_limits<size_t>::max() - amount);
+ DCHECK_LE(pos_, len_ - amount);
+ pos_ += amount;
+ }
+
+ quiche::Endianness endianness() const { return endianness_; }
+
+ private:
+ // TODO(fkastenholz, b/73004262) change buffer_, et al, to be uint8_t, not
+ // char. The data buffer that we're reading from.
+ const char* data_;
+
+ // The length of the data buffer that we're reading from.
+ size_t len_;
+
+ // The location of the next read from our data buffer.
+ size_t pos_;
+
+ // The endianness to read integers and floating numbers.
+ quiche::Endianness endianness_;
+};
+
+} // namespace quiche
+
+#endif // QUICHE_COMMON_QUICHE_DATA_READER_H_
diff --git a/common/quiche_data_writer.cc b/common/quiche_data_writer.cc
new file mode 100644
index 0000000..4488f72
--- /dev/null
+++ b/common/quiche_data_writer.cc
@@ -0,0 +1,152 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/common/quiche_data_writer.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "net/third_party/quiche/src/common/platform/api/quiche_endian.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h"
+
+namespace quiche {
+
+QuicheDataWriter::QuicheDataWriter(size_t size, char* buffer)
+ : QuicheDataWriter(size, buffer, quiche::NETWORK_BYTE_ORDER) {}
+
+QuicheDataWriter::QuicheDataWriter(size_t size,
+ char* buffer,
+ quiche::Endianness endianness)
+ : buffer_(buffer), capacity_(size), length_(0), endianness_(endianness) {}
+
+QuicheDataWriter::~QuicheDataWriter() {}
+
+char* QuicheDataWriter::data() {
+ return buffer_;
+}
+
+bool QuicheDataWriter::WriteUInt8(uint8_t value) {
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool QuicheDataWriter::WriteUInt16(uint16_t value) {
+ if (endianness_ == quiche::NETWORK_BYTE_ORDER) {
+ value = quiche::QuicheEndian::HostToNet16(value);
+ }
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool QuicheDataWriter::WriteUInt32(uint32_t value) {
+ if (endianness_ == quiche::NETWORK_BYTE_ORDER) {
+ value = quiche::QuicheEndian::HostToNet32(value);
+ }
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool QuicheDataWriter::WriteUInt64(uint64_t value) {
+ if (endianness_ == quiche::NETWORK_BYTE_ORDER) {
+ value = quiche::QuicheEndian::HostToNet64(value);
+ }
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool QuicheDataWriter::WriteBytesToUInt64(size_t num_bytes, uint64_t value) {
+ if (num_bytes > sizeof(value)) {
+ return false;
+ }
+ if (endianness_ == quiche::HOST_BYTE_ORDER) {
+ return WriteBytes(&value, num_bytes);
+ }
+
+ value = quiche::QuicheEndian::HostToNet64(value);
+ return WriteBytes(reinterpret_cast<char*>(&value) + sizeof(value) - num_bytes,
+ num_bytes);
+}
+
+bool QuicheDataWriter::WriteStringPiece16(quiche::QuicheStringPiece val) {
+ if (val.size() > std::numeric_limits<uint16_t>::max()) {
+ return false;
+ }
+ if (!WriteUInt16(static_cast<uint16_t>(val.size()))) {
+ return false;
+ }
+ return WriteBytes(val.data(), val.size());
+}
+
+bool QuicheDataWriter::WriteStringPiece(quiche::QuicheStringPiece val) {
+ return WriteBytes(val.data(), val.size());
+}
+
+char* QuicheDataWriter::BeginWrite(size_t length) {
+ if (length_ > capacity_) {
+ return nullptr;
+ }
+
+ if (capacity_ - length_ < length) {
+ return nullptr;
+ }
+
+#ifdef ARCH_CPU_64_BITS
+ DCHECK_LE(length, std::numeric_limits<uint32_t>::max());
+#endif
+
+ return buffer_ + length_;
+}
+
+bool QuicheDataWriter::WriteBytes(const void* data, size_t data_len) {
+ char* dest = BeginWrite(data_len);
+ if (!dest) {
+ return false;
+ }
+
+ memcpy(dest, data, data_len);
+
+ length_ += data_len;
+ return true;
+}
+
+bool QuicheDataWriter::WriteRepeatedByte(uint8_t byte, size_t count) {
+ char* dest = BeginWrite(count);
+ if (!dest) {
+ return false;
+ }
+
+ memset(dest, byte, count);
+
+ length_ += count;
+ return true;
+}
+
+void QuicheDataWriter::WritePadding() {
+ DCHECK_LE(length_, capacity_);
+ if (length_ > capacity_) {
+ return;
+ }
+ memset(buffer_ + length_, 0x00, capacity_ - length_);
+ length_ = capacity_;
+}
+
+bool QuicheDataWriter::WritePaddingBytes(size_t count) {
+ return WriteRepeatedByte(0x00, count);
+}
+
+bool QuicheDataWriter::WriteTag(uint32_t tag) {
+ return WriteBytes(&tag, sizeof(tag));
+}
+
+bool QuicheDataWriter::Seek(size_t length) {
+ if (!BeginWrite(length)) {
+ return false;
+ }
+ length_ += length;
+ return true;
+}
+
+std::string QuicheDataWriter::DebugString() const {
+ return quiche::QuicheStrCat(" { capacity: ", capacity_, ", length: ", length_,
+ " }");
+}
+
+} // namespace quiche
diff --git a/common/quiche_data_writer.h b/common/quiche_data_writer.h
new file mode 100644
index 0000000..8df1f90
--- /dev/null
+++ b/common/quiche_data_writer.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2020 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 QUICHE_COMMON_QUICHE_DATA_WRITER_H_
+#define QUICHE_COMMON_QUICHE_DATA_WRITER_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+
+#include "net/third_party/quiche/src/common/platform/api/quiche_endian.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_export.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h"
+
+namespace quiche {
+
+// This class provides facilities for packing binary data.
+//
+// The QuicheDataWriter supports appending primitive values (int, string, etc)
+// to a frame instance. The internal memory buffer is exposed as the "data"
+// of the QuicheDataWriter.
+class QUICHE_EXPORT_PRIVATE QuicheDataWriter {
+ public:
+ // Creates a QuicheDataWriter where |buffer| is not owned
+ // using NETWORK_BYTE_ORDER endianness.
+ QuicheDataWriter(size_t size, char* buffer);
+ // Creates a QuicheDataWriter where |buffer| is not owned
+ // using the specified endianness.
+ QuicheDataWriter(size_t size, char* buffer, quiche::Endianness endianness);
+ QuicheDataWriter(const QuicheDataWriter&) = delete;
+ QuicheDataWriter& operator=(const QuicheDataWriter&) = delete;
+
+ ~QuicheDataWriter();
+
+ // Returns the size of the QuicheDataWriter's data.
+ size_t length() const { return length_; }
+
+ // Retrieves the buffer from the QuicheDataWriter without changing ownership.
+ char* data();
+
+ // Methods for adding to the payload. These values are appended to the end
+ // of the QuicheDataWriter payload.
+
+ // Writes 8/16/32/64-bit unsigned integers.
+ bool WriteUInt8(uint8_t value);
+ bool WriteUInt16(uint16_t value);
+ bool WriteUInt32(uint32_t value);
+ bool WriteUInt64(uint64_t value);
+
+ // Writes least significant |num_bytes| of a 64-bit unsigned integer in the
+ // correct byte order.
+ bool WriteBytesToUInt64(size_t num_bytes, uint64_t value);
+
+ bool WriteStringPiece(quiche::QuicheStringPiece val);
+ bool WriteStringPiece16(quiche::QuicheStringPiece val);
+ bool WriteBytes(const void* data, size_t data_len);
+ bool WriteRepeatedByte(uint8_t byte, size_t count);
+ // Fills the remaining buffer with null characters.
+ void WritePadding();
+ // Write padding of |count| bytes.
+ bool WritePaddingBytes(size_t count);
+
+ // Write tag as a 32-bit unsigned integer to the payload. As tags are already
+ // converted to big endian (e.g., CHLO is 'C','H','L','O') in memory by TAG or
+ // MakeQuicTag and tags are written in byte order, so tags on the wire are
+ // in big endian.
+ bool WriteTag(uint32_t tag);
+
+ // Advance the writer's position for writing by |length| bytes without writing
+ // anything. This method only makes sense to be used on a buffer that has
+ // already been written to (and is having certain parts rewritten).
+ bool Seek(size_t length);
+
+ size_t capacity() const { return capacity_; }
+
+ size_t remaining() const { return capacity_ - length_; }
+
+ std::string DebugString() const;
+
+ protected:
+ // Returns the location that the data should be written at, or nullptr if
+ // there is not enough room. Call EndWrite with the returned offset and the
+ // given length to pad out for the next write.
+ char* BeginWrite(size_t length);
+
+ quiche::Endianness endianness() const { return endianness_; }
+
+ char* buffer() const { return buffer_; }
+
+ void IncreaseLength(size_t delta) {
+ DCHECK_LE(length_, std::numeric_limits<size_t>::max() - delta);
+ DCHECK_LE(length_, capacity_ - delta);
+ length_ += delta;
+ }
+
+ private:
+ // TODO(fkastenholz, b/73004262) change buffer_, et al, to be uint8_t, not
+ // char.
+ char* buffer_;
+ size_t capacity_; // Allocation size of payload (or -1 if buffer is const).
+ size_t length_; // Current length of the buffer.
+
+ // The endianness to write integers and floating numbers.
+ quiche::Endianness endianness_;
+};
+
+} // namespace quiche
+
+#endif // QUICHE_COMMON_QUICHE_DATA_WRITER_H_
diff --git a/common/quiche_data_writer_test.cc b/common/quiche_data_writer_test.cc
new file mode 100644
index 0000000..78f8abf
--- /dev/null
+++ b/common/quiche_data_writer_test.cc
@@ -0,0 +1,419 @@
+// Copyright (c) 2020 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 "net/third_party/quiche/src/common/quiche_data_writer.h"
+
+#include <cstdint>
+#include <cstring>
+
+#include "net/third_party/quiche/src/common/platform/api/quiche_arraysize.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_endian.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_test.h"
+#include "net/third_party/quiche/src/common/quiche_data_reader.h"
+#include "net/third_party/quiche/src/common/test_tools/quiche_test_utils.h"
+
+namespace quiche {
+namespace test {
+namespace {
+
+char* AsChars(unsigned char* data) {
+ return reinterpret_cast<char*>(data);
+}
+
+struct TestParams {
+ explicit TestParams(quiche::Endianness endianness) : endianness(endianness) {}
+
+ quiche::Endianness endianness;
+};
+
+// Used by ::testing::PrintToStringParamName().
+std::string PrintToString(const TestParams& p) {
+ return quiche::QuicheStrCat(
+ (p.endianness == quiche::NETWORK_BYTE_ORDER ? "Network" : "Host"),
+ "ByteOrder");
+}
+
+std::vector<TestParams> GetTestParams() {
+ std::vector<TestParams> params;
+ for (quiche::Endianness endianness :
+ {quiche::NETWORK_BYTE_ORDER, quiche::HOST_BYTE_ORDER}) {
+ params.push_back(TestParams(endianness));
+ }
+ return params;
+}
+
+class QuicheDataWriterTest : public QuicheTestWithParam<TestParams> {};
+
+INSTANTIATE_TEST_SUITE_P(QuicheDataWriterTests,
+ QuicheDataWriterTest,
+ ::testing::ValuesIn(GetTestParams()),
+ ::testing::PrintToStringParamName());
+
+TEST_P(QuicheDataWriterTest, Write16BitUnsignedIntegers) {
+ char little_endian16[] = {0x22, 0x11};
+ char big_endian16[] = {0x11, 0x22};
+ char buffer16[2];
+ {
+ uint16_t in_memory16 = 0x1122;
+ QuicheDataWriter writer(2, buffer16, GetParam().endianness);
+ writer.WriteUInt16(in_memory16);
+ test::CompareCharArraysWithHexError(
+ "uint16_t", buffer16, 2,
+ GetParam().endianness == quiche::NETWORK_BYTE_ORDER ? big_endian16
+ : little_endian16,
+ 2);
+
+ uint16_t read_number16;
+ QuicheDataReader reader(buffer16, 2, GetParam().endianness);
+ reader.ReadUInt16(&read_number16);
+ EXPECT_EQ(in_memory16, read_number16);
+ }
+
+ {
+ uint64_t in_memory16 = 0x0000000000001122;
+ QuicheDataWriter writer(2, buffer16, GetParam().endianness);
+ writer.WriteBytesToUInt64(2, in_memory16);
+ test::CompareCharArraysWithHexError(
+ "uint16_t", buffer16, 2,
+ GetParam().endianness == quiche::NETWORK_BYTE_ORDER ? big_endian16
+ : little_endian16,
+ 2);
+
+ uint64_t read_number16;
+ QuicheDataReader reader(buffer16, 2, GetParam().endianness);
+ reader.ReadBytesToUInt64(2, &read_number16);
+ EXPECT_EQ(in_memory16, read_number16);
+ }
+}
+
+TEST_P(QuicheDataWriterTest, Write24BitUnsignedIntegers) {
+ char little_endian24[] = {0x33, 0x22, 0x11};
+ char big_endian24[] = {0x11, 0x22, 0x33};
+ char buffer24[3];
+ uint64_t in_memory24 = 0x0000000000112233;
+ QuicheDataWriter writer(3, buffer24, GetParam().endianness);
+ writer.WriteBytesToUInt64(3, in_memory24);
+ test::CompareCharArraysWithHexError(
+ "uint24", buffer24, 3,
+ GetParam().endianness == quiche::NETWORK_BYTE_ORDER ? big_endian24
+ : little_endian24,
+ 3);
+
+ uint64_t read_number24;
+ QuicheDataReader reader(buffer24, 3, GetParam().endianness);
+ reader.ReadBytesToUInt64(3, &read_number24);
+ EXPECT_EQ(in_memory24, read_number24);
+}
+
+TEST_P(QuicheDataWriterTest, Write32BitUnsignedIntegers) {
+ char little_endian32[] = {0x44, 0x33, 0x22, 0x11};
+ char big_endian32[] = {0x11, 0x22, 0x33, 0x44};
+ char buffer32[4];
+ {
+ uint32_t in_memory32 = 0x11223344;
+ QuicheDataWriter writer(4, buffer32, GetParam().endianness);
+ writer.WriteUInt32(in_memory32);
+ test::CompareCharArraysWithHexError(
+ "uint32_t", buffer32, 4,
+ GetParam().endianness == quiche::NETWORK_BYTE_ORDER ? big_endian32
+ : little_endian32,
+ 4);
+
+ uint32_t read_number32;
+ QuicheDataReader reader(buffer32, 4, GetParam().endianness);
+ reader.ReadUInt32(&read_number32);
+ EXPECT_EQ(in_memory32, read_number32);
+ }
+
+ {
+ uint64_t in_memory32 = 0x11223344;
+ QuicheDataWriter writer(4, buffer32, GetParam().endianness);
+ writer.WriteBytesToUInt64(4, in_memory32);
+ test::CompareCharArraysWithHexError(
+ "uint32_t", buffer32, 4,
+ GetParam().endianness == quiche::NETWORK_BYTE_ORDER ? big_endian32
+ : little_endian32,
+ 4);
+
+ uint64_t read_number32;
+ QuicheDataReader reader(buffer32, 4, GetParam().endianness);
+ reader.ReadBytesToUInt64(4, &read_number32);
+ EXPECT_EQ(in_memory32, read_number32);
+ }
+}
+
+TEST_P(QuicheDataWriterTest, Write40BitUnsignedIntegers) {
+ uint64_t in_memory40 = 0x0000001122334455;
+ char little_endian40[] = {0x55, 0x44, 0x33, 0x22, 0x11};
+ char big_endian40[] = {0x11, 0x22, 0x33, 0x44, 0x55};
+ char buffer40[5];
+ QuicheDataWriter writer(5, buffer40, GetParam().endianness);
+ writer.WriteBytesToUInt64(5, in_memory40);
+ test::CompareCharArraysWithHexError(
+ "uint40", buffer40, 5,
+ GetParam().endianness == quiche::NETWORK_BYTE_ORDER ? big_endian40
+ : little_endian40,
+ 5);
+
+ uint64_t read_number40;
+ QuicheDataReader reader(buffer40, 5, GetParam().endianness);
+ reader.ReadBytesToUInt64(5, &read_number40);
+ EXPECT_EQ(in_memory40, read_number40);
+}
+
+TEST_P(QuicheDataWriterTest, Write48BitUnsignedIntegers) {
+ uint64_t in_memory48 = 0x0000112233445566;
+ char little_endian48[] = {0x66, 0x55, 0x44, 0x33, 0x22, 0x11};
+ char big_endian48[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+ char buffer48[6];
+ QuicheDataWriter writer(6, buffer48, GetParam().endianness);
+ writer.WriteBytesToUInt64(6, in_memory48);
+ test::CompareCharArraysWithHexError(
+ "uint48", buffer48, 6,
+ GetParam().endianness == quiche::NETWORK_BYTE_ORDER ? big_endian48
+ : little_endian48,
+ 6);
+
+ uint64_t read_number48;
+ QuicheDataReader reader(buffer48, 6, GetParam().endianness);
+ reader.ReadBytesToUInt64(6., &read_number48);
+ EXPECT_EQ(in_memory48, read_number48);
+}
+
+TEST_P(QuicheDataWriterTest, Write56BitUnsignedIntegers) {
+ uint64_t in_memory56 = 0x0011223344556677;
+ char little_endian56[] = {0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11};
+ char big_endian56[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+ char buffer56[7];
+ QuicheDataWriter writer(7, buffer56, GetParam().endianness);
+ writer.WriteBytesToUInt64(7, in_memory56);
+ test::CompareCharArraysWithHexError(
+ "uint56", buffer56, 7,
+ GetParam().endianness == quiche::NETWORK_BYTE_ORDER ? big_endian56
+ : little_endian56,
+ 7);
+
+ uint64_t read_number56;
+ QuicheDataReader reader(buffer56, 7, GetParam().endianness);
+ reader.ReadBytesToUInt64(7, &read_number56);
+ EXPECT_EQ(in_memory56, read_number56);
+}
+
+TEST_P(QuicheDataWriterTest, Write64BitUnsignedIntegers) {
+ uint64_t in_memory64 = 0x1122334455667788;
+ unsigned char little_endian64[] = {0x88, 0x77, 0x66, 0x55,
+ 0x44, 0x33, 0x22, 0x11};
+ unsigned char big_endian64[] = {0x11, 0x22, 0x33, 0x44,
+ 0x55, 0x66, 0x77, 0x88};
+ char buffer64[8];
+ QuicheDataWriter writer(8, buffer64, GetParam().endianness);
+ writer.WriteBytesToUInt64(8, in_memory64);
+ test::CompareCharArraysWithHexError(
+ "uint64_t", buffer64, 8,
+ GetParam().endianness == quiche::NETWORK_BYTE_ORDER
+ ? AsChars(big_endian64)
+ : AsChars(little_endian64),
+ 8);
+
+ uint64_t read_number64;
+ QuicheDataReader reader(buffer64, 8, GetParam().endianness);
+ reader.ReadBytesToUInt64(8, &read_number64);
+ EXPECT_EQ(in_memory64, read_number64);
+
+ QuicheDataWriter writer2(8, buffer64, GetParam().endianness);
+ writer2.WriteUInt64(in_memory64);
+ test::CompareCharArraysWithHexError(
+ "uint64_t", buffer64, 8,
+ GetParam().endianness == quiche::NETWORK_BYTE_ORDER
+ ? AsChars(big_endian64)
+ : AsChars(little_endian64),
+ 8);
+ read_number64 = 0u;
+ QuicheDataReader reader2(buffer64, 8, GetParam().endianness);
+ reader2.ReadUInt64(&read_number64);
+ EXPECT_EQ(in_memory64, read_number64);
+}
+
+TEST_P(QuicheDataWriterTest, WriteIntegers) {
+ char buf[43];
+ uint8_t i8 = 0x01;
+ uint16_t i16 = 0x0123;
+ uint32_t i32 = 0x01234567;
+ uint64_t i64 = 0x0123456789ABCDEF;
+ QuicheDataWriter writer(46, buf, GetParam().endianness);
+ for (size_t i = 0; i < 10; ++i) {
+ switch (i) {
+ case 0u:
+ EXPECT_TRUE(writer.WriteBytesToUInt64(i, i64));
+ break;
+ case 1u:
+ EXPECT_TRUE(writer.WriteUInt8(i8));
+ EXPECT_TRUE(writer.WriteBytesToUInt64(i, i64));
+ break;
+ case 2u:
+ EXPECT_TRUE(writer.WriteUInt16(i16));
+ EXPECT_TRUE(writer.WriteBytesToUInt64(i, i64));
+ break;
+ case 3u:
+ EXPECT_TRUE(writer.WriteBytesToUInt64(i, i64));
+ break;
+ case 4u:
+ EXPECT_TRUE(writer.WriteUInt32(i32));
+ EXPECT_TRUE(writer.WriteBytesToUInt64(i, i64));
+ break;
+ case 5u:
+ case 6u:
+ case 7u:
+ case 8u:
+ EXPECT_TRUE(writer.WriteBytesToUInt64(i, i64));
+ break;
+ default:
+ EXPECT_FALSE(writer.WriteBytesToUInt64(i, i64));
+ }
+ }
+
+ QuicheDataReader reader(buf, 46, GetParam().endianness);
+ for (size_t i = 0; i < 10; ++i) {
+ uint8_t read8;
+ uint16_t read16;
+ uint32_t read32;
+ uint64_t read64;
+ switch (i) {
+ case 0u:
+ EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64));
+ EXPECT_EQ(0u, read64);
+ break;
+ case 1u:
+ EXPECT_TRUE(reader.ReadUInt8(&read8));
+ EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64));
+ EXPECT_EQ(i8, read8);
+ EXPECT_EQ(0xEFu, read64);
+ break;
+ case 2u:
+ EXPECT_TRUE(reader.ReadUInt16(&read16));
+ EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64));
+ EXPECT_EQ(i16, read16);
+ EXPECT_EQ(0xCDEFu, read64);
+ break;
+ case 3u:
+ EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64));
+ EXPECT_EQ(0xABCDEFu, read64);
+ break;
+ case 4u:
+ EXPECT_TRUE(reader.ReadUInt32(&read32));
+ EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64));
+ EXPECT_EQ(i32, read32);
+ EXPECT_EQ(0x89ABCDEFu, read64);
+ break;
+ case 5u:
+ EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64));
+ EXPECT_EQ(0x6789ABCDEFu, read64);
+ break;
+ case 6u:
+ EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64));
+ EXPECT_EQ(0x456789ABCDEFu, read64);
+ break;
+ case 7u:
+ EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64));
+ EXPECT_EQ(0x23456789ABCDEFu, read64);
+ break;
+ case 8u:
+ EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64));
+ EXPECT_EQ(0x0123456789ABCDEFu, read64);
+ break;
+ default:
+ EXPECT_FALSE(reader.ReadBytesToUInt64(i, &read64));
+ }
+ }
+}
+
+TEST_P(QuicheDataWriterTest, WriteBytes) {
+ char bytes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
+ char buf[QUICHE_ARRAYSIZE(bytes)];
+ QuicheDataWriter writer(QUICHE_ARRAYSIZE(buf), buf, GetParam().endianness);
+ EXPECT_TRUE(writer.WriteBytes(bytes, QUICHE_ARRAYSIZE(bytes)));
+ for (unsigned int i = 0; i < QUICHE_ARRAYSIZE(bytes); ++i) {
+ EXPECT_EQ(bytes[i], buf[i]);
+ }
+}
+
+TEST_P(QuicheDataWriterTest, Seek) {
+ char buffer[3] = {};
+ QuicheDataWriter writer(QUICHE_ARRAYSIZE(buffer), buffer,
+ GetParam().endianness);
+ EXPECT_TRUE(writer.WriteUInt8(42));
+ EXPECT_TRUE(writer.Seek(1));
+ EXPECT_TRUE(writer.WriteUInt8(3));
+
+ char expected[] = {42, 0, 3};
+ for (size_t i = 0; i < QUICHE_ARRAYSIZE(expected); ++i) {
+ EXPECT_EQ(buffer[i], expected[i]);
+ }
+}
+
+TEST_P(QuicheDataWriterTest, SeekTooFarFails) {
+ char buffer[20];
+
+ // Check that one can seek to the end of the writer, but not past.
+ {
+ QuicheDataWriter writer(QUICHE_ARRAYSIZE(buffer), buffer,
+ GetParam().endianness);
+ EXPECT_TRUE(writer.Seek(20));
+ EXPECT_FALSE(writer.Seek(1));
+ }
+
+ // Seeking several bytes past the end fails.
+ {
+ QuicheDataWriter writer(QUICHE_ARRAYSIZE(buffer), buffer,
+ GetParam().endianness);
+ EXPECT_FALSE(writer.Seek(100));
+ }
+
+ // Seeking so far that arithmetic overflow could occur also fails.
+ {
+ QuicheDataWriter writer(QUICHE_ARRAYSIZE(buffer), buffer,
+ GetParam().endianness);
+ EXPECT_TRUE(writer.Seek(10));
+ EXPECT_FALSE(writer.Seek(std::numeric_limits<size_t>::max()));
+ }
+}
+
+TEST_P(QuicheDataWriterTest, PayloadReads) {
+ char buffer[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
+ char expected_first_read[4] = {1, 2, 3, 4};
+ char expected_remaining[12] = {5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
+ QuicheDataReader reader(buffer, sizeof(buffer));
+ char first_read_buffer[4] = {};
+ EXPECT_TRUE(reader.ReadBytes(first_read_buffer, sizeof(first_read_buffer)));
+ test::CompareCharArraysWithHexError(
+ "first read", first_read_buffer, sizeof(first_read_buffer),
+ expected_first_read, sizeof(expected_first_read));
+ quiche::QuicheStringPiece peeked_remaining_payload =
+ reader.PeekRemainingPayload();
+ test::CompareCharArraysWithHexError(
+ "peeked_remaining_payload", peeked_remaining_payload.data(),
+ peeked_remaining_payload.length(), expected_remaining,
+ sizeof(expected_remaining));
+ quiche::QuicheStringPiece full_payload = reader.FullPayload();
+ test::CompareCharArraysWithHexError("full_payload", full_payload.data(),
+ full_payload.length(), buffer,
+ sizeof(buffer));
+ quiche::QuicheStringPiece read_remaining_payload =
+ reader.ReadRemainingPayload();
+ test::CompareCharArraysWithHexError(
+ "read_remaining_payload", read_remaining_payload.data(),
+ read_remaining_payload.length(), expected_remaining,
+ sizeof(expected_remaining));
+ EXPECT_TRUE(reader.IsDoneReading());
+ quiche::QuicheStringPiece full_payload2 = reader.FullPayload();
+ test::CompareCharArraysWithHexError("full_payload2", full_payload2.data(),
+ full_payload2.length(), buffer,
+ sizeof(buffer));
+}
+
+} // namespace
+} // namespace test
+} // namespace quiche