blob: 1e140664a4358b77f1a4f0f5f196a50266d0b181 [file] [log] [blame]
// 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