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