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