Add a consistent overhead byte stuffing implementation
I plan to use this for packing UART data, to allow 0 bytes to deliminate
frames.
Change-Id: Ie448946d7a05fcbf1f1b19ab6a5019db0e0337fe
diff --git a/y2019/jevois/cobs_test.cc b/y2019/jevois/cobs_test.cc
new file mode 100644
index 0000000..320bde3
--- /dev/null
+++ b/y2019/jevois/cobs_test.cc
@@ -0,0 +1,130 @@
+#include "y2019/jevois/cobs.h"
+
+#include "aos/testing/test_logging.h"
+#include "gtest/gtest.h"
+#include "third_party/GSL/include/gsl/gsl"
+
+namespace frc971 {
+namespace jevois {
+
+// Tests the size conversions for some known, simple values.
+TEST(CobsMaxEncodedSizeTest, Simple) {
+ EXPECT_EQ(0u, CobsMaxEncodedSize(0));
+ EXPECT_EQ(2u, CobsMaxEncodedSize(1));
+ EXPECT_EQ(3u, CobsMaxEncodedSize(2));
+
+ EXPECT_EQ(254u, CobsMaxEncodedSize(253));
+ EXPECT_EQ(255u, CobsMaxEncodedSize(254));
+ EXPECT_EQ(257u, CobsMaxEncodedSize(255));
+}
+
+class CobsTest : public ::testing::Test {
+ public:
+ CobsTest() { aos::testing::EnableTestLogging(); }
+
+ template <size_t min_buffer_size = 0, size_t number_elements>
+ void EncodeAndDecode(const char (&input_data)[number_elements]) {
+ static constexpr size_t input_size =
+ (min_buffer_size > number_elements) ? min_buffer_size : number_elements;
+ EncodeAndDecode<input_size>(gsl::span<const char>(input_data));
+ }
+
+ template <size_t max_decoded_size>
+ void EncodeAndDecode(const gsl::span<const char> decoded_input) {
+ std::array<char, CobsMaxEncodedSize(max_decoded_size)> encoded_buffer;
+ const auto encoded =
+ CobsEncode<max_decoded_size>(decoded_input, &encoded_buffer);
+ ASSERT_LE(encoded.size(), encoded_buffer.size());
+ ASSERT_EQ(encoded.data(), &encoded_buffer.front());
+
+ std::array<char, max_decoded_size> decoded_buffer;
+ const auto decoded = CobsDecode<max_decoded_size>(encoded, &decoded_buffer);
+ ASSERT_LE(decoded.size(), decoded_buffer.size());
+ ASSERT_EQ(decoded.data(), &decoded_buffer.front());
+ ASSERT_EQ(decoded.size(), decoded_input.size());
+ for (int i = 0; i < decoded.size(); ++i) {
+ EXPECT_EQ(decoded[i], decoded_input[i]);
+ }
+ }
+};
+
+// Tests various small buffers.
+TEST_F(CobsTest, Small) {
+ EncodeAndDecode<1>(std::array<char, 0>{});
+ EncodeAndDecode<5>(std::array<char, 0>{});
+ {
+ const char data[] = {0};
+ EncodeAndDecode(data);
+ }
+ {
+ const char data[] = {1};
+ EncodeAndDecode(data);
+ }
+ {
+ const char data[] = {static_cast<char>(254)};
+ EncodeAndDecode(data);
+ }
+ {
+ const char data[] = {static_cast<char>(255)};
+ EncodeAndDecode(data);
+ }
+ {
+ const char data[] = {0, 1};
+ EncodeAndDecode(data);
+ }
+ {
+ const char data[] = {0, static_cast<char>(254)};
+ EncodeAndDecode(data);
+ }
+ {
+ const char data[] = {0, static_cast<char>(255)};
+ EncodeAndDecode(data);
+ }
+ {
+ const char data[] = {0, 1, static_cast<char>(254)};
+ EncodeAndDecode(data);
+ }
+ {
+ const char data[] = {0, 1, static_cast<char>(255)};
+ EncodeAndDecode(data);
+ }
+ {
+ const char data[] = {static_cast<char>(254), 1};
+ EncodeAndDecode(data);
+ }
+ {
+ const char data[] = {static_cast<char>(255), 1};
+ EncodeAndDecode(data);
+ }
+ {
+ const char data[] = {static_cast<char>(254), 0};
+ EncodeAndDecode(data);
+ }
+ {
+ const char data[] = {static_cast<char>(255), 0};
+ EncodeAndDecode(data);
+ }
+}
+
+// Tests encoding arrays with approximately one full chunk. This exposes some
+// corner cases in the binary format.
+TEST_F(CobsTest, AroundOneChunk) {
+ char data[256];
+ for (size_t i = 0; i < sizeof(data); ++i) {
+ data[i] = (i * 7) & 0xFF;
+ }
+ const gsl::span<char> data_span = data;
+ for (int i = 253; i <= 256; ++i) {
+ EncodeAndDecode<256>(data_span.subspan(0, i));
+ }
+ for (int i = 253; i <= 255; ++i) {
+ EncodeAndDecode<255>(data_span.subspan(0, i));
+ }
+ for (int i = 253; i <= 254; ++i) {
+ EncodeAndDecode<254>(data_span.subspan(0, i));
+ }
+ EncodeAndDecode<253>(data_span.subspan(0, 253));
+}
+
+} // namespace jevois
+} // namespace frc971