Change script for apply upstream code
[platform/upstream/connectedhomeip.git] / third_party / pigweed / repo / pw_hdlc_lite / encoder.cc
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_lite/encoder.h"
16
17 #include <algorithm>
18 #include <array>
19 #include <cstddef>
20 #include <cstring>
21 #include <span>
22
23 #include "pw_bytes/endian.h"
24 #include "pw_checksum/crc32.h"
25 #include "pw_hdlc_lite_private/protocol.h"
26
27 using std::byte;
28
29 namespace pw::hdlc_lite {
30 namespace {
31
32 // Indicates this an information packet with sequence numbers set to 0.
33 constexpr byte kUnusedControl = byte{0};
34
35 Status EscapeAndWrite(const byte b, stream::Writer& writer) {
36   if (b == kFlag) {
37     return writer.Write(kEscapedFlag);
38   }
39   if (b == kEscape) {
40     return writer.Write(kEscapedEscape);
41   }
42   return writer.Write(b);
43 }
44
45 // Encodes and writes HDLC frames.
46 class Encoder {
47  public:
48   constexpr Encoder(stream::Writer& output) : writer_(output) {}
49
50   // Writes the header for an I-frame. After successfully calling
51   // StartInformationFrame, WriteData may be called any number of times.
52   Status StartInformationFrame(uint8_t address);
53
54   // Writes data for an ongoing frame. Must only be called after a successful
55   // StartInformationFrame call, and prior to a FinishFrame() call.
56   Status WriteData(ConstByteSpan data);
57
58   // Finishes a frame. Writes the frame check sequence and a terminating flag.
59   Status FinishFrame();
60
61  private:
62   stream::Writer& writer_;
63   checksum::Crc32 fcs_;
64 };
65
66 Status Encoder::StartInformationFrame(uint8_t address) {
67   fcs_.clear();
68   if (Status status = writer_.Write(kFlag); !status.ok()) {
69     return status;
70   }
71
72   const byte address_and_control[] = {std::byte{address}, kUnusedControl};
73   return WriteData(address_and_control);
74 }
75
76 Status Encoder::WriteData(ConstByteSpan data) {
77   auto begin = data.begin();
78   while (true) {
79     auto end = std::find_if(begin, data.end(), NeedsEscaping);
80
81     if (Status status = writer_.Write(std::span(begin, end)); !status.ok()) {
82       return status;
83     }
84     if (end == data.end()) {
85       fcs_.Update(data);
86       return Status::Ok();
87     }
88     if (Status status = EscapeAndWrite(*end, writer_); !status.ok()) {
89       return status;
90     }
91     begin = end + 1;
92   }
93 }
94
95 Status Encoder::FinishFrame() {
96   if (Status status =
97           WriteData(bytes::CopyInOrder(std::endian::little, fcs_.value()));
98       !status.ok()) {
99     return status;
100   }
101   return writer_.Write(kFlag);
102 }
103
104 }  // namespace
105
106 Status WriteInformationFrame(uint8_t address,
107                              ConstByteSpan payload,
108                              stream::Writer& writer) {
109   Encoder encoder(writer);
110
111   if (Status status = encoder.StartInformationFrame(address); !status.ok()) {
112     return status;
113   }
114   if (Status status = encoder.WriteData(payload); !status.ok()) {
115     return status;
116   }
117   return encoder.FinishFrame();
118 }
119
120 }  // namespace pw::hdlc_lite