Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / third_party / pigweed / repo / pw_hdlc / 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/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_hdlc/internal/encoder.h"
25 #include "pw_hdlc_private/protocol.h"
26
27 using std::byte;
28
29 namespace pw::hdlc {
30 namespace internal {
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 Status Encoder::StartInformationFrame(uint8_t address) {
46   fcs_.clear();
47   if (Status status = writer_.Write(kFlag); !status.ok()) {
48     return status;
49   }
50
51   const byte address_and_control[] = {
52       std::byte{address}, kUnusedControl, kUnusedControl};
53   return WriteData(address_and_control);
54 }
55
56 Status Encoder::StartUnnumberedFrame(uint8_t address) {
57   fcs_.clear();
58   if (Status status = writer_.Write(kFlag); !status.ok()) {
59     return status;
60   }
61
62   const byte address_and_control[] = {
63       std::byte{address}, UFrameControl::UnnumberedInformation().data()};
64   return WriteData(address_and_control);
65 }
66
67 Status Encoder::WriteData(ConstByteSpan data) {
68   auto begin = data.begin();
69   while (true) {
70     auto end = std::find_if(begin, data.end(), NeedsEscaping);
71
72     if (Status status = writer_.Write(std::span(begin, end)); !status.ok()) {
73       return status;
74     }
75     if (end == data.end()) {
76       fcs_.Update(data);
77       return OkStatus();
78     }
79     if (Status status = EscapeAndWrite(*end, writer_); !status.ok()) {
80       return status;
81     }
82     begin = end + 1;
83   }
84 }
85
86 Status Encoder::FinishFrame() {
87   if (Status status =
88           WriteData(bytes::CopyInOrder(std::endian::little, fcs_.value()));
89       !status.ok()) {
90     return status;
91   }
92   return writer_.Write(kFlag);
93 }
94
95 size_t Encoder::MaxEncodedSize(uint8_t address, ConstByteSpan payload) {
96   constexpr size_t kFcsMaxSize = 8;  // Worst case FCS: 0x7e7e7e7e.
97   size_t encoded_address_size = NeedsEscaping(std::byte{address}) ? 2 : 1;
98   size_t encoded_payload_size =
99       payload.size() +
100       std::count_if(payload.begin(), payload.end(), NeedsEscaping);
101
102   return encoded_address_size + encoded_payload_size + kFcsMaxSize;
103 }
104
105 }  // namespace internal
106
107 Status WriteUIFrame(uint8_t address,
108                     ConstByteSpan payload,
109                     stream::Writer& writer) {
110   if (internal::Encoder::MaxEncodedSize(address, payload) >
111       writer.ConservativeWriteLimit()) {
112     return Status::ResourceExhausted();
113   }
114
115   internal::Encoder encoder(writer);
116
117   if (Status status = encoder.StartUnnumberedFrame(address); !status.ok()) {
118     return status;
119   }
120   if (Status status = encoder.WriteData(payload); !status.ok()) {
121     return status;
122   }
123   return encoder.FinishFrame();
124 }
125
126 }  // namespace pw::hdlc