xref: /aosp_15_r20/external/pigweed/pw_hdlc/encoder.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2020 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #include "pw_hdlc/encoder.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include <algorithm>
18*61c4878aSAndroid Build Coastguard Worker #include <array>
19*61c4878aSAndroid Build Coastguard Worker #include <cstddef>
20*61c4878aSAndroid Build Coastguard Worker #include <cstring>
21*61c4878aSAndroid Build Coastguard Worker 
22*61c4878aSAndroid Build Coastguard Worker #include "pw_bytes/endian.h"
23*61c4878aSAndroid Build Coastguard Worker #include "pw_hdlc/encoded_size.h"
24*61c4878aSAndroid Build Coastguard Worker #include "pw_span/span.h"
25*61c4878aSAndroid Build Coastguard Worker #include "pw_varint/varint.h"
26*61c4878aSAndroid Build Coastguard Worker 
27*61c4878aSAndroid Build Coastguard Worker using std::byte;
28*61c4878aSAndroid Build Coastguard Worker 
29*61c4878aSAndroid Build Coastguard Worker namespace pw::hdlc {
30*61c4878aSAndroid Build Coastguard Worker 
EscapeAndWrite(const byte b,stream::Writer & writer)31*61c4878aSAndroid Build Coastguard Worker Status EscapeAndWrite(const byte b, stream::Writer& writer) {
32*61c4878aSAndroid Build Coastguard Worker   if (b == kFlag) {
33*61c4878aSAndroid Build Coastguard Worker     return writer.Write(kEscapedFlag);
34*61c4878aSAndroid Build Coastguard Worker   }
35*61c4878aSAndroid Build Coastguard Worker   if (b == kEscape) {
36*61c4878aSAndroid Build Coastguard Worker     return writer.Write(kEscapedEscape);
37*61c4878aSAndroid Build Coastguard Worker   }
38*61c4878aSAndroid Build Coastguard Worker   return writer.Write(b);
39*61c4878aSAndroid Build Coastguard Worker }
40*61c4878aSAndroid Build Coastguard Worker 
WriteData(ConstByteSpan data)41*61c4878aSAndroid Build Coastguard Worker Status Encoder::WriteData(ConstByteSpan data) {
42*61c4878aSAndroid Build Coastguard Worker   auto begin = data.begin();
43*61c4878aSAndroid Build Coastguard Worker   while (true) {
44*61c4878aSAndroid Build Coastguard Worker     auto end = std::find_if(begin, data.end(), NeedsEscaping);
45*61c4878aSAndroid Build Coastguard Worker 
46*61c4878aSAndroid Build Coastguard Worker     if (Status status = writer_.Write(span(begin, end)); !status.ok()) {
47*61c4878aSAndroid Build Coastguard Worker       return status;
48*61c4878aSAndroid Build Coastguard Worker     }
49*61c4878aSAndroid Build Coastguard Worker     if (end == data.end()) {
50*61c4878aSAndroid Build Coastguard Worker       fcs_.Update(data);
51*61c4878aSAndroid Build Coastguard Worker       return OkStatus();
52*61c4878aSAndroid Build Coastguard Worker     }
53*61c4878aSAndroid Build Coastguard Worker     if (Status status = EscapeAndWrite(*end, writer_); !status.ok()) {
54*61c4878aSAndroid Build Coastguard Worker       return status;
55*61c4878aSAndroid Build Coastguard Worker     }
56*61c4878aSAndroid Build Coastguard Worker     begin = end + 1;
57*61c4878aSAndroid Build Coastguard Worker   }
58*61c4878aSAndroid Build Coastguard Worker }
59*61c4878aSAndroid Build Coastguard Worker 
FinishFrame()60*61c4878aSAndroid Build Coastguard Worker Status Encoder::FinishFrame() {
61*61c4878aSAndroid Build Coastguard Worker   if (Status status =
62*61c4878aSAndroid Build Coastguard Worker           WriteData(bytes::CopyInOrder(endian::little, fcs_.value()));
63*61c4878aSAndroid Build Coastguard Worker       !status.ok()) {
64*61c4878aSAndroid Build Coastguard Worker     return status;
65*61c4878aSAndroid Build Coastguard Worker   }
66*61c4878aSAndroid Build Coastguard Worker   return writer_.Write(kFlag);
67*61c4878aSAndroid Build Coastguard Worker }
68*61c4878aSAndroid Build Coastguard Worker 
StartFrame(uint64_t address,std::byte control)69*61c4878aSAndroid Build Coastguard Worker Status Encoder::StartFrame(uint64_t address, std::byte control) {
70*61c4878aSAndroid Build Coastguard Worker   fcs_.clear();
71*61c4878aSAndroid Build Coastguard Worker   if (Status status = writer_.Write(kFlag); !status.ok()) {
72*61c4878aSAndroid Build Coastguard Worker     return status;
73*61c4878aSAndroid Build Coastguard Worker   }
74*61c4878aSAndroid Build Coastguard Worker 
75*61c4878aSAndroid Build Coastguard Worker   std::array<std::byte, 16> metadata_buffer;
76*61c4878aSAndroid Build Coastguard Worker   size_t metadata_size =
77*61c4878aSAndroid Build Coastguard Worker       varint::Encode(address, metadata_buffer, kAddressFormat);
78*61c4878aSAndroid Build Coastguard Worker   if (metadata_size == 0) {
79*61c4878aSAndroid Build Coastguard Worker     return Status::InvalidArgument();
80*61c4878aSAndroid Build Coastguard Worker   }
81*61c4878aSAndroid Build Coastguard Worker 
82*61c4878aSAndroid Build Coastguard Worker   metadata_buffer[metadata_size++] = control;
83*61c4878aSAndroid Build Coastguard Worker   return WriteData(span(metadata_buffer).first(metadata_size));
84*61c4878aSAndroid Build Coastguard Worker }
85*61c4878aSAndroid Build Coastguard Worker 
WriteUIFrame(uint64_t address,ConstByteSpan payload,stream::Writer & writer)86*61c4878aSAndroid Build Coastguard Worker Status WriteUIFrame(uint64_t address,
87*61c4878aSAndroid Build Coastguard Worker                     ConstByteSpan payload,
88*61c4878aSAndroid Build Coastguard Worker                     stream::Writer& writer) {
89*61c4878aSAndroid Build Coastguard Worker   if (MaxEncodedFrameSize(address, payload) > writer.ConservativeWriteLimit()) {
90*61c4878aSAndroid Build Coastguard Worker     return Status::ResourceExhausted();
91*61c4878aSAndroid Build Coastguard Worker   }
92*61c4878aSAndroid Build Coastguard Worker 
93*61c4878aSAndroid Build Coastguard Worker   Encoder encoder(writer);
94*61c4878aSAndroid Build Coastguard Worker 
95*61c4878aSAndroid Build Coastguard Worker   if (Status status = encoder.StartUnnumberedFrame(address); !status.ok()) {
96*61c4878aSAndroid Build Coastguard Worker     return status;
97*61c4878aSAndroid Build Coastguard Worker   }
98*61c4878aSAndroid Build Coastguard Worker   if (Status status = encoder.WriteData(payload); !status.ok()) {
99*61c4878aSAndroid Build Coastguard Worker     return status;
100*61c4878aSAndroid Build Coastguard Worker   }
101*61c4878aSAndroid Build Coastguard Worker   return encoder.FinishFrame();
102*61c4878aSAndroid Build Coastguard Worker }
103*61c4878aSAndroid Build Coastguard Worker 
104*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::hdlc
105