blob: 112873d9eff48554f6e8fed1d374af58e40ff735 [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
Brian Silvermanc64372e2019-02-17 18:07:40 -08006#include <algorithm>
Brian Silverman41709732019-02-09 20:53:08 -08007#include <array>
8
Brian Silverman41709732019-02-09 20:53:08 -08009#include "third_party/GSL/include/gsl/gsl"
Brian Silvermanfdfb3132019-02-24 15:27:27 -080010#ifdef __linux__
11#include "aos/logging/logging.h"
12#else
13#define CHECK(...)
14#define CHECK_LE(...)
15#endif
Brian Silverman41709732019-02-09 20:53:08 -080016
17// This file contains code for encoding and decoding Consistent Overhead Byte
18// Stuffing data. <http://www.stuartcheshire.org/papers/cobsforton.pdf> has
19// details on what this entails and why it's a good idea.
20
21namespace frc971 {
22namespace jevois {
23
24constexpr size_t CobsMaxEncodedSize(size_t decoded_size) {
25 return decoded_size + ((decoded_size + 253) / 254);
26}
27
28// Encodes some data using COBS.
29// input is the data to encode. Its size may be at most max_decoded_size.
30// output_buffer is where to store the result.
31// Returns a span in output_buffer which has no 0 bytes.
32template <size_t max_decoded_size>
33gsl::span<char> CobsEncode(
34 gsl::span<const char> input,
35 std::array<char, CobsMaxEncodedSize(max_decoded_size)> *output_buffer);
36
37// Decodes some COBS-encoded data.
38// input is the data to decide. Its size may be at most
39// CobsMaxEncodedSize(max_decoded_size), and it may not have any 0 bytes.
40// output_buffer is where to store the result.
41// Returns a span in output_buffer.
42// If the input data is invalid, this will simply stop when either the input or
43// output buffer is exhausted and return the result.
44template <size_t max_decoded_size>
45gsl::span<char> CobsDecode(gsl::span<const char> input,
46 std::array<char, max_decoded_size> *output_buffer);
47
Brian Silvermanc64372e2019-02-17 18:07:40 -080048// Manages scanning a stream of bytes for 0s and exposing the resulting buffers.
49//
50// This will silently truncate packets longer than max_decoded_size, and ignore
51// empty packets.
52template <size_t max_decoded_size>
53class CobsPacketizer {
54 public:
55 CobsPacketizer() = default;
56 CobsPacketizer(const CobsPacketizer &) = delete;
57 CobsPacketizer &operator=(const CobsPacketizer &) = delete;
58
59 // Parses some new data. received_packet() will be filled out to the end of
60 // a packet if the end delimeters for any packets are present in new_data. If
61 // multiple end delimiters are present, received_packet() will be filled out
62 // to an arbitrary one of them.
63 void ParseData(gsl::span<const char> new_data);
64
65 // Returns the most-recently-parsed packet.
66 // If this is empty, it indicates no packet was received.
67 gsl::span<const char> received_packet() const { return complete_packet_; }
68 void clear_received_packet() { complete_packet_ = gsl::span<char>(); }
69
70 private:
71 using Buffer = std::array<char, CobsMaxEncodedSize(max_decoded_size)>;
72
73 void CopyData(gsl::span<const char> input) {
74 const size_t size = std::min(input.size(), remaining_active_.size());
75 for (size_t i = 0; i < size; ++i) {
76 remaining_active_[i] = input[i];
77 }
78 remaining_active_ = remaining_active_.subspan(size);
79 }
80
81 void FinishPacket() {
82 const Buffer &active_buffer = buffers_[active_index_];
83 complete_packet_ =
84 gsl::span<const char>(active_buffer)
85 .first(active_buffer.size() - remaining_active_.size());
86
87 active_index_ = 1 - active_index_;
88 remaining_active_ = buffers_[active_index_];
89 }
90
91 Buffer buffers_[2];
92 // The remaining space in the active buffer.
93 gsl::span<char> remaining_active_ = buffers_[0];
94 // The last complete packet we parsed.
95 gsl::span<const char> complete_packet_;
96 int active_index_ = 0;
97};
98
Brian Silverman41709732019-02-09 20:53:08 -080099template <size_t max_decoded_size>
100gsl::span<char> CobsEncode(
101 gsl::span<const char> input,
102 std::array<char, CobsMaxEncodedSize(max_decoded_size)> *output_buffer) {
103 static_assert(max_decoded_size > 0, "Empty buffers not supported");
104 CHECK_LE(static_cast<size_t>(input.size()), max_decoded_size);
105 auto input_pointer = input.begin();
106 auto output_pointer = output_buffer->begin();
107 auto code_pointer = output_pointer;
108 ++output_pointer;
109 uint8_t code = 1;
110 while (input_pointer < input.end()) {
111 CHECK(output_pointer < output_buffer->end());
112 if (*input_pointer == 0u) {
113 *code_pointer = code;
114 code_pointer = output_pointer;
115 ++output_pointer;
116 code = 1;
117 } else {
118 *output_pointer = *input_pointer;
119 ++output_pointer;
120 ++code;
121 if (code == 0xFFu) {
122 *code_pointer = 0xFF;
123 code_pointer = output_pointer;
124 ++output_pointer;
125 code = 1;
126 }
127 }
128 ++input_pointer;
129 }
130 *code_pointer = code;
131 CHECK(output_pointer <= output_buffer->end());
132 return gsl::span<char>(*output_buffer)
133 .subspan(0, output_pointer - output_buffer->begin());
134}
135
136template <size_t max_decoded_size>
137gsl::span<char> CobsDecode(gsl::span<const char> input,
138 std::array<char, max_decoded_size> *output_buffer) {
139 static_assert(max_decoded_size > 0, "Empty buffers not supported");
140 CHECK_LE(static_cast<size_t>(input.size()),
141 CobsMaxEncodedSize(max_decoded_size));
142 auto input_pointer = input.begin();
143 auto output_pointer = output_buffer->begin();
144 while (input_pointer < input.end()) {
145 const uint8_t code = *input_pointer;
146 ++input_pointer;
147 for (uint8_t i = 1; i < code; ++i) {
148 if (input_pointer == input.end()) {
149 break;
150 }
151 if (output_pointer == output_buffer->end()) {
152 return gsl::span<char>(*output_buffer);
153 }
154 *output_pointer = *input_pointer;
155 ++output_pointer;
156 ++input_pointer;
157 }
158 if (output_pointer == output_buffer->end()) {
159 return gsl::span<char>(*output_buffer);
160 }
161 if (code < 0xFFu) {
162 *output_pointer = 0;
163 ++output_pointer;
164 }
165 }
166 return gsl::span<char>(*output_buffer)
167 .subspan(0, output_pointer - output_buffer->begin() - 1);
168}
169
Brian Silvermanc64372e2019-02-17 18:07:40 -0800170template <size_t max_decoded_size>
171void CobsPacketizer<max_decoded_size>::ParseData(
172 gsl::span<const char> new_data) {
173 // Find where the active packet ends.
174 const auto first_end = std::find(new_data.begin(), new_data.end(), 0);
175 if (first_end == new_data.end()) {
176 // This is the common case, where there's no packet end in new_data.
177 CopyData(new_data);
178 return;
179 }
180
181 // Copy any remaining data for the active packet, and then finish it.
182 const auto first_end_index = first_end - new_data.begin();
183 CopyData(new_data.subspan(0, first_end_index));
184 FinishPacket();
185
186 // Look for where the last packet end is.
187 const auto first_end_reverse = new_data.rend() - first_end_index - 1;
188 const auto last_end = std::find(new_data.rbegin(), first_end_reverse, 0);
189 if (last_end == first_end_reverse) {
190 // If we didn't find another zero afterwards, then copy the rest of the data
191 // into the new packet and we're done.
192 CopyData(new_data.subspan(first_end_index + 1));
193 return;
194 }
195
196 // Otherwise, find the second-to-the-end packet end, which is where the last
197 // packet starts.
198 auto new_start = last_end;
199 auto new_end = new_data.rbegin();
200 // If a second packet ends at the end of new_data, then we want to grab it
201 // instead of ignoring it.
202 if (new_start == new_end) {
203 ++new_end;
204 new_start = std::find(new_end, first_end_reverse, 0);
205 }
206
207 // Being here means we found the end of multiple packets in new_data. Only
208 // copy the data which is part of the last one.
209 const auto new_start_index = new_data.rend() - new_start;
210 CopyData(new_data.subspan(new_start_index, new_start - new_end));
211 if (last_end == new_data.rbegin()) {
212 // If we also found the end of a packet, then return it.
213 FinishPacket();
214 }
215}
216
Brian Silverman41709732019-02-09 20:53:08 -0800217} // namespace jevois
218} // namespace frc971
219
220#endif // Y2019_JEVOIS_COBS_H_