From 9a20fa6292e3eb6ecf30aa49124ce837ff052b55 Mon Sep 17 00:00:00 2001 From: mikescarlett Date: Mon, 11 Apr 2016 16:11:38 -0700 Subject: [PATCH] Add WriteUVarint to ByteBufferWriter and ReadUVarint to ByteBufferReader Methods to write/read a varint as described by https://developers.google.com/protocol-buffers/docs/encoding#varints that will be used for a QUIC data channel. Split from CL https://codereview.webrtc.org/1844803002/. Review URL: https://codereview.webrtc.org/1844333006 Cr-Commit-Position: refs/heads/master@{#12322} --- webrtc/base/bytebuffer.cc | 37 ++++++++++++++++++ webrtc/base/bytebuffer.h | 2 + webrtc/base/bytebuffer_unittest.cc | 60 ++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+) diff --git a/webrtc/base/bytebuffer.cc b/webrtc/base/bytebuffer.cc index cf4ce42574..9730ff23d6 100644 --- a/webrtc/base/bytebuffer.cc +++ b/webrtc/base/bytebuffer.cc @@ -88,6 +88,20 @@ void ByteBufferWriter::WriteUInt64(uint64_t val) { WriteBytes(reinterpret_cast(&v), 8); } +// Serializes an unsigned varint in the format described by +// https://developers.google.com/protocol-buffers/docs/encoding#varints +// with the caveat that integers are 64-bit, not 128-bit. +void ByteBufferWriter::WriteUVarint(uint64_t val) { + while (val >= 0x80) { + // Write 7 bits at a time, then set the msb to a continuation byte (msb=1). + char byte = static_cast(val) | 0x80; + WriteBytes(&byte, 1); + val >>= 7; + } + char last_byte = static_cast(val); + WriteBytes(&last_byte, 1); +} + void ByteBufferWriter::WriteString(const std::string& val) { WriteBytes(val.c_str(), val.size()); } @@ -220,6 +234,29 @@ bool ByteBufferReader::ReadUInt64(uint64_t* val) { } } +bool ByteBufferReader::ReadUVarint(uint64_t* val) { + if (!val) { + return false; + } + // Integers are deserialized 7 bits at a time, with each byte having a + // continuation byte (msb=1) if there are more bytes to be read. + uint64_t v = 0; + for (int i = 0; i < 64; i += 7) { + char byte; + if (!ReadBytes(&byte, 1)) { + return false; + } + // Read the first 7 bits of the byte, then offset by bits read so far. + v |= (static_cast(byte) & 0x7F) << i; + // True if the msb is not a continuation byte. + if (static_cast(byte) < 0x80) { + *val = v; + return true; + } + } + return false; +} + bool ByteBufferReader::ReadString(std::string* val, size_t len) { if (!val) return false; diff --git a/webrtc/base/bytebuffer.h b/webrtc/base/bytebuffer.h index 8fd086367d..cd7b2c6cea 100644 --- a/webrtc/base/bytebuffer.h +++ b/webrtc/base/bytebuffer.h @@ -57,6 +57,7 @@ class ByteBufferWriter : public ByteBuffer { void WriteUInt24(uint32_t val); void WriteUInt32(uint32_t val); void WriteUInt64(uint64_t val); + void WriteUVarint(uint64_t val); void WriteString(const std::string& val); void WriteBytes(const char* val, size_t len); @@ -110,6 +111,7 @@ class ByteBufferReader : public ByteBuffer { bool ReadUInt24(uint32_t* val); bool ReadUInt32(uint32_t* val); bool ReadUInt64(uint64_t* val); + bool ReadUVarint(uint64_t* val); bool ReadBytes(char* val, size_t len); // Appends next |len| bytes from the buffer to |val|. Returns false diff --git a/webrtc/base/bytebuffer_unittest.cc b/webrtc/base/bytebuffer_unittest.cc index 723641811f..bdbb159b66 100644 --- a/webrtc/base/bytebuffer_unittest.cc +++ b/webrtc/base/bytebuffer_unittest.cc @@ -196,4 +196,64 @@ TEST(ByteBufferTest, TestReadWriteBuffer) { } } +TEST(ByteBufferTest, TestReadWriteUVarint) { + ByteBufferWriter::ByteOrder orders[2] = {ByteBufferWriter::ORDER_HOST, + ByteBufferWriter::ORDER_NETWORK}; + for (ByteBufferWriter::ByteOrder& order : orders) { + ByteBufferWriter write_buffer(order); + size_t size = 0; + EXPECT_EQ(size, write_buffer.Length()); + + write_buffer.WriteUVarint(1u); + ++size; + EXPECT_EQ(size, write_buffer.Length()); + + write_buffer.WriteUVarint(2u); + ++size; + EXPECT_EQ(size, write_buffer.Length()); + + write_buffer.WriteUVarint(27u); + ++size; + EXPECT_EQ(size, write_buffer.Length()); + + write_buffer.WriteUVarint(149u); + size += 2; + EXPECT_EQ(size, write_buffer.Length()); + + write_buffer.WriteUVarint(68719476736u); + size += 6; + EXPECT_EQ(size, write_buffer.Length()); + + ByteBufferReader read_buffer(write_buffer.Data(), write_buffer.Length(), + order); + EXPECT_EQ(size, read_buffer.Length()); + uint64_t val1, val2, val3, val4, val5; + + ASSERT_TRUE(read_buffer.ReadUVarint(&val1)); + EXPECT_EQ(1u, val1); + --size; + EXPECT_EQ(size, read_buffer.Length()); + + ASSERT_TRUE(read_buffer.ReadUVarint(&val2)); + EXPECT_EQ(2u, val2); + --size; + EXPECT_EQ(size, read_buffer.Length()); + + ASSERT_TRUE(read_buffer.ReadUVarint(&val3)); + EXPECT_EQ(27u, val3); + --size; + EXPECT_EQ(size, read_buffer.Length()); + + ASSERT_TRUE(read_buffer.ReadUVarint(&val4)); + EXPECT_EQ(149u, val4); + size -= 2; + EXPECT_EQ(size, read_buffer.Length()); + + ASSERT_TRUE(read_buffer.ReadUVarint(&val5)); + EXPECT_EQ(68719476736u, val5); + size -= 6; + EXPECT_EQ(size, read_buffer.Length()); + } +} + } // namespace rtc