Pull from FB rev 63ce89e2f2301e6bba44a111cc7d4218022156f6
[folly.git] / folly / test / GroupVarintTest.cpp
diff --git a/folly/test/GroupVarintTest.cpp b/folly/test/GroupVarintTest.cpp
new file mode 100644 (file)
index 0000000..cdc693b
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2012 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdarg.h>
+#include "folly/GroupVarint.h"
+
+#include <gtest/gtest.h>
+
+using namespace folly;
+
+namespace {
+
+class StringAppender {
+ public:
+  /* implicit */ StringAppender(std::string& s) : s_(s) { }
+  void operator()(StringPiece sp) {
+    s_.append(sp.data(), sp.size());
+  }
+ private:
+  std::string& s_;
+};
+
+typedef GroupVarintEncoder<uint32_t, StringAppender> GroupVarint32Encoder;
+typedef GroupVarintEncoder<uint64_t, StringAppender> GroupVarint64Encoder;
+typedef GroupVarintDecoder<uint32_t> GroupVarint32Decoder;
+typedef GroupVarintDecoder<uint32_t> GroupVarint64Decoder;
+
+// Expected bytes follow, terminate with -1
+void testGroupVarint32(uint32_t a, uint32_t b, uint32_t c, uint32_t d, ...) {
+  va_list ap;
+  va_start(ap, d);
+  std::vector<char> expectedBytes;
+  int byte;
+  while ((byte = va_arg(ap, int)) != -1) {
+    expectedBytes.push_back(byte);
+  }
+  va_end(ap);
+
+  size_t size = GroupVarint32::size(a, b, c, d);
+  EXPECT_EQ(expectedBytes.size(), size);
+
+  std::vector<char> foundBytes;
+  foundBytes.resize(size + 4);
+  char* start = &(foundBytes.front());
+  char* p = GroupVarint32::encode(start, a, b, c, d);
+  EXPECT_EQ((void*)(start + size), (void*)p);
+
+  for (size_t i = 0; i < size; i++) {
+    EXPECT_EQ(0xff & expectedBytes[i], 0xff & foundBytes[i]);
+  }
+
+  // Test decoding
+  EXPECT_EQ(size, GroupVarint32::encodedSize(start));
+
+  uint32_t fa, fb, fc, fd;
+  const char* r = GroupVarint32::decode(start, &fa, &fb, &fc, &fd);
+  EXPECT_EQ((void*)(start + size), (void*)r);
+
+  EXPECT_EQ(a, fa);
+  EXPECT_EQ(b, fb);
+  EXPECT_EQ(c, fc);
+  EXPECT_EQ(d, fd);
+}
+
+void testGroupVarint64(uint64_t a, uint64_t b, uint64_t c, uint64_t d,
+                       uint64_t e, ...) {
+  va_list ap;
+  va_start(ap, e);
+  std::vector<char> expectedBytes;
+  int byte;
+  while ((byte = va_arg(ap, int)) != -1) {
+    expectedBytes.push_back(byte);
+  }
+  va_end(ap);
+
+  size_t size = GroupVarint64::size(a, b, c, d, e);
+  EXPECT_EQ(expectedBytes.size(), size);
+
+  std::vector<char> foundBytes;
+  foundBytes.resize(size + 8);
+  char* start = &(foundBytes.front());
+  char* p = GroupVarint64::encode(start, a, b, c, d, e);
+  EXPECT_EQ((void*)(start + size), (void*)p);
+
+  for (size_t i = 0; i < size; i++) {
+    EXPECT_EQ(0xff & expectedBytes[i], 0xff & foundBytes[i]);
+  }
+
+  // Test decoding
+  EXPECT_EQ(size, GroupVarint64::encodedSize(start));
+
+  uint64_t fa, fb, fc, fd, fe;
+  const char* r = GroupVarint64::decode(start, &fa, &fb, &fc, &fd, &fe);
+  EXPECT_EQ((void*)(start + size), (void*)r);
+
+  EXPECT_EQ(a, fa);
+  EXPECT_EQ(b, fb);
+  EXPECT_EQ(c, fc);
+  EXPECT_EQ(d, fd);
+  EXPECT_EQ(e, fe);
+}
+
+}  // namespace
+
+TEST(GroupVarint, GroupVarint32) {
+  EXPECT_EQ(0, GroupVarint32::maxSize(0));
+  EXPECT_EQ(5, GroupVarint32::maxSize(1));
+  EXPECT_EQ(9, GroupVarint32::maxSize(2));
+  EXPECT_EQ(13, GroupVarint32::maxSize(3));
+  EXPECT_EQ(17, GroupVarint32::maxSize(4));
+  EXPECT_EQ(22, GroupVarint32::maxSize(5));
+  EXPECT_EQ(26, GroupVarint32::maxSize(6));
+  testGroupVarint32(0, 0, 0, 0,
+                    0, 0, 0, 0, 0, -1);
+  testGroupVarint32(1, 2, 3, 4,
+                    0, 1, 2, 3, 4, -1);
+  testGroupVarint32(1 << 8, (2 << 16) + 3, (4 << 24) + (5 << 8) + 6, 7,
+                    0x39, 0, 1, 3, 0, 2, 6, 5, 0, 4, 7, -1);
+}
+
+TEST(GroupVarint, GroupVarint64) {
+  EXPECT_EQ(0, GroupVarint64::maxSize(0));
+  EXPECT_EQ(10, GroupVarint64::maxSize(1));
+  EXPECT_EQ(18, GroupVarint64::maxSize(2));
+  EXPECT_EQ(26, GroupVarint64::maxSize(3));
+  EXPECT_EQ(34, GroupVarint64::maxSize(4));
+  EXPECT_EQ(42, GroupVarint64::maxSize(5));
+  EXPECT_EQ(52, GroupVarint64::maxSize(6));
+  testGroupVarint64(0, 0, 0, 0, 0,
+                    0, 0, 0, 0, 0, 0, 0, -1);
+  testGroupVarint64(1, 2, 3, 4, 5,
+                    0, 0, 1, 2, 3, 4, 5, -1);
+  testGroupVarint64(1 << 8, (2 << 16) + 3, (4 << 24) + (5 << 8) + 6,
+                    (7ULL << 32) + (8 << 16),
+                    (9ULL << 56) + (10ULL << 40) + 11,
+                    0xd1, 0x78,
+                    0, 1,
+                    3, 0, 2,
+                    6, 5, 0, 4,
+                    0, 0, 8, 0, 7,
+                    11, 0, 0, 0, 0, 10, 0, 9,
+                    -1);
+}
+
+TEST(GroupVarint, GroupVarintEncoder) {
+  std::string s;
+  {
+    GroupVarint32Encoder gv(s);
+    gv.add(0);
+    gv.finish();
+  }
+  EXPECT_EQ(2, s.size());
+  EXPECT_EQ(std::string("\x00\x00", 2), s);
+  s.clear();
+  {
+    GroupVarint32Encoder gv(s);
+    gv.add(1);
+    gv.add(2);
+    gv.add(3);
+    gv.add(4);
+    gv.finish();
+  }
+  EXPECT_EQ(5, s.size());
+  EXPECT_EQ(std::string("\x00\x01\x02\x03\x04", 5), s);
+}
+
+
+TEST(GroupVarint, GroupVarintDecoder) {
+  // Make sure we don't read out of bounds
+  std::string padding(17, 'X');
+
+  {
+    std::string s("\x00\x00", 2);
+    s += padding;
+    StringPiece p(s.data(), 2);
+
+    GroupVarint32Decoder gv(p);
+    uint32_t v;
+    EXPECT_TRUE(gv.next(&v));
+    EXPECT_EQ(0, v);
+    EXPECT_FALSE(gv.next(&v));
+    EXPECT_TRUE(gv.rest().empty());
+  }
+
+  {
+    std::string s("\x00\x01\x02\x03\x04\x01\x02\x03\x04", 9);
+    s += padding;
+    StringPiece p(s.data(), 9);
+
+    GroupVarint32Decoder gv(p);
+    uint32_t v;
+    EXPECT_TRUE(gv.next(&v));
+    EXPECT_EQ(1, v);
+    EXPECT_TRUE(gv.next(&v));
+    EXPECT_EQ(2, v);
+    EXPECT_TRUE(gv.next(&v));
+    EXPECT_EQ(3, v);
+    EXPECT_TRUE(gv.next(&v));
+    EXPECT_EQ(4, v);
+    EXPECT_TRUE(gv.next(&v));
+    EXPECT_EQ(0x0302, v);
+    EXPECT_TRUE(gv.next(&v));
+    EXPECT_EQ(4, v);
+    EXPECT_FALSE(gv.next(&v));
+    EXPECT_TRUE(gv.rest().empty());
+  }
+
+  {
+    // Limit max count when reading a full block
+    std::string s("\x00\x01\x02\x03\x04\x01\x02\x03\x04", 9);
+    s += padding;
+    StringPiece p(s.data(), 9);
+
+    GroupVarint32Decoder gv(p, 3);
+    uint32_t v;
+    EXPECT_TRUE(gv.next(&v));
+    EXPECT_EQ(1, v);
+    EXPECT_TRUE(gv.next(&v));
+    EXPECT_EQ(2, v);
+    EXPECT_TRUE(gv.next(&v));
+    EXPECT_EQ(3, v);
+    EXPECT_FALSE(gv.next(&v));
+    EXPECT_EQ(std::string("\x04\x01\x02\x03\x04", 5), gv.rest().toString());
+  }
+
+  {
+    // Limit max count when reading a partial block
+    std::string s("\x00\x01\x02\x03\x04\x01\x02\x03\x04", 9);
+    s += padding;
+    StringPiece p(s.data(), 9);
+
+    GroupVarint32Decoder gv(p, 5);
+    uint32_t v;
+    EXPECT_TRUE(gv.next(&v));
+    EXPECT_EQ(1, v);
+    EXPECT_TRUE(gv.next(&v));
+    EXPECT_EQ(2, v);
+    EXPECT_TRUE(gv.next(&v));
+    EXPECT_EQ(3, v);
+    EXPECT_TRUE(gv.next(&v));
+    EXPECT_EQ(4, v);
+    EXPECT_TRUE(gv.next(&v));
+    EXPECT_EQ(0x0302, v);
+    EXPECT_FALSE(gv.next(&v));
+    EXPECT_EQ(std::string("\x04", 1), gv.rest().toString());
+  }
+}
+