xref: /aosp_15_r20/external/pigweed/pw_hdlc/encoder.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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