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