1 // Copyright 2020 The Pigweed Authors
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
7 // https://www.apache.org/licenses/LICENSE-2.0
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
15 #include "pw_hdlc/encoder.h"
23 #include "pw_bytes/endian.h"
24 #include "pw_hdlc/internal/encoder.h"
25 #include "pw_hdlc_private/protocol.h"
32 // Indicates this an information packet with sequence numbers set to 0.
33 constexpr byte kUnusedControl = byte{0};
35 Status EscapeAndWrite(const byte b, stream::Writer& writer) {
37 return writer.Write(kEscapedFlag);
40 return writer.Write(kEscapedEscape);
42 return writer.Write(b);
45 Status Encoder::StartInformationFrame(uint8_t address) {
47 if (Status status = writer_.Write(kFlag); !status.ok()) {
51 const byte address_and_control[] = {
52 std::byte{address}, kUnusedControl, kUnusedControl};
53 return WriteData(address_and_control);
56 Status Encoder::StartUnnumberedFrame(uint8_t address) {
58 if (Status status = writer_.Write(kFlag); !status.ok()) {
62 const byte address_and_control[] = {
63 std::byte{address}, UFrameControl::UnnumberedInformation().data()};
64 return WriteData(address_and_control);
67 Status Encoder::WriteData(ConstByteSpan data) {
68 auto begin = data.begin();
70 auto end = std::find_if(begin, data.end(), NeedsEscaping);
72 if (Status status = writer_.Write(std::span(begin, end)); !status.ok()) {
75 if (end == data.end()) {
79 if (Status status = EscapeAndWrite(*end, writer_); !status.ok()) {
86 Status Encoder::FinishFrame() {
88 WriteData(bytes::CopyInOrder(std::endian::little, fcs_.value()));
92 return writer_.Write(kFlag);
95 size_t Encoder::MaxEncodedSize(uint8_t address, ConstByteSpan payload) {
96 constexpr size_t kFcsMaxSize = 8; // Worst case FCS: 0x7e7e7e7e.
97 size_t encoded_address_size = NeedsEscaping(std::byte{address}) ? 2 : 1;
98 size_t encoded_payload_size =
100 std::count_if(payload.begin(), payload.end(), NeedsEscaping);
102 return encoded_address_size + encoded_payload_size + kFcsMaxSize;
105 } // namespace internal
107 Status WriteUIFrame(uint8_t address,
108 ConstByteSpan payload,
109 stream::Writer& writer) {
110 if (internal::Encoder::MaxEncodedSize(address, payload) >
111 writer.ConservativeWriteLimit()) {
112 return Status::ResourceExhausted();
115 internal::Encoder encoder(writer);
117 if (Status status = encoder.StartUnnumberedFrame(address); !status.ok()) {
120 if (Status status = encoder.WriteData(payload); !status.ok()) {
123 return encoder.FinishFrame();
126 } // namespace pw::hdlc