Added a BitBufferWriter subclass that contains methods for writing bit and byte-sized data, along with exponential golomb encoded data.
This pattern (read-only base, writable subclass) was picked to maintain a *Buffer option that doesn't copy the source bits when parsing. ByteBuffer and Buffer both copy. I'm open to discussion on what the type relationship would be, though :)
Tests have been added to ensure the symmetric nature of read/write operations.
BUG=
R=bcornell@google.com, pthatcher@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/45259005
Cr-Commit-Position: refs/heads/master@{#9107}
diff --git a/webrtc/base/bitbuffer_unittest.cc b/webrtc/base/bitbuffer_unittest.cc
index 143ea6f..21cffd6 100644
--- a/webrtc/base/bitbuffer_unittest.cc
+++ b/webrtc/base/bitbuffer_unittest.cc
@@ -135,6 +135,39 @@
EXPECT_FALSE(buffer.ReadBits(&val, 1));
}
+TEST(BitBufferTest, SetOffsetValues) {
+ uint8 bytes[4] = {0};
+ BitBufferWriter buffer(bytes, 4);
+
+ size_t byte_offset, bit_offset;
+ // Bit offsets are [0,7].
+ EXPECT_TRUE(buffer.Seek(0, 0));
+ EXPECT_TRUE(buffer.Seek(0, 7));
+ buffer.GetCurrentOffset(&byte_offset, &bit_offset);
+ EXPECT_EQ(0u, byte_offset);
+ EXPECT_EQ(7u, bit_offset);
+ EXPECT_FALSE(buffer.Seek(0, 8));
+ buffer.GetCurrentOffset(&byte_offset, &bit_offset);
+ EXPECT_EQ(0u, byte_offset);
+ EXPECT_EQ(7u, bit_offset);
+ // Byte offsets are [0,length]. At byte offset length, the bit offset must be
+ // 0.
+ EXPECT_TRUE(buffer.Seek(0, 0));
+ EXPECT_TRUE(buffer.Seek(2, 4));
+ buffer.GetCurrentOffset(&byte_offset, &bit_offset);
+ EXPECT_EQ(2u, byte_offset);
+ EXPECT_EQ(4u, bit_offset);
+ EXPECT_TRUE(buffer.Seek(4, 0));
+ EXPECT_FALSE(buffer.Seek(5, 0));
+ buffer.GetCurrentOffset(&byte_offset, &bit_offset);
+ EXPECT_EQ(4u, byte_offset);
+ EXPECT_EQ(0u, bit_offset);
+ EXPECT_FALSE(buffer.Seek(4, 1));
+
+ // Passing a NULL out parameter is death.
+ EXPECT_DEATH(buffer.GetCurrentOffset(&byte_offset, NULL), "");
+}
+
uint64 GolombEncoded(uint32 val) {
val++;
uint32 bit_counter = val;
@@ -146,19 +179,23 @@
return static_cast<uint64>(val) << (64 - (bit_count * 2 - 1));
}
-TEST(BitBufferTest, GolombString) {
- char test_string[] = "my precious";
- for (size_t i = 0; i < ARRAY_SIZE(test_string); ++i) {
- uint64 encoded_val = GolombEncoded(test_string[i]);
- // Use ByteBuffer to convert to bytes, to account for endianness (BitBuffer
- // requires network order).
- ByteBuffer byteBuffer;
+TEST(BitBufferTest, GolombUint32Values) {
+ ByteBuffer byteBuffer;
+ byteBuffer.Resize(16);
+ BitBuffer buffer(reinterpret_cast<const uint8*>(byteBuffer.Data()),
+ byteBuffer.Capacity());
+ // Test over the uint32 range with a large enough step that the test doesn't
+ // take forever. Around 20,000 iterations should do.
+ const int kStep = std::numeric_limits<uint32>::max() / 20000;
+ for (uint32 i = 0; i < std::numeric_limits<uint32>::max() - kStep;
+ i += kStep) {
+ uint64 encoded_val = GolombEncoded(i);
+ byteBuffer.Clear();
byteBuffer.WriteUInt64(encoded_val);
- BitBuffer buffer(reinterpret_cast<const uint8*>(byteBuffer.Data()),
- byteBuffer.Length());
uint32 decoded_val;
+ EXPECT_TRUE(buffer.Seek(0, 0));
EXPECT_TRUE(buffer.ReadExponentialGolomb(&decoded_val));
- EXPECT_EQ(test_string[i], static_cast<char>(decoded_val));
+ EXPECT_EQ(i, decoded_val);
}
}
@@ -180,4 +217,87 @@
EXPECT_EQ(0x01FEu, decoded_val);
}
+TEST(BitBufferWriterTest, SymmetricReadWrite) {
+ uint8 bytes[16] = {0};
+ BitBufferWriter buffer(bytes, 4);
+
+ // Write some bit data at various sizes.
+ EXPECT_TRUE(buffer.WriteBits(0x2u, 3));
+ EXPECT_TRUE(buffer.WriteBits(0x1u, 2));
+ EXPECT_TRUE(buffer.WriteBits(0x53u, 7));
+ EXPECT_TRUE(buffer.WriteBits(0x0u, 2));
+ EXPECT_TRUE(buffer.WriteBits(0x1u, 1));
+ EXPECT_TRUE(buffer.WriteBits(0x1ABCDu, 17));
+ // That should be all that fits in the buffer.
+ EXPECT_FALSE(buffer.WriteBits(1, 1));
+
+ EXPECT_TRUE(buffer.Seek(0, 0));
+ uint32 val;
+ EXPECT_TRUE(buffer.ReadBits(&val, 3));
+ EXPECT_EQ(0x2u, val);
+ EXPECT_TRUE(buffer.ReadBits(&val, 2));
+ EXPECT_EQ(0x1u, val);
+ EXPECT_TRUE(buffer.ReadBits(&val, 7));
+ EXPECT_EQ(0x53u, val);
+ EXPECT_TRUE(buffer.ReadBits(&val, 2));
+ EXPECT_EQ(0x0u, val);
+ EXPECT_TRUE(buffer.ReadBits(&val, 1));
+ EXPECT_EQ(0x1u, val);
+ EXPECT_TRUE(buffer.ReadBits(&val, 17));
+ EXPECT_EQ(0x1ABCDu, val);
+ // And there should be nothing left.
+ EXPECT_FALSE(buffer.ReadBits(&val, 1));
+}
+
+TEST(BitBufferWriterTest, SymmetricBytesMisaligned) {
+ uint8 bytes[16] = {0};
+ BitBufferWriter buffer(bytes, 16);
+
+ // Offset 3, to get things misaligned.
+ EXPECT_TRUE(buffer.ConsumeBits(3));
+ EXPECT_TRUE(buffer.WriteUInt8(0x12u));
+ EXPECT_TRUE(buffer.WriteUInt16(0x3456u));
+ EXPECT_TRUE(buffer.WriteUInt32(0x789ABCDEu));
+
+ buffer.Seek(0, 3);
+ uint8 val8;
+ uint16 val16;
+ uint32 val32;
+ EXPECT_TRUE(buffer.ReadUInt8(&val8));
+ EXPECT_EQ(0x12u, val8);
+ EXPECT_TRUE(buffer.ReadUInt16(&val16));
+ EXPECT_EQ(0x3456u, val16);
+ EXPECT_TRUE(buffer.ReadUInt32(&val32));
+ EXPECT_EQ(0x789ABCDEu, val32);
+}
+
+TEST(BitBufferWriterTest, SymmetricGolomb) {
+ char test_string[] = "my precious";
+ uint8 bytes[64] = {0};
+ BitBufferWriter buffer(bytes, 64);
+ for (size_t i = 0; i < ARRAY_SIZE(test_string); ++i) {
+ EXPECT_TRUE(buffer.WriteExponentialGolomb(test_string[i]));
+ }
+ buffer.Seek(0, 0);
+ for (size_t i = 0; i < ARRAY_SIZE(test_string); ++i) {
+ uint32 val;
+ EXPECT_TRUE(buffer.ReadExponentialGolomb(&val));
+ EXPECT_LE(val, std::numeric_limits<uint8>::max());
+ EXPECT_EQ(test_string[i], static_cast<char>(val));
+ }
+}
+
+TEST(BitBufferWriterTest, WriteClearsBits) {
+ uint8 bytes[] = {0xFF, 0xFF};
+ BitBufferWriter buffer(bytes, 2);
+ EXPECT_TRUE(buffer.ConsumeBits(3));
+ EXPECT_TRUE(buffer.WriteBits(0, 1));
+ EXPECT_EQ(0xEFu, bytes[0]);
+ EXPECT_TRUE(buffer.WriteBits(0, 3));
+ EXPECT_EQ(0xE1u, bytes[0]);
+ EXPECT_TRUE(buffer.WriteBits(0, 2));
+ EXPECT_EQ(0xE0u, bytes[0]);
+ EXPECT_EQ(0x7F, bytes[1]);
+}
+
} // namespace rtc