• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_result/result.h"
26 #include "pw_status/status.h"
27 
28 namespace pw::hdlc {
29 
30 // Represents the contents of an HDLC frame -- the unescaped data between two
31 // flag bytes. Instances of Frame are only created when a full, valid frame has
32 // been read.
33 //
34 // For now, the Frame class assumes a single-byte control field and a 32-bit
35 // frame check sequence (FCS).
36 class Frame {
37  private:
38   static constexpr size_t kMinimumAddressSize = 1;
39   static constexpr size_t kControlSize = 1;
40   static constexpr size_t kFcsSize = sizeof(uint32_t);
41 
42  public:
43   // The minimum size of a frame, excluding control bytes (flag or escape).
44   static constexpr size_t kMinSizeBytes =
45       kMinimumAddressSize + kControlSize + kFcsSize;
46 
47   static Result<Frame> Parse(ConstByteSpan frame);
48 
address()49   constexpr uint64_t address() const { return address_; }
50 
control()51   constexpr std::byte control() const { return control_; }
52 
data()53   constexpr ConstByteSpan data() const { return data_; }
54 
55  private:
56   // Creates a Frame with the specified data. The data MUST be valid frame data
57   // with a verified frame check sequence.
Frame(uint64_t address,std::byte control,ConstByteSpan data)58   constexpr Frame(uint64_t address, std::byte control, ConstByteSpan data)
59       : data_(data), address_(address), control_(control) {}
60 
61   ConstByteSpan data_;
62   uint64_t address_;
63   std::byte control_;
64 };
65 
66 // The Decoder class facilitates decoding of data frames using the HDLC
67 // protocol, by returning packets as they are decoded and storing incomplete
68 // data frames in a buffer.
69 //
70 // The Decoder class does not own the buffer it writes to. It can be used to
71 // write bytes to any buffer. The DecoderBuffer template class, defined below,
72 // allocates a buffer.
73 class Decoder {
74  public:
Decoder(ByteSpan buffer)75   constexpr Decoder(ByteSpan buffer)
76       : buffer_(buffer),
77         last_read_bytes_({}),
78         last_read_bytes_index_(0),
79         current_frame_size_(0),
80         state_(State::kInterFrame) {}
81 
82   Decoder(const Decoder&) = delete;
83   Decoder& operator=(const Decoder&) = delete;
84 
85   // Parses a single byte of an HDLC stream. Returns a Result with the complete
86   // frame if the byte completes a frame. The status is the following:
87   //
88   //     OK - A frame was successfully decoded. The Result contains the Frame,
89   //         which is invalidated by the next Process call.
90   //     UNAVAILABLE - No frame is available.
91   //     RESOURCE_EXHAUSTED - A frame completed, but it was too large to fit in
92   //         the decoder's buffer.
93   //     DATA_LOSS - A frame completed, but it was invalid. The frame was
94   //         incomplete or the frame check sequence verification failed.
95   //
96   Result<Frame> Process(std::byte new_byte);
97 
98   // Processes a span of data and calls the provided callback with each frame or
99   // error.
100   template <typename F, typename... Args>
Process(ConstByteSpan data,F && callback,Args &&...args)101   void Process(ConstByteSpan data, F&& callback, Args&&... args) {
102     for (std::byte b : data) {
103       auto result = Process(b);
104       if (result.status() != Status::Unavailable()) {
105         std::invoke(
106             std::forward<F>(callback), std::forward<Args>(args)..., result);
107       }
108     }
109   }
110 
111   // Returns the maximum size of the Decoder's frame buffer.
max_size()112   size_t max_size() const { return buffer_.size(); }
113 
114   // Clears and resets the decoder.
Clear()115   void Clear() {
116     state_ = State::kInterFrame;
117     Reset();
118   };
119 
120  private:
121   // State enum class is used to make the Decoder a finite state machine.
122   enum class State {
123     kInterFrame,
124     kFrame,
125     kFrameEscape,
126   };
127 
Reset()128   void Reset() {
129     current_frame_size_ = 0;
130     last_read_bytes_index_ = 0;
131     fcs_.clear();
132   }
133 
134   void AppendByte(std::byte new_byte);
135 
136   Status CheckFrame() const;
137 
138   bool VerifyFrameCheckSequence() const;
139 
140   const ByteSpan buffer_;
141 
142   // Ring buffer of the last four bytes read into the current frame, to allow
143   // calculating the frame's CRC incrementally. As data is evicted from this
144   // buffer, it is added to the running CRC. Once a frame is complete, the
145   // buffer contains the frame's FCS.
146   std::array<std::byte, sizeof(uint32_t)> last_read_bytes_;
147   size_t last_read_bytes_index_;
148 
149   // Incremental checksum of the current frame.
150   checksum::Crc32 fcs_;
151 
152   size_t current_frame_size_;
153 
154   State state_;
155 };
156 
157 // DecoderBuffers declare a buffer along with a Decoder.
158 template <size_t kSizeBytes>
159 class DecoderBuffer : public Decoder {
160  public:
DecoderBuffer()161   DecoderBuffer() : Decoder(frame_buffer_) {}
162 
163   // Returns the maximum length of the bytes that can be inserted in the bytes
164   // buffer.
max_size()165   static constexpr size_t max_size() { return kSizeBytes; }
166 
167  private:
168   static_assert(kSizeBytes >= Frame::kMinSizeBytes);
169 
170   std::array<std::byte, kSizeBytes> frame_buffer_;
171 };
172 
173 }  // namespace pw::hdlc
174