pw_varint: C support
This change exposes a subset of the pw::varint API for use in C code.
Change-Id: I7db2dd1d2622711785e23c8534de6119301c57c3
diff --git a/pw_varint/BUILD b/pw_varint/BUILD
index abfa158..78bc286 100644
--- a/pw_varint/BUILD
+++ b/pw_varint/BUILD
@@ -1,4 +1,4 @@
-# Copyright 2019 The Pigweed Authors
+# Copyright 2020 The Pigweed Authors
#
# 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
@@ -38,6 +38,9 @@
pw_cc_test(
name = "varint_test",
- srcs = ["varint_test.cc"],
+ srcs = [
+ "varint_test.c",
+ "varint_test.cc",
+ ],
deps = ["//pw_varint"],
)
diff --git a/pw_varint/BUILD.gn b/pw_varint/BUILD.gn
index 80af019..cb46782 100644
--- a/pw_varint/BUILD.gn
+++ b/pw_varint/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2019 The Pigweed Authors
+# Copyright 2020 The Pigweed Authors
#
# 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
@@ -44,6 +44,7 @@
":pw_varint",
]
sources = [
+ "varint_test.c",
"varint_test.cc",
]
}
diff --git a/pw_varint/public/pw_varint/varint.h b/pw_varint/public/pw_varint/varint.h
index eab1b59..d0dc830 100644
--- a/pw_varint/public/pw_varint/varint.h
+++ b/pw_varint/public/pw_varint/varint.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Pigweed Authors
+// Copyright 2020 The Pigweed Authors
//
// 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
@@ -13,8 +13,27 @@
// the License.
#pragma once
-#include <cstddef>
-#include <cstdint>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Expose a subset of the varint API for use in C code.
+
+size_t pw_VarintEncode(uint64_t integer, void* output, size_t output_size);
+size_t pw_VarintZigZagEncode(int64_t integer, void* output, size_t output_size);
+
+size_t pw_VarintDecode(const void* input, size_t input_size, uint64_t* output);
+size_t pw_VarintZigZagDecode(const void* input,
+ size_t input_size,
+ int64_t* output);
+
+#ifdef __cplusplus
+
+} // extern "C"
+
#include <type_traits>
#include "pw_span/span.h"
@@ -51,8 +70,10 @@
}
// Encodes a uint64_t with Little-Endian Base 128 (LEB128) encoding.
-size_t EncodeLittleEndianBase128(uint64_t integer,
- const span<std::byte>& output);
+inline size_t EncodeLittleEndianBase128(uint64_t integer,
+ const span<std::byte>& output) {
+ return pw_VarintEncode(integer, output.data(), output.size());
+}
// Encodes the provided integer using a variable-length encoding and returns the
// number of bytes written.
@@ -67,9 +88,9 @@
template <typename T>
size_t Encode(T integer, const span<std::byte>& output) {
if constexpr (std::is_signed<T>()) {
- return EncodeLittleEndianBase128(ZigZagEncode(integer), output);
+ return pw_VarintZigZagEncode(integer, output.data(), output.size());
} else {
- return EncodeLittleEndianBase128(integer, output);
+ return pw_VarintEncode(integer, output.data(), output.size());
}
}
@@ -93,7 +114,14 @@
// data = data.subspan(bytes)
// }
//
-size_t Decode(const span<const std::byte>& input, int64_t* value);
-size_t Decode(const span<const std::byte>& input, uint64_t* value);
+inline size_t Decode(const span<const std::byte>& input, int64_t* value) {
+ return pw_VarintZigZagDecode(input.data(), input.size(), value);
+}
+
+inline size_t Decode(const span<const std::byte>& input, uint64_t* value) {
+ return pw_VarintDecode(input.data(), input.size(), value);
+}
} // namespace pw::varint
+
+#endif // __cplusplus
diff --git a/pw_varint/varint.cc b/pw_varint/varint.cc
index e9c5199..006952c 100644
--- a/pw_varint/varint.cc
+++ b/pw_varint/varint.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Pigweed Authors
+// Copyright 2020 The Pigweed Authors
//
// 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
@@ -18,35 +18,41 @@
namespace pw::varint {
-size_t EncodeLittleEndianBase128(uint64_t integer,
- const span<std::byte>& output) {
+extern "C" size_t pw_VarintEncode(uint64_t integer,
+ void* output,
+ size_t output_size) {
size_t written = 0;
+ std::byte* buffer = static_cast<std::byte*>(output);
+
do {
- if (written >= output.size()) {
+ if (written >= output_size) {
return 0;
}
// Grab 7 bits; the eighth bit is set to 1 to indicate more data coming.
- output[written++] = static_cast<std::byte>(integer) | std::byte{0x80};
+ buffer[written++] = static_cast<std::byte>(integer) | std::byte{0x80};
integer >>= 7;
} while (integer != 0u);
- output[written - 1] &= std::byte{0x7f}; // clear the top bit of the last byte
+ buffer[written - 1] &= std::byte{0x7f}; // clear the top bit of the last byte
return written;
}
-size_t Decode(const span<const std::byte>& input, int64_t* value) {
- const size_t bytes = Decode(input, reinterpret_cast<uint64_t*>(value));
- *value = ZigZagDecode(static_cast<uint64_t>(*value));
- return bytes;
+extern "C" size_t pw_VarintZigZagEncode(int64_t integer,
+ void* output,
+ size_t output_size) {
+ return pw_VarintEncode(ZigZagEncode(integer), output, output_size);
}
-size_t Decode(const span<const std::byte>& input, uint64_t* value) {
+extern "C" size_t pw_VarintDecode(const void* input,
+ size_t input_size,
+ uint64_t* output) {
uint64_t decoded_value = 0;
uint_fast8_t count = 0;
+ const std::byte* buffer = static_cast<const std::byte*>(input);
// The largest 64-bit ints require 10 B.
- const size_t max_count = std::min(kMaxVarintSizeBytes, input.size());
+ const size_t max_count = std::min(kMaxVarintSizeBytes, input_size);
while (true) {
if (count >= max_count) {
@@ -54,17 +60,26 @@
}
// Add the bottom seven bits of the next byte to the result.
- decoded_value |= static_cast<uint64_t>(input[count] & std::byte{0x7f})
+ decoded_value |= static_cast<uint64_t>(buffer[count] & std::byte{0x7f})
<< (7 * count);
// Stop decoding if the top bit is not set.
- if ((input[count++] & std::byte{0x80}) == std::byte{0}) {
+ if ((buffer[count++] & std::byte{0x80}) == std::byte{0}) {
break;
}
}
- *value = decoded_value;
+ *output = decoded_value;
return count;
}
+extern "C" size_t pw_VarintZigZagDecode(const void* input,
+ size_t input_size,
+ int64_t* output) {
+ uint64_t value = 0;
+ size_t bytes = pw_VarintDecode(input, input_size, &value);
+ *output = ZigZagDecode(value);
+ return bytes;
+}
+
} // namespace pw::varint
diff --git a/pw_varint/varint_test.c b/pw_varint/varint_test.c
new file mode 100644
index 0000000..e0ee8db
--- /dev/null
+++ b/pw_varint/varint_test.c
@@ -0,0 +1,40 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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
+//
+// https://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.
+
+// These tests call the pw_varint module API from C. The return values are
+// checked in the main C++ tests.
+
+#include "pw_varint/varint.h"
+
+#include <stddef.h>
+
+size_t pw_VarintCallEncode(uint64_t integer, void* output, size_t output_size) {
+ return pw_VarintEncode(integer, output, output_size);
+}
+
+size_t pw_VarintCallZigZagEncode(int64_t integer,
+ void* output,
+ size_t output_size) {
+ return pw_VarintZigZagEncode(integer, output, output_size);
+}
+
+size_t pw_VarintCallDecode(void* input, size_t input_size, uint64_t* output) {
+ return pw_VarintDecode(input, input_size, output);
+}
+
+size_t pw_VarintCallZigZagDecode(void* input,
+ size_t input_size,
+ int64_t* output) {
+ return pw_VarintZigZagDecode(input, input_size, output);
+}
diff --git a/pw_varint/varint_test.cc b/pw_varint/varint_test.cc
index c50df32..dc03f31 100644
--- a/pw_varint/varint_test.cc
+++ b/pw_varint/varint_test.cc
@@ -24,6 +24,20 @@
namespace pw::varint {
namespace {
+extern "C" {
+
+// Functions defined in varint_test.c which call the varint API from C.
+size_t pw_VarintCallEncode(uint64_t integer, void* output, size_t output_size);
+size_t pw_VarintCallZigZagEncode(int64_t integer,
+ void* output,
+ size_t output_size);
+size_t pw_VarintCallDecode(void* input, size_t input_size, uint64_t* output);
+size_t pw_VarintCallZigZagDecode(void* input,
+ size_t input_size,
+ int64_t* output);
+
+} // extern "C"
+
class Varint : public ::testing::Test {
protected:
Varint()
@@ -49,6 +63,15 @@
EXPECT_EQ(std::byte{2}, buffer_[0]);
}
+TEST_F(Varint, EncodeSizeUnsigned32_SmallSingleByte_C) {
+ ASSERT_EQ(1u, pw_VarintCallEncode(UINT32_C(0), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{0}, buffer_[0]);
+ ASSERT_EQ(1u, pw_VarintCallEncode(UINT32_C(1), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{1}, buffer_[0]);
+ ASSERT_EQ(1u, pw_VarintCallEncode(UINT32_C(2), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{2}, buffer_[0]);
+}
+
TEST_F(Varint, EncodeSizeUnsigned32_LargeSingleByte) {
ASSERT_EQ(1u, Encode(UINT32_C(63), buffer_));
EXPECT_EQ(std::byte{63}, buffer_[0]);
@@ -60,6 +83,17 @@
EXPECT_EQ(std::byte{127}, buffer_[0]);
}
+TEST_F(Varint, EncodeSizeUnsigned32_LargeSingleByte_C) {
+ ASSERT_EQ(1u, pw_VarintCallEncode(UINT32_C(63), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{63}, buffer_[0]);
+ ASSERT_EQ(1u, pw_VarintCallEncode(UINT32_C(64), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{64}, buffer_[0]);
+ ASSERT_EQ(1u, pw_VarintCallEncode(UINT32_C(126), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{126}, buffer_[0]);
+ ASSERT_EQ(1u, pw_VarintCallEncode(UINT32_C(127), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{127}, buffer_[0]);
+}
+
TEST_F(Varint, EncodeSizeUnsigned32_MultiByte) {
ASSERT_EQ(2u, Encode(UINT32_C(128), buffer_));
EXPECT_EQ(std::memcmp("\x80\x01", buffer_, 2), 0);
@@ -73,6 +107,25 @@
EXPECT_EQ(std::memcmp("\xff\xff\xff\xff\x0f", buffer_, 5), 0);
}
+TEST_F(Varint, EncodeSizeUnsigned32_MultiByte_C) {
+ ASSERT_EQ(2u, pw_VarintCallEncode(UINT32_C(128), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::memcmp("\x80\x01", buffer_, 2), 0);
+ ASSERT_EQ(2u, pw_VarintCallEncode(UINT32_C(129), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::memcmp("\x81\x01", buffer_, 2), 0);
+
+ ASSERT_EQ(
+ 5u,
+ pw_VarintCallEncode(
+ std::numeric_limits<uint32_t>::max() - 1, buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::memcmp("\xfe\xff\xff\xff\x0f", buffer_, 5), 0);
+
+ ASSERT_EQ(
+ 5u,
+ pw_VarintCallEncode(
+ std::numeric_limits<uint32_t>::max(), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::memcmp("\xff\xff\xff\xff\x0f", buffer_, 5), 0);
+}
+
TEST_F(Varint, EncodeSizeSigned32_SmallSingleByte) {
ASSERT_EQ(1u, Encode(INT32_C(0), buffer_));
EXPECT_EQ(std::byte{0}, buffer_[0]);
@@ -86,6 +139,24 @@
EXPECT_EQ(std::byte{4}, buffer_[0]);
}
+TEST_F(Varint, EncodeSizeSigned32_SmallSingleByte_C) {
+ ASSERT_EQ(1u,
+ pw_VarintCallZigZagEncode(INT32_C(0), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{0}, buffer_[0]);
+ ASSERT_EQ(1u,
+ pw_VarintCallZigZagEncode(INT32_C(-1), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{1}, buffer_[0]);
+ ASSERT_EQ(1u,
+ pw_VarintCallZigZagEncode(INT32_C(1), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{2}, buffer_[0]);
+ ASSERT_EQ(1u,
+ pw_VarintCallZigZagEncode(INT32_C(-2), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{3}, buffer_[0]);
+ ASSERT_EQ(1u,
+ pw_VarintCallZigZagEncode(INT32_C(2), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{4}, buffer_[0]);
+}
+
TEST_F(Varint, EncodeSizeSigned32_LargeSingleByte) {
ASSERT_EQ(1u, Encode(INT32_C(-63), buffer_));
EXPECT_EQ(std::byte{125}, buffer_[0]);
@@ -95,6 +166,18 @@
EXPECT_EQ(std::byte{127}, buffer_[0]);
}
+TEST_F(Varint, EncodeSizeSigned32_LargeSingleByte_C) {
+ ASSERT_EQ(1u,
+ pw_VarintCallZigZagEncode(INT32_C(-63), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{125}, buffer_[0]);
+ ASSERT_EQ(1u,
+ pw_VarintCallZigZagEncode(INT32_C(63), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{126}, buffer_[0]);
+ ASSERT_EQ(1u,
+ pw_VarintCallZigZagEncode(INT32_C(-64), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{127}, buffer_[0]);
+}
+
TEST_F(Varint, EncodeSizeSigned32_MultiByte) {
ASSERT_EQ(2u, Encode(INT32_C(64), buffer_));
EXPECT_EQ(std::memcmp("\x80\x01", buffer_, 2), 0);
@@ -110,6 +193,28 @@
EXPECT_EQ(std::memcmp("\xfe\xff\xff\xff\x0f", buffer_, 5), 0);
}
+TEST_F(Varint, EncodeSizeSigned32_MultiByte_C) {
+ ASSERT_EQ(2u,
+ pw_VarintCallZigZagEncode(INT32_C(64), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::memcmp("\x80\x01", buffer_, 2), 0);
+ ASSERT_EQ(2u,
+ pw_VarintCallZigZagEncode(INT32_C(-65), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::memcmp("\x81\x01", buffer_, 2), 0);
+ ASSERT_EQ(2u,
+ pw_VarintCallZigZagEncode(INT32_C(65), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::memcmp("\x82\x01", buffer_, 2), 0);
+
+ ASSERT_EQ(5u,
+ pw_VarintCallZigZagEncode(
+ std::numeric_limits<int32_t>::min(), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::memcmp("\xff\xff\xff\xff\x0f", buffer_, 5), 0);
+
+ ASSERT_EQ(5u,
+ pw_VarintCallZigZagEncode(
+ std::numeric_limits<int32_t>::max(), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::memcmp("\xfe\xff\xff\xff\x0f", buffer_, 5), 0);
+}
+
TEST_F(Varint, EncodeSizeUnsigned64_SmallSingleByte) {
ASSERT_EQ(1u, Encode(UINT64_C(0), buffer_));
EXPECT_EQ(std::byte{0}, buffer_[0]);
@@ -119,6 +224,15 @@
EXPECT_EQ(std::byte{2}, buffer_[0]);
}
+TEST_F(Varint, EncodeSizeUnsigned64_SmallSingleByte_C) {
+ ASSERT_EQ(1u, pw_VarintCallEncode(UINT64_C(0), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{0}, buffer_[0]);
+ ASSERT_EQ(1u, pw_VarintCallEncode(UINT64_C(1), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{1}, buffer_[0]);
+ ASSERT_EQ(1u, pw_VarintCallEncode(UINT64_C(2), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{2}, buffer_[0]);
+}
+
TEST_F(Varint, EncodeSizeUnsigned64_LargeSingleByte) {
ASSERT_EQ(1u, Encode(UINT64_C(63), buffer_));
EXPECT_EQ(std::byte{63}, buffer_[0]);
@@ -130,6 +244,17 @@
EXPECT_EQ(std::byte{127}, buffer_[0]);
}
+TEST_F(Varint, EncodeSizeUnsigned64_LargeSingleByte_C) {
+ ASSERT_EQ(1u, pw_VarintCallEncode(UINT64_C(63), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{63}, buffer_[0]);
+ ASSERT_EQ(1u, pw_VarintCallEncode(UINT64_C(64), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{64}, buffer_[0]);
+ ASSERT_EQ(1u, pw_VarintCallEncode(UINT64_C(126), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{126}, buffer_[0]);
+ ASSERT_EQ(1u, pw_VarintCallEncode(UINT64_C(127), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{127}, buffer_[0]);
+}
+
TEST_F(Varint, EncodeSizeUnsigned64_MultiByte) {
ASSERT_EQ(2u, Encode(UINT64_C(128), buffer_));
EXPECT_EQ(std::memcmp("\x80\x01", buffer_, 2), 0);
@@ -151,6 +276,39 @@
std::memcmp("\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01", buffer_, 10), 0);
}
+TEST_F(Varint, EncodeSizeUnsigned64_MultiByte_C) {
+ ASSERT_EQ(2u, pw_VarintCallEncode(UINT64_C(128), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::memcmp("\x80\x01", buffer_, 2), 0);
+ ASSERT_EQ(2u, pw_VarintCallEncode(UINT64_C(129), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::memcmp("\x81\x01", buffer_, 2), 0);
+
+ ASSERT_EQ(
+ 5u,
+ pw_VarintCallEncode(
+ std::numeric_limits<uint32_t>::max() - 1, buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::memcmp("\xfe\xff\xff\xff\x0f", buffer_, 5), 0);
+
+ ASSERT_EQ(
+ 5u,
+ pw_VarintCallEncode(
+ std::numeric_limits<uint32_t>::max(), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::memcmp("\xff\xff\xff\xff\x0f", buffer_, 5), 0);
+
+ ASSERT_EQ(
+ 10u,
+ pw_VarintCallEncode(
+ std::numeric_limits<uint64_t>::max() - 1, buffer_, sizeof(buffer_)));
+ EXPECT_EQ(
+ std::memcmp("\xfe\xff\xff\xff\xff\xff\xff\xff\xff\x01", buffer_, 10), 0);
+
+ ASSERT_EQ(
+ 10u,
+ pw_VarintCallEncode(
+ std::numeric_limits<uint64_t>::max(), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(
+ std::memcmp("\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01", buffer_, 10), 0);
+}
+
TEST_F(Varint, EncodeSizeSigned64_SmallSingleByte) {
ASSERT_EQ(1u, Encode(INT64_C(0), buffer_));
EXPECT_EQ(std::byte{0}, buffer_[0]);
@@ -164,6 +322,24 @@
EXPECT_EQ(std::byte{4}, buffer_[0]);
}
+TEST_F(Varint, EncodeSizeSigned64_SmallSingleByte_C) {
+ ASSERT_EQ(1u,
+ pw_VarintCallZigZagEncode(INT64_C(0), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{0}, buffer_[0]);
+ ASSERT_EQ(1u,
+ pw_VarintCallZigZagEncode(INT64_C(-1), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{1}, buffer_[0]);
+ ASSERT_EQ(1u,
+ pw_VarintCallZigZagEncode(INT64_C(1), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{2}, buffer_[0]);
+ ASSERT_EQ(1u,
+ pw_VarintCallZigZagEncode(INT64_C(-2), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{3}, buffer_[0]);
+ ASSERT_EQ(1u,
+ pw_VarintCallZigZagEncode(INT64_C(2), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{4}, buffer_[0]);
+}
+
TEST_F(Varint, EncodeSizeSigned64_LargeSingleByte) {
ASSERT_EQ(1u, Encode(INT64_C(-63), buffer_));
EXPECT_EQ(std::byte{125}, buffer_[0]);
@@ -173,6 +349,18 @@
EXPECT_EQ(std::byte{127}, buffer_[0]);
}
+TEST_F(Varint, EncodeSizeSigned64_LargeSingleByte_C) {
+ ASSERT_EQ(1u,
+ pw_VarintCallZigZagEncode(INT64_C(-63), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{125}, buffer_[0]);
+ ASSERT_EQ(1u,
+ pw_VarintCallZigZagEncode(INT64_C(63), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{126}, buffer_[0]);
+ ASSERT_EQ(1u,
+ pw_VarintCallZigZagEncode(INT64_C(-64), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::byte{127}, buffer_[0]);
+}
+
TEST_F(Varint, EncodeSizeSigned64_MultiByte) {
ASSERT_EQ(2u, Encode(INT64_C(64), buffer_));
EXPECT_EQ(std::memcmp("\x80\x01", buffer_, 2), 0);
@@ -200,6 +388,44 @@
std::memcmp("\xfe\xff\xff\xff\xff\xff\xff\xff\xff\x01", buffer_, 10), 0);
}
+TEST_F(Varint, EncodeSizeSigned64_MultiByte_C) {
+ ASSERT_EQ(2u,
+ pw_VarintCallZigZagEncode(INT64_C(64), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::memcmp("\x80\x01", buffer_, 2), 0);
+ ASSERT_EQ(2u,
+ pw_VarintCallZigZagEncode(INT64_C(-65), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::memcmp("\x81\x01", buffer_, 2), 0);
+ ASSERT_EQ(2u,
+ pw_VarintCallZigZagEncode(INT64_C(65), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(std::memcmp("\x82\x01", buffer_, 2), 0);
+
+ ASSERT_EQ(5u,
+ pw_VarintCallZigZagEncode(
+ static_cast<int64_t>(std::numeric_limits<int32_t>::min()),
+ buffer_,
+ sizeof(buffer_)));
+ EXPECT_EQ(std::memcmp("\xff\xff\xff\xff\x0f", buffer_, 5), 0);
+
+ ASSERT_EQ(5u,
+ pw_VarintCallZigZagEncode(
+ static_cast<int64_t>(std::numeric_limits<int32_t>::max()),
+ buffer_,
+ sizeof(buffer_)));
+ EXPECT_EQ(std::memcmp("\xfe\xff\xff\xff\x0f", buffer_, 5), 0);
+
+ ASSERT_EQ(10u,
+ pw_VarintCallZigZagEncode(
+ std::numeric_limits<int64_t>::min(), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(
+ std::memcmp("\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01", buffer_, 10), 0);
+
+ ASSERT_EQ(10u,
+ pw_VarintCallZigZagEncode(
+ std::numeric_limits<int64_t>::max(), buffer_, sizeof(buffer_)));
+ EXPECT_EQ(
+ std::memcmp("\xfe\xff\xff\xff\xff\xff\xff\xff\xff\x01", buffer_, 10), 0);
+}
+
TEST_F(Varint, EncodeDecodeSigned32) {
// Set the increment to 1 to test every number (this is slow)
static constexpr int kIncrement = 1'000'009;
@@ -222,6 +448,29 @@
}
}
+TEST_F(Varint, EncodeDecodeSigned32_C) {
+ // Set the increment to 1 to test every number (this is slow)
+ static constexpr int kIncrement = 1'000'009;
+
+ int32_t i = std::numeric_limits<int32_t>::min();
+ while (true) {
+ size_t encoded = pw_VarintCallZigZagEncode(i, buffer_, sizeof(buffer_));
+
+ int64_t result;
+ size_t decoded =
+ pw_VarintCallZigZagDecode(buffer_, sizeof(buffer_), &result);
+
+ EXPECT_EQ(encoded, decoded);
+ ASSERT_EQ(i, result);
+
+ if (i > std::numeric_limits<int32_t>::max() - kIncrement) {
+ break;
+ }
+
+ i += kIncrement;
+ }
+}
+
TEST_F(Varint, EncodeDecodeUnsigned32) {
// Set the increment to 1 to test every number (this is slow)
static constexpr int kIncrement = 1'000'009;
@@ -244,6 +493,28 @@
}
}
+TEST_F(Varint, EncodeDecodeUnsigned32_C) {
+ // Set the increment to 1 to test every number (this is slow)
+ static constexpr int kIncrement = 1'000'009;
+
+ uint32_t i = 0;
+ while (true) {
+ size_t encoded = pw_VarintCallEncode(i, buffer_, sizeof(buffer_));
+
+ uint64_t result;
+ size_t decoded = pw_VarintCallDecode(buffer_, sizeof(buffer_), &result);
+
+ EXPECT_EQ(encoded, decoded);
+ ASSERT_EQ(i, result);
+
+ if (i > std::numeric_limits<uint32_t>::max() - kIncrement) {
+ break;
+ }
+
+ i += kIncrement;
+ }
+}
+
template <size_t kStringSize>
auto MakeBuffer(const char (&data)[kStringSize]) {
constexpr size_t kSizeBytes = kStringSize - 1;
@@ -276,6 +547,40 @@
EXPECT_EQ(value, 2);
}
+TEST(VarintDecode, DecodeSigned64_SingleByte_C) {
+ int64_t value = -1234;
+
+ auto buffer = MakeBuffer("\x00");
+ EXPECT_EQ(pw_VarintCallZigZagDecode(buffer.data(), buffer.size(), &value),
+ 1u);
+ EXPECT_EQ(value, 0);
+
+ buffer = MakeBuffer("\x01");
+ EXPECT_EQ(pw_VarintCallZigZagDecode(buffer.data(), buffer.size(), &value),
+ 1u);
+ EXPECT_EQ(value, -1);
+
+ buffer = MakeBuffer("\x02");
+ EXPECT_EQ(pw_VarintCallZigZagDecode(buffer.data(), buffer.size(), &value),
+ 1u);
+ EXPECT_EQ(value, 1);
+
+ buffer = MakeBuffer("\x03");
+ EXPECT_EQ(pw_VarintCallZigZagDecode(buffer.data(), buffer.size(), &value),
+ 1u);
+ EXPECT_EQ(value, -2);
+
+ buffer = MakeBuffer("\x04");
+ EXPECT_EQ(pw_VarintCallZigZagDecode(buffer.data(), buffer.size(), &value),
+ 1u);
+ EXPECT_EQ(value, 2);
+
+ buffer = MakeBuffer("\x04");
+ EXPECT_EQ(pw_VarintCallZigZagDecode(buffer.data(), buffer.size(), &value),
+ 1u);
+ EXPECT_EQ(value, 2);
+}
+
TEST(VarintDecode, DecodeSigned64_MultiByte) {
int64_t value = -1234;
@@ -305,6 +610,45 @@
EXPECT_EQ(value, std::numeric_limits<int64_t>::max());
}
+TEST(VarintDecode, DecodeSigned64_MultiByte_C) {
+ int64_t value = -1234;
+
+ auto buffer2 = MakeBuffer("\x80\x01");
+ EXPECT_EQ(pw_VarintCallZigZagDecode(buffer2.data(), buffer2.size(), &value),
+ 2u);
+ EXPECT_EQ(value, 64);
+
+ buffer2 = MakeBuffer("\x81\x01");
+ EXPECT_EQ(pw_VarintCallZigZagDecode(buffer2.data(), buffer2.size(), &value),
+ 2u);
+ EXPECT_EQ(value, -65);
+
+ buffer2 = MakeBuffer("\x82\x01");
+ EXPECT_EQ(pw_VarintCallZigZagDecode(buffer2.data(), buffer2.size(), &value),
+ 2u);
+ EXPECT_EQ(value, 65);
+
+ auto buffer4 = MakeBuffer("\xff\xff\xff\xff\x0f");
+ EXPECT_EQ(pw_VarintCallZigZagDecode(buffer4.data(), buffer4.size(), &value),
+ 5u);
+ EXPECT_EQ(value, std::numeric_limits<int32_t>::min());
+
+ buffer4 = MakeBuffer("\xfe\xff\xff\xff\x0f");
+ EXPECT_EQ(pw_VarintCallZigZagDecode(buffer4.data(), buffer4.size(), &value),
+ 5u);
+ EXPECT_EQ(value, std::numeric_limits<int32_t>::max());
+
+ auto buffer8 = MakeBuffer("\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01");
+ EXPECT_EQ(pw_VarintCallZigZagDecode(buffer8.data(), buffer8.size(), &value),
+ 10u);
+ EXPECT_EQ(value, std::numeric_limits<int64_t>::min());
+
+ buffer8 = MakeBuffer("\xfe\xff\xff\xff\xff\xff\xff\xff\xff\x01");
+ EXPECT_EQ(pw_VarintCallZigZagDecode(buffer8.data(), buffer8.size(), &value),
+ 10u);
+ EXPECT_EQ(value, std::numeric_limits<int64_t>::max());
+}
+
TEST(Varint, ZigZagEncode_Int8) {
EXPECT_EQ(ZigZagEncode(int8_t(0)), uint8_t(0));
EXPECT_EQ(ZigZagEncode(int8_t(-1)), uint8_t(1));