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