1 // Copyright 2022 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/encoded_size.h"
16 
17 #include <array>
18 #include <cstddef>
19 #include <cstdint>
20 
21 #include "gtest/gtest.h"
22 #include "pw_bytes/array.h"
23 #include "pw_hdlc/decoder.h"
24 #include "pw_hdlc/encoder.h"
25 #include "pw_hdlc/internal/encoder.h"
26 #include "pw_result/result.h"
27 #include "pw_stream/memory_stream.h"
28 #include "pw_varint/varint.h"
29 
30 namespace pw::hdlc {
31 namespace {
32 
33 // The varint-encoded address that represents the value that will result in the
34 // largest on-the-wire address after HDLC escaping.
35 constexpr auto kWidestVarintAddress =
36     bytes::String("\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x03");
37 
38 // This is the decoded varint value of kWidestVarintAddress. This is
39 // pre-calculated as a constant to simplify tests.
40 constexpr uint64_t kWidestAddress = 0xbf7efdfbf7efdfbf;
41 
42 // UI frames created by WriteUIFrame() will never be have an escaped control
43 // field, but it's technically possible for other HDLC frame types to produce
44 // control bytes that would need to be escaped.
45 constexpr size_t kEscapedControlCost = kControlSize;
46 
47 // UI frames created by WriteUIFrame() will never have an escaped control
48 // field, but it's technically possible for other HDLC frame types to produce
49 // control bytes that would need to be escaped.
50 constexpr size_t kEscapedFcsCost = kMaxEscapedFcsSize - kFcsSize;
51 
52 // Due to API limitations, the worst case buffer calculations used by the HDLC
53 // encoder/decoder can't be fully saturated. This constexpr value accounts for
54 // this by expressing the delta between the constant largest testable HDLC frame
55 // and the calculated worst-case-scenario.
56 constexpr size_t kTestLimitationsOverhead =
57     kEscapedControlCost + kEscapedFcsCost;
58 
59 // A payload only containing bytes that need to be escaped.
60 constexpr auto kFullyEscapedPayload =
61     bytes::String("\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e");
62 
63 constexpr uint8_t kEscapeAddress = static_cast<uint8_t>(kFlag);
64 constexpr uint8_t kNoEscapeAddress = 6;
65 
TEST(EncodedSize,Constants_WidestAddress)66 TEST(EncodedSize, Constants_WidestAddress) {
67   uint64_t address = 0;
68   size_t address_size =
69       varint::Decode(kWidestVarintAddress, &address, kAddressFormat);
70   EXPECT_EQ(address_size, 10u);
71   EXPECT_EQ(address_size, kMaxAddressSize);
72   EXPECT_EQ(kMaxEscapedVarintAddressSize, 19u);
73   EXPECT_EQ(EscapedSize(kWidestVarintAddress), kMaxEscapedVarintAddressSize);
74   EXPECT_EQ(address, kWidestAddress);
75   EXPECT_EQ(varint::EncodedSize(kWidestAddress), 10u);
76 }
77 
TEST(EncodedSize,EscapedSize_AllEscapeBytes)78 TEST(EncodedSize, EscapedSize_AllEscapeBytes) {
79   EXPECT_EQ(EscapedSize(kFullyEscapedPayload), kFullyEscapedPayload.size() * 2);
80 }
81 
TEST(EncodedSize,EscapedSize_NoEscapeBytes)82 TEST(EncodedSize, EscapedSize_NoEscapeBytes) {
83   constexpr auto kData = bytes::String("\x01\x23\x45\x67\x89\xab\xcd\xef");
84   EXPECT_EQ(EscapedSize(kData), kData.size());
85 }
86 
TEST(EncodedSize,EscapedSize_SomeEscapeBytes)87 TEST(EncodedSize, EscapedSize_SomeEscapeBytes) {
88   constexpr auto kData = bytes::String("\x7epabu\x7d");
89   EXPECT_EQ(EscapedSize(kData), kData.size() + 2);
90 }
91 
TEST(EncodedSize,EscapedSize_Address)92 TEST(EncodedSize, EscapedSize_Address) {
93   EXPECT_EQ(EscapedSize(kWidestVarintAddress),
94             varint::EncodedSize(kWidestAddress) * 2 - 1);
95 }
96 
TEST(EncodedSize,MaxEncodedSize_Overload)97 TEST(EncodedSize, MaxEncodedSize_Overload) {
98   EXPECT_EQ(MaxEncodedFrameSize(kFullyEscapedPayload.size()),
99             MaxEncodedFrameSize(kWidestAddress, kFullyEscapedPayload));
100 }
101 
TEST(EncodedSize,MaxEncodedSize_EmptyPayload)102 TEST(EncodedSize, MaxEncodedSize_EmptyPayload) {
103   EXPECT_EQ(14u, MaxEncodedFrameSize(kNoEscapeAddress, {}));
104   EXPECT_EQ(14u, MaxEncodedFrameSize(kEscapeAddress, {}));
105 }
106 
TEST(EncodedSize,MaxEncodedSize_PayloadWithoutEscapes)107 TEST(EncodedSize, MaxEncodedSize_PayloadWithoutEscapes) {
108   constexpr auto data = bytes::Array<0x00, 0x01, 0x02, 0x03>();
109   EXPECT_EQ(18u, MaxEncodedFrameSize(kNoEscapeAddress, data));
110   EXPECT_EQ(18u, MaxEncodedFrameSize(kEscapeAddress, data));
111 }
112 
TEST(EncodedSize,MaxEncodedSize_PayloadWithOneEscape)113 TEST(EncodedSize, MaxEncodedSize_PayloadWithOneEscape) {
114   constexpr auto data = bytes::Array<0x00, 0x01, 0x7e, 0x03>();
115   EXPECT_EQ(19u, MaxEncodedFrameSize(kNoEscapeAddress, data));
116   EXPECT_EQ(19u, MaxEncodedFrameSize(kEscapeAddress, data));
117 }
118 
TEST(EncodedSize,MaxEncodedSize_PayloadWithAllEscapes)119 TEST(EncodedSize, MaxEncodedSize_PayloadWithAllEscapes) {
120   constexpr auto data = bytes::Initialized<8>(0x7e);
121   EXPECT_EQ(30u, MaxEncodedFrameSize(kNoEscapeAddress, data));
122   EXPECT_EQ(30u, MaxEncodedFrameSize(kEscapeAddress, data));
123 }
124 
TEST(EncodedSize,MaxPayload_UndersizedFrame)125 TEST(EncodedSize, MaxPayload_UndersizedFrame) {
126   EXPECT_EQ(MaxSafePayloadSize(4), 0u);
127 }
128 
TEST(EncodedSize,MaxPayload_SmallFrame)129 TEST(EncodedSize, MaxPayload_SmallFrame) {
130   EXPECT_EQ(MaxSafePayloadSize(128), 48u);
131 }
132 
TEST(EncodedSize,MaxPayload_MediumFrame)133 TEST(EncodedSize, MaxPayload_MediumFrame) {
134   EXPECT_EQ(MaxSafePayloadSize(512), 240u);
135 }
136 
TEST(EncodedSize,FrameToPayloadInversion_Odd)137 TEST(EncodedSize, FrameToPayloadInversion_Odd) {
138   static constexpr size_t kIntendedPayloadSize = 1234567891;
139   EXPECT_EQ(MaxSafePayloadSize(MaxEncodedFrameSize(kIntendedPayloadSize)),
140             kIntendedPayloadSize);
141 }
142 
TEST(EncodedSize,PayloadToFrameInversion_Odd)143 TEST(EncodedSize, PayloadToFrameInversion_Odd) {
144   static constexpr size_t kIntendedFrameSize = 1234567891;
145   EXPECT_EQ(MaxEncodedFrameSize(MaxSafePayloadSize(kIntendedFrameSize)),
146             kIntendedFrameSize);
147 }
148 
TEST(EncodedSize,FrameToPayloadInversion_Even)149 TEST(EncodedSize, FrameToPayloadInversion_Even) {
150   static constexpr size_t kIntendedPayloadSize = 42;
151   EXPECT_EQ(MaxSafePayloadSize(MaxEncodedFrameSize(kIntendedPayloadSize)),
152             kIntendedPayloadSize);
153 }
154 
TEST(EncodedSize,PayloadToFrameInversion_Even)155 TEST(EncodedSize, PayloadToFrameInversion_Even) {
156   static constexpr size_t kIntendedFrameSize = 42;
157   // Because of HDLC encoding overhead requirements, the last byte of the
158   // intended frame size is wasted because it doesn't allow sufficient space for
159   // another byte since said additional byte could require escaping, therefore
160   // requiring a second byte to increase the safe payload size by one.
161   const size_t max_frame_usage =
162       MaxEncodedFrameSize(MaxSafePayloadSize(kIntendedFrameSize));
163   EXPECT_EQ(max_frame_usage, kIntendedFrameSize - 1);
164 
165   // There's no further change if the inversion is done again since the frame
166   // size is aligned to the reduced bounds.
167   EXPECT_EQ(MaxEncodedFrameSize(MaxSafePayloadSize(max_frame_usage)),
168             kIntendedFrameSize - 1);
169 }
170 
TEST(EncodedSize,MostlyEscaped)171 TEST(EncodedSize, MostlyEscaped) {
172   constexpr auto kMostlyEscapedPayload =
173       bytes::String(":)\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e");
174   constexpr size_t kUnescapedBytes =
175       2 * kMostlyEscapedPayload.size() - EscapedSize(kMostlyEscapedPayload);
176   // Subtracting 2 should still leave enough space since two bytes won't need
177   // to be escaped.
178   constexpr size_t kExpectedMaxFrameSize =
179       MaxEncodedFrameSize(kMostlyEscapedPayload.size()) - kUnescapedBytes;
180   std::array<std::byte, kExpectedMaxFrameSize> dest_buffer;
181   stream::MemoryWriter writer(dest_buffer);
182   EXPECT_EQ(kUnescapedBytes, 2u);
183   EXPECT_EQ(OkStatus(),
184             WriteUIFrame(kWidestAddress, kFullyEscapedPayload, writer));
185   EXPECT_EQ(writer.size(),
186             kExpectedMaxFrameSize - kTestLimitationsOverhead - kUnescapedBytes);
187 }
188 
TEST(EncodedSize,BigAddress_SaturatedPayload)189 TEST(EncodedSize, BigAddress_SaturatedPayload) {
190   constexpr size_t kExpectedMaxFrameSize =
191       MaxEncodedFrameSize(kFullyEscapedPayload.size());
192   std::array<std::byte, kExpectedMaxFrameSize> dest_buffer;
193   stream::MemoryWriter writer(dest_buffer);
194   EXPECT_EQ(OkStatus(),
195             WriteUIFrame(kWidestAddress, kFullyEscapedPayload, writer));
196   EXPECT_EQ(writer.size(), kExpectedMaxFrameSize - kTestLimitationsOverhead);
197 }
198 
TEST(EncodedSize,BigAddress_OffByOne)199 TEST(EncodedSize, BigAddress_OffByOne) {
200   constexpr size_t kExpectedMaxFrameSize =
201       MaxEncodedFrameSize(kFullyEscapedPayload.size()) - 1;
202   std::array<std::byte, kExpectedMaxFrameSize> dest_buffer;
203   stream::MemoryWriter writer(dest_buffer);
204   EXPECT_EQ(Status::ResourceExhausted(),
205             WriteUIFrame(kWidestAddress, kFullyEscapedPayload, writer));
206 }
207 
TEST(EncodedSize,SmallAddress_SaturatedPayload)208 TEST(EncodedSize, SmallAddress_SaturatedPayload) {
209   constexpr auto kSmallerEscapedAddress = bytes::String("\x7e\x7d");
210   // varint::Decode() is not constexpr, so this is a hard-coded and then runtime
211   // validated.
212   constexpr size_t kVarintDecodedAddress = 7999;
213   constexpr size_t kExpectedMaxFrameSize =
214       MaxEncodedFrameSize(kVarintDecodedAddress, kFullyEscapedPayload);
215   std::array<std::byte, kExpectedMaxFrameSize> dest_buffer;
216   stream::MemoryWriter writer(dest_buffer);
217 
218   uint64_t address = 0;
219   size_t address_size =
220       varint::Decode(kSmallerEscapedAddress, &address, kAddressFormat);
221   EXPECT_EQ(address, kVarintDecodedAddress);
222   EXPECT_EQ(address_size, 2u);
223 
224   EXPECT_EQ(OkStatus(), WriteUIFrame(address, kFullyEscapedPayload, writer));
225   EXPECT_EQ(writer.size(), kExpectedMaxFrameSize - kTestLimitationsOverhead);
226 }
227 
TEST(EncodedSize,SmallAddress_OffByOne)228 TEST(EncodedSize, SmallAddress_OffByOne) {
229   constexpr auto kSmallerEscapedAddress = bytes::String("\x7e\x7d");
230   // varint::Decode() is not constexpr, so this is a hard-coded and then runtime
231   // validated.
232   constexpr size_t kVarintDecodedAddress = 7999;
233   constexpr size_t kExpectedMaxFrameSize =
234       MaxEncodedFrameSize(kVarintDecodedAddress, kFullyEscapedPayload);
235   std::array<std::byte, kExpectedMaxFrameSize - 1> dest_buffer;
236   stream::MemoryWriter writer(dest_buffer);
237 
238   uint64_t address = 0;
239   size_t address_size =
240       varint::Decode(kSmallerEscapedAddress, &address, kAddressFormat);
241   EXPECT_EQ(address, kVarintDecodedAddress);
242   EXPECT_EQ(address_size, 2u);
243 
244   EXPECT_EQ(Status::ResourceExhausted(),
245             WriteUIFrame(address, kFullyEscapedPayload, writer));
246 }
247 
TEST(DecodedSize,BigAddress_SaturatedPayload)248 TEST(DecodedSize, BigAddress_SaturatedPayload) {
249   constexpr auto kNoEscapePayload =
250       bytes::String("The decoder needs the most space when there's no escapes");
251   constexpr size_t kExpectedMaxFrameSize =
252       MaxEncodedFrameSize(kNoEscapePayload.size());
253   std::array<std::byte, kExpectedMaxFrameSize> dest_buffer;
254   stream::MemoryWriter writer(dest_buffer);
255   EXPECT_EQ(OkStatus(),
256             WriteUIFrame(kNoEscapeAddress, kNoEscapePayload, writer));
257 
258   // Allocate at least enough real buffer space.
259   constexpr size_t kDecoderBufferSize =
260       Decoder::RequiredBufferSizeForFrameSize(kExpectedMaxFrameSize);
261   std::array<std::byte, kDecoderBufferSize> buffer;
262 
263   // Pretend the supported frame size is whatever the final size of the encoded
264   // frame was.
265   const size_t max_frame_size =
266       Decoder::RequiredBufferSizeForFrameSize(writer.size());
267 
268   Decoder decoder(ByteSpan(buffer).first(max_frame_size));
269   for (const std::byte b : writer.WrittenData()) {
270     Result<Frame> frame = decoder.Process(b);
271     if (frame.ok()) {
272       EXPECT_EQ(frame->address(), kNoEscapeAddress);
273       EXPECT_EQ(frame->data().size(), kNoEscapePayload.size());
274       EXPECT_TRUE(std::memcmp(frame->data().data(),
275                               kNoEscapePayload.begin(),
276                               kNoEscapePayload.size()) == 0);
277     }
278   }
279 }
280 
TEST(DecodedSize,BigAddress_OffByOne)281 TEST(DecodedSize, BigAddress_OffByOne) {
282   constexpr auto kNoEscapePayload =
283       bytes::String("The decoder needs the most space when there's no escapes");
284   constexpr size_t kExpectedMaxFrameSize =
285       MaxEncodedFrameSize(kNoEscapePayload.size());
286   std::array<std::byte, kExpectedMaxFrameSize> dest_buffer;
287   stream::MemoryWriter writer(dest_buffer);
288   EXPECT_EQ(OkStatus(),
289             WriteUIFrame(kNoEscapeAddress, kNoEscapePayload, writer));
290 
291   // Allocate at least enough real buffer space.
292   constexpr size_t kDecoderBufferSize =
293       Decoder::RequiredBufferSizeForFrameSize(kExpectedMaxFrameSize);
294   std::array<std::byte, kDecoderBufferSize> buffer;
295 
296   // Pretend the supported frame size is whatever the final size of the encoded
297   // frame was.
298   const size_t max_frame_size =
299       Decoder::RequiredBufferSizeForFrameSize(writer.size());
300 
301   Decoder decoder(ByteSpan(buffer).first(max_frame_size - 1));
302   for (size_t i = 0; i < writer.size(); i++) {
303     Result<Frame> frame = decoder.Process(writer[i]);
304     if (i < writer.size() - 1) {
305       EXPECT_EQ(frame.status(), Status::Unavailable());
306     } else {
307       EXPECT_EQ(frame.status(), Status::ResourceExhausted());
308     }
309   }
310 }
311 
312 }  // namespace
313 }  // namespace pw::hdlc
314