• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_varint/varint.h"
26 
27 using std::byte;
28 
29 namespace pw::hdlc {
30 namespace internal {
31 
EscapeAndWrite(const byte b,stream::Writer & writer)32 Status EscapeAndWrite(const byte b, stream::Writer& writer) {
33   if (b == kFlag) {
34     return writer.Write(kEscapedFlag);
35   }
36   if (b == kEscape) {
37     return writer.Write(kEscapedEscape);
38   }
39   return writer.Write(b);
40 }
41 
WriteData(ConstByteSpan data)42 Status Encoder::WriteData(ConstByteSpan data) {
43   auto begin = data.begin();
44   while (true) {
45     auto end = std::find_if(begin, data.end(), NeedsEscaping);
46 
47     if (Status status = writer_.Write(std::span(begin, end)); !status.ok()) {
48       return status;
49     }
50     if (end == data.end()) {
51       fcs_.Update(data);
52       return OkStatus();
53     }
54     if (Status status = EscapeAndWrite(*end, writer_); !status.ok()) {
55       return status;
56     }
57     begin = end + 1;
58   }
59 }
60 
FinishFrame()61 Status Encoder::FinishFrame() {
62   if (Status status =
63           WriteData(bytes::CopyInOrder(std::endian::little, fcs_.value()));
64       !status.ok()) {
65     return status;
66   }
67   return writer_.Write(kFlag);
68 }
69 
MaxEncodedSize(uint64_t address,ConstByteSpan payload)70 size_t Encoder::MaxEncodedSize(uint64_t address, ConstByteSpan payload) {
71   constexpr size_t kFcsMaxSize = 8;  // Worst case FCS: 0x7e7e7e7e.
72   size_t max_encoded_address_size = varint::EncodedSize(address) * 2;
73   size_t encoded_payload_size =
74       payload.size() +
75       std::count_if(payload.begin(), payload.end(), NeedsEscaping);
76 
77   return max_encoded_address_size + sizeof(kUnusedControl) +
78          encoded_payload_size + kFcsMaxSize;
79 }
80 
StartFrame(uint64_t address,std::byte control)81 Status Encoder::StartFrame(uint64_t address, std::byte control) {
82   fcs_.clear();
83   if (Status status = writer_.Write(kFlag); !status.ok()) {
84     return status;
85   }
86 
87   std::array<std::byte, 16> metadata_buffer;
88   size_t metadata_size =
89       varint::Encode(address, metadata_buffer, kAddressFormat);
90   if (metadata_size == 0) {
91     return Status::InvalidArgument();
92   }
93 
94   metadata_buffer[metadata_size++] = control;
95   return WriteData(std::span(metadata_buffer).first(metadata_size));
96 }
97 
98 }  // namespace internal
99 
WriteUIFrame(uint64_t address,ConstByteSpan payload,stream::Writer & writer)100 Status WriteUIFrame(uint64_t address,
101                     ConstByteSpan payload,
102                     stream::Writer& writer) {
103   if (internal::Encoder::MaxEncodedSize(address, payload) >
104       writer.ConservativeWriteLimit()) {
105     return Status::ResourceExhausted();
106   }
107 
108   internal::Encoder encoder(writer);
109 
110   if (Status status = encoder.StartUnnumberedFrame(address); !status.ok()) {
111     return status;
112   }
113   if (Status status = encoder.WriteData(payload); !status.ok()) {
114     return status;
115   }
116   return encoder.FinishFrame();
117 }
118 
119 }  // namespace pw::hdlc
120