blob: d1304c7a3fb9ac2fb4875ec1d5facfddba7a1efc [file] [log] [blame]
Brian Silverman41709732019-02-09 20:53:08 -08001#ifndef Y2019_JEVOIS_COBS_H_
2#define Y2019_JEVOIS_COBS_H_
3
4#include <stdint.h>
5
6#include <array>
7
8#include "aos/logging/logging.h"
9#include "third_party/GSL/include/gsl/gsl"
10
11// This file contains code for encoding and decoding Consistent Overhead Byte
12// Stuffing data. <http://www.stuartcheshire.org/papers/cobsforton.pdf> has
13// details on what this entails and why it's a good idea.
14
15namespace frc971 {
16namespace jevois {
17
18constexpr size_t CobsMaxEncodedSize(size_t decoded_size) {
19 return decoded_size + ((decoded_size + 253) / 254);
20}
21
22// Encodes some data using COBS.
23// input is the data to encode. Its size may be at most max_decoded_size.
24// output_buffer is where to store the result.
25// Returns a span in output_buffer which has no 0 bytes.
26template <size_t max_decoded_size>
27gsl::span<char> CobsEncode(
28 gsl::span<const char> input,
29 std::array<char, CobsMaxEncodedSize(max_decoded_size)> *output_buffer);
30
31// Decodes some COBS-encoded data.
32// input is the data to decide. Its size may be at most
33// CobsMaxEncodedSize(max_decoded_size), and it may not have any 0 bytes.
34// output_buffer is where to store the result.
35// Returns a span in output_buffer.
36// If the input data is invalid, this will simply stop when either the input or
37// output buffer is exhausted and return the result.
38template <size_t max_decoded_size>
39gsl::span<char> CobsDecode(gsl::span<const char> input,
40 std::array<char, max_decoded_size> *output_buffer);
41
42template <size_t max_decoded_size>
43gsl::span<char> CobsEncode(
44 gsl::span<const char> input,
45 std::array<char, CobsMaxEncodedSize(max_decoded_size)> *output_buffer) {
46 static_assert(max_decoded_size > 0, "Empty buffers not supported");
47 CHECK_LE(static_cast<size_t>(input.size()), max_decoded_size);
48 auto input_pointer = input.begin();
49 auto output_pointer = output_buffer->begin();
50 auto code_pointer = output_pointer;
51 ++output_pointer;
52 uint8_t code = 1;
53 while (input_pointer < input.end()) {
54 CHECK(output_pointer < output_buffer->end());
55 if (*input_pointer == 0u) {
56 *code_pointer = code;
57 code_pointer = output_pointer;
58 ++output_pointer;
59 code = 1;
60 } else {
61 *output_pointer = *input_pointer;
62 ++output_pointer;
63 ++code;
64 if (code == 0xFFu) {
65 *code_pointer = 0xFF;
66 code_pointer = output_pointer;
67 ++output_pointer;
68 code = 1;
69 }
70 }
71 ++input_pointer;
72 }
73 *code_pointer = code;
74 CHECK(output_pointer <= output_buffer->end());
75 return gsl::span<char>(*output_buffer)
76 .subspan(0, output_pointer - output_buffer->begin());
77}
78
79template <size_t max_decoded_size>
80gsl::span<char> CobsDecode(gsl::span<const char> input,
81 std::array<char, max_decoded_size> *output_buffer) {
82 static_assert(max_decoded_size > 0, "Empty buffers not supported");
83 CHECK_LE(static_cast<size_t>(input.size()),
84 CobsMaxEncodedSize(max_decoded_size));
85 auto input_pointer = input.begin();
86 auto output_pointer = output_buffer->begin();
87 while (input_pointer < input.end()) {
88 const uint8_t code = *input_pointer;
89 ++input_pointer;
90 for (uint8_t i = 1; i < code; ++i) {
91 if (input_pointer == input.end()) {
92 break;
93 }
94 if (output_pointer == output_buffer->end()) {
95 return gsl::span<char>(*output_buffer);
96 }
97 *output_pointer = *input_pointer;
98 ++output_pointer;
99 ++input_pointer;
100 }
101 if (output_pointer == output_buffer->end()) {
102 return gsl::span<char>(*output_buffer);
103 }
104 if (code < 0xFFu) {
105 *output_pointer = 0;
106 ++output_pointer;
107 }
108 }
109 return gsl::span<char>(*output_buffer)
110 .subspan(0, output_pointer - output_buffer->begin() - 1);
111}
112
113} // namespace jevois
114} // namespace frc971
115
116#endif // Y2019_JEVOIS_COBS_H_