Pigweed
 
Loading...
Searching...
No Matches
decoder.h
1// Copyright 2020 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14#pragma once
15
16#include <algorithm>
17#include <array>
18#include <cstddef>
19#include <cstring>
20#include <functional> // std::invoke
21
22#include "pw_assert/assert.h"
23#include "pw_bytes/span.h"
24#include "pw_checksum/crc32.h"
25#include "pw_hdlc/internal/protocol.h"
26#include "pw_result/result.h"
27#include "pw_status/status.h"
28
29namespace pw::hdlc {
30
31// Represents the contents of an HDLC frame -- the unescaped data between two
32// flag bytes. Instances of Frame are only created when a full, valid frame has
33// been read.
34class Frame {
35 public:
36 // The minimum size of a frame, excluding control bytes (flag or escape).
37 static constexpr size_t kMinContentSizeBytes =
38 kMinAddressSize + kControlSize + kFcsSize;
39
40 static Result<Frame> Parse(ConstByteSpan frame);
41
42 constexpr uint64_t address() const { return address_; }
43
44 constexpr std::byte control() const { return control_; }
45
46 constexpr ConstByteSpan data() const { return data_; }
47
48 private:
49 // Creates a Frame with the specified data. The data MUST be valid frame data
50 // with a verified frame check sequence.
51 constexpr Frame(uint64_t address, std::byte control, ConstByteSpan data)
52 : data_(data), address_(address), control_(control) {}
53
54 ConstByteSpan data_;
55 uint64_t address_;
56 std::byte control_;
57};
58
59// The Decoder class facilitates decoding of data frames using the HDLC
60// protocol, by returning packets as they are decoded and storing incomplete
61// data frames in a buffer.
62//
63// The Decoder class does not own the buffer it writes to. It can be used to
64// write bytes to any buffer. The DecoderBuffer template class, defined below,
65// allocates a buffer.
66class Decoder {
67 public:
68 constexpr Decoder(ByteSpan buffer)
69 : buffer_(buffer),
70 last_read_bytes_({}),
71 last_read_bytes_index_(0),
72 current_frame_size_(0),
73 state_(State::kInterFrame) {}
74
75 Decoder(const Decoder&) = delete;
76 Decoder& operator=(const Decoder&) = delete;
77 Decoder(Decoder&&) = default;
78 Decoder& operator=(Decoder&&) = default;
79
100 Result<Frame> Process(std::byte new_byte);
101
102 // Returns the buffer space required for a `Decoder` to successfully decode a
103 // frame whose on-the-wire HDLC encoded size does not exceed `max_frame_size`.
104 static constexpr size_t RequiredBufferSizeForFrameSize(
105 size_t max_frame_size) {
106 // Flag bytes aren't stored in the internal buffer, so we can save a couple
107 // bytes.
108 return max_frame_size < Frame::kMinContentSizeBytes
109 ? Frame::kMinContentSizeBytes
110 : max_frame_size - 2;
111 }
112
115 template <typename F, typename... Args>
116 void Process(ConstByteSpan data, F&& callback, Args&&... args) {
117 for (std::byte b : data) {
118 auto result = Process(b);
119 if (result.status() != Status::Unavailable()) {
120 callback(std::forward<Args>(args)..., result);
121 }
122 }
123 }
124
125 // Returns the maximum size of the Decoder's frame buffer.
126 size_t max_size() const { return buffer_.size(); }
127
128 // Clears and resets the decoder.
129 void Clear() {
130 state_ = State::kInterFrame;
131 Reset();
132 }
133
134 private:
135 // State enum class is used to make the Decoder a finite state machine.
136 enum class State {
137 kInterFrame,
138 kFrame,
139 kFrameEscape,
140 };
141
142 void Reset() {
143 current_frame_size_ = 0;
144 last_read_bytes_index_ = 0;
145 fcs_.clear();
146 }
147
148 void AppendByte(std::byte new_byte);
149
150 Status CheckFrame() const;
151
152 bool VerifyFrameCheckSequence() const;
153
154 ByteSpan buffer_;
155
156 // Ring buffer of the last four bytes read into the current frame, to allow
157 // calculating the frame's CRC incrementally. As data is evicted from this
158 // buffer, it is added to the running CRC. Once a frame is complete, the
159 // buffer contains the frame's FCS.
160 std::array<std::byte, sizeof(uint32_t)> last_read_bytes_;
161 size_t last_read_bytes_index_;
162
163 // Incremental checksum of the current frame.
164 checksum::Crc32 fcs_;
165
166 size_t current_frame_size_;
167
168 State state_;
169};
170
171// DecoderBuffers declare a buffer along with a Decoder.
172template <size_t kSizeBytes>
173class DecoderBuffer : public Decoder {
174 public:
175 DecoderBuffer() : Decoder(frame_buffer_) {}
176
180
184
185 // Returns the maximum length of the bytes that can be inserted in the bytes
186 // buffer.
187 static constexpr size_t max_size() { return kSizeBytes; }
188
189 private:
190 static_assert(kSizeBytes >= Frame::kMinContentSizeBytes);
191
192 std::array<std::byte, kSizeBytes> frame_buffer_;
193};
194
195} // namespace pw::hdlc
Definition: decoder.h:173
DecoderBuffer & operator=(DecoderBuffer &&)=delete
DecoderBuffer(DecoderBuffer &&)=delete
Definition: decoder.h:66
Result< Frame > Process(std::byte new_byte)
Parses a single byte of an HDLC stream.
void Process(ConstByteSpan data, F &&callback, Args &&... args)
Processes a span of data and calls the provided callback with each frame or error.
Definition: decoder.h:116
Definition: decoder.h:34