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
15 #include "pw_hdlc/encoder.h"
16
17 #include <algorithm>
18 #include <array>
19 #include <cstddef>
20 #include <cstring>
21 #include <span>
22
23 #include "pw_bytes/endian.h"
24 #include "pw_hdlc/internal/encoder.h"
25 #include "pw_varint/varint.h"
26
27 using std::byte;
28
29 namespace pw::hdlc {
30 namespace internal {
31
EscapeAndWrite(const byte b,stream::Writer & writer)32 Status EscapeAndWrite(const byte b, stream::Writer& writer) {
33 if (b == kFlag) {
34 return writer.Write(kEscapedFlag);
35 }
36 if (b == kEscape) {
37 return writer.Write(kEscapedEscape);
38 }
39 return writer.Write(b);
40 }
41
WriteData(ConstByteSpan data)42 Status Encoder::WriteData(ConstByteSpan data) {
43 auto begin = data.begin();
44 while (true) {
45 auto end = std::find_if(begin, data.end(), NeedsEscaping);
46
47 if (Status status = writer_.Write(std::span(begin, end)); !status.ok()) {
48 return status;
49 }
50 if (end == data.end()) {
51 fcs_.Update(data);
52 return OkStatus();
53 }
54 if (Status status = EscapeAndWrite(*end, writer_); !status.ok()) {
55 return status;
56 }
57 begin = end + 1;
58 }
59 }
60
FinishFrame()61 Status Encoder::FinishFrame() {
62 if (Status status =
63 WriteData(bytes::CopyInOrder(std::endian::little, fcs_.value()));
64 !status.ok()) {
65 return status;
66 }
67 return writer_.Write(kFlag);
68 }
69
MaxEncodedSize(uint64_t address,ConstByteSpan payload)70 size_t Encoder::MaxEncodedSize(uint64_t address, ConstByteSpan payload) {
71 constexpr size_t kFcsMaxSize = 8; // Worst case FCS: 0x7e7e7e7e.
72 size_t max_encoded_address_size = varint::EncodedSize(address) * 2;
73 size_t encoded_payload_size =
74 payload.size() +
75 std::count_if(payload.begin(), payload.end(), NeedsEscaping);
76
77 return max_encoded_address_size + sizeof(kUnusedControl) +
78 encoded_payload_size + kFcsMaxSize;
79 }
80
StartFrame(uint64_t address,std::byte control)81 Status Encoder::StartFrame(uint64_t address, std::byte control) {
82 fcs_.clear();
83 if (Status status = writer_.Write(kFlag); !status.ok()) {
84 return status;
85 }
86
87 std::array<std::byte, 16> metadata_buffer;
88 size_t metadata_size =
89 varint::Encode(address, metadata_buffer, kAddressFormat);
90 if (metadata_size == 0) {
91 return Status::InvalidArgument();
92 }
93
94 metadata_buffer[metadata_size++] = control;
95 return WriteData(std::span(metadata_buffer).first(metadata_size));
96 }
97
98 } // namespace internal
99
WriteUIFrame(uint64_t address,ConstByteSpan payload,stream::Writer & writer)100 Status WriteUIFrame(uint64_t address,
101 ConstByteSpan payload,
102 stream::Writer& writer) {
103 if (internal::Encoder::MaxEncodedSize(address, payload) >
104 writer.ConservativeWriteLimit()) {
105 return Status::ResourceExhausted();
106 }
107
108 internal::Encoder encoder(writer);
109
110 if (Status status = encoder.StartUnnumberedFrame(address); !status.ok()) {
111 return status;
112 }
113 if (Status status = encoder.WriteData(payload); !status.ok()) {
114 return status;
115 }
116 return encoder.FinishFrame();
117 }
118
119 } // namespace pw::hdlc
120