/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* 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 <folly/GroupVarint.h>
#include <algorithm>
#include <cstdarg>
// On platforms where it's not supported, GroupVarint will be compiled out.
#if FOLLY_HAVE_GROUP_VARINT
#include <folly/portability/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_;
};
// 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;
// ssse3 decoding requires that the source buffer have length >= 17,
// so that it can read 128 bits from &start[1] via _mm_loadu_si128.
foundBytes.resize(std::max<size_t>(size + 4, 17UL));
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));
// clang-format off
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);
// clang-format on
}
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));
// clang-format off
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);
// clang-format on
}
TEST(GroupVarint, GroupVarintEncoder) {
using GroupVarint32Encoder = GroupVarintEncoder<uint32_t, StringAppender>;
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());
}
}
#endif