| // Copyright 2020 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| #pragma once |
| |
| #include <algorithm> |
| #include <array> |
| #include <cstddef> |
| #include <cstring> |
| #include <functional> // std::invoke |
| |
| #include "pw_bytes/span.h" |
| #include "pw_result/result.h" |
| #include "pw_status/status.h" |
| |
| namespace pw::hdlc_lite { |
| |
| // Represents the contents of an HDLC frame -- the unescaped data between two |
| // flag bytes. Instances of Frame are only created when a full, valid frame has |
| // been read. |
| // |
| // For now, the Frame class assumes single-byte address and control fields and a |
| // 32-bit frame check sequence (FCS). |
| class Frame { |
| private: |
| static constexpr size_t kAddressSize = 1; |
| static constexpr size_t kControlSize = 1; |
| static constexpr size_t kFcsSize = sizeof(uint32_t); |
| |
| public: |
| // The minimum size of a frame, excluding control bytes (flag or escape). |
| static constexpr size_t kMinSizeBytes = |
| kAddressSize + kControlSize + kFcsSize; |
| |
| // Creates a Frame with the specified data. The data MUST be valid frame data |
| // with a verified frame check sequence. |
| explicit constexpr Frame(ConstByteSpan data) : frame_(data) { |
| // TODO(pwbug/246): Use PW_DASSERT when available. |
| // PW_DASSERT(data.size() >= kMinSizeBytes); |
| } |
| |
| constexpr unsigned address() const { |
| return std::to_integer<unsigned>(frame_[0]); |
| } |
| |
| constexpr std::byte control() const { return frame_[kAddressSize]; } |
| |
| constexpr ConstByteSpan data() const { |
| return frame_.subspan(kAddressSize + kControlSize, |
| frame_.size() - kMinSizeBytes); |
| } |
| |
| private: |
| ConstByteSpan frame_; |
| }; |
| |
| // The Decoder class facilitates decoding of data frames using the HDLC-Lite |
| // protocol, by returning packets as they are decoded and storing incomplete |
| // data frames in a buffer. |
| // |
| // The Decoder class does not own the buffer it writes to. It can be used to |
| // write bytes to any buffer. The DecoderBuffer template class, defined below, |
| // allocates a buffer. |
| class Decoder { |
| public: |
| constexpr Decoder(ByteSpan buffer) |
| : buffer_(buffer), current_frame_size_(0), state_(State::kInterFrame) {} |
| |
| Decoder(const Decoder&) = delete; |
| Decoder& operator=(const Decoder&) = delete; |
| |
| // Parses a single byte of an HDLC stream. Returns a Result with the complete |
| // frame if the byte completes a frame. The status is the following: |
| // |
| // OK - A frame was successfully decoded. The Result contains the Frame, |
| // which is invalidated by the next Process call. |
| // UNAVAILABLE - No frame is available. |
| // RESOURCE_EXHAUSTED - A frame completed, but it was too large to fit in |
| // the decoder's buffer. |
| // DATA_LOSS - A frame completed, but it was invalid. The frame was |
| // incomplete or the frame check sequence verification failed. |
| // |
| Result<Frame> Process(std::byte b); |
| |
| // Calls the provided callback with each frame or error. |
| template <typename F, typename... Args> |
| void Process(ConstByteSpan data, F&& callback, Args&&... args) { |
| for (std::byte b : data) { |
| auto result = Process(b); |
| if (result.status() != Status::UNAVAILABLE) { |
| std::invoke( |
| std::forward<F>(callback), std::forward<Args>(args)..., result); |
| } |
| } |
| } |
| |
| // Returns the maximum size of the Decoder's frame buffer. |
| size_t max_size() const { return buffer_.size(); } |
| |
| // Clears and resets the decoder. |
| void clear() { |
| current_frame_size_ = 0; |
| state_ = State::kInterFrame; |
| }; |
| |
| private: |
| // State enum class is used to make the Decoder a finite state machine. |
| enum class State { |
| kInterFrame, |
| kFrame, |
| kFrameEscape, |
| }; |
| |
| void AppendByte(std::byte new_byte); |
| |
| Status CheckFrame() const; |
| |
| bool VerifyFrameCheckSequence() const; |
| |
| const ByteSpan buffer_; |
| |
| size_t current_frame_size_; |
| |
| State state_; |
| }; |
| |
| // DecoderBuffers declare a buffer along with a Decoder. |
| template <size_t size_bytes> |
| class DecoderBuffer : public Decoder { |
| public: |
| DecoderBuffer() : Decoder(frame_buffer_) {} |
| |
| // Returns the maximum length of the bytes that can be inserted in the bytes |
| // buffer. |
| static constexpr size_t max_size() { return size_bytes; } |
| |
| private: |
| static_assert(size_bytes >= Frame::kMinSizeBytes); |
| |
| std::array<std::byte, size_bytes> frame_buffer_; |
| }; |
| |
| } // namespace pw::hdlc_lite |