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 #pragma once
15
16 #include <algorithm>
17 #include <cstddef>
18 #include <cstdint>
19 #include <type_traits>
20
21 #include "pw_bytes/span.h"
22 #include "pw_hdlc/internal/protocol.h"
23 #include "pw_span/span.h"
24 #include "pw_varint/varint.h"
25
26 namespace pw::hdlc {
27
28 // Calculates the size of a series of bytes after HDLC escaping.
EscapedSize(ConstByteSpan data)29 constexpr size_t EscapedSize(ConstByteSpan data) {
30 size_t count = 0;
31 for (std::byte b : data) {
32 count += NeedsEscaping(b) ? 2 : 1;
33 }
34 return count;
35 }
36
37 template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
EscapedSize(T val)38 constexpr size_t EscapedSize(T val) {
39 return EscapedSize(as_bytes(span(&val, 1)));
40 }
41
42 // Calculate the buffer space required for encoding an HDLC frame with a given
43 // payload size. This uses worst-case escaping cost as part of the calculation,
44 // which is extremely expensive but guarantees the payload will always fit in
45 // the frame AND the value can be evaluated at compile-time.
46 //
47 // This is NOT a perfect inverse of MaxSafePayloadSize()! This is because
48 // increasing the frame size by one doesn't mean another payload byte can safely
49 // fit since it might need to be escaped.
MaxEncodedFrameSize(size_t max_payload_size)50 constexpr size_t MaxEncodedFrameSize(size_t max_payload_size) {
51 return 2 * sizeof(kFlag) + kMaxEscapedVarintAddressSize + kMaxEscapedFcsSize +
52 kMaxEscapedControlSize + max_payload_size * 2;
53 }
54
55 // Calculates a maximum the on-the-wire encoded size for a given payload
56 // destined for the provided address. Because payload escaping and some of the
57 // address is accounted for, there's significantly less wasted space when
58 // compared to MaxEncodedFrameSize(). However, because the checksum, address,
59 // and control fields are not precisely calculated, there's up to 17 bytes of
60 // potentially unused overhead left over by this estimation. This is done to
61 // improve the speed of this calculation, since precisely calculating all of
62 // this information isn't nearly as efficient.
MaxEncodedFrameSize(uint64_t address,ConstByteSpan payload)63 constexpr size_t MaxEncodedFrameSize(uint64_t address, ConstByteSpan payload) {
64 // The largest on-the-wire escaped varint will never exceed
65 // kMaxEscapedVarintAddressSize since the 10th varint byte can never be an
66 // byte that needs escaping.
67 const size_t max_encoded_address_size =
68 std::min(varint::EncodedSize(address) * 2, kMaxEscapedVarintAddressSize);
69 return 2 * sizeof(kFlag) + max_encoded_address_size + kMaxEscapedFcsSize +
70 kMaxEscapedControlSize + EscapedSize(payload);
71 }
72
73 // Calculates the maximum safe payload size of an HDLC-encoded frame. As long as
74 // a payload does not exceed this value, it will always be safe to encode it
75 // as an HDLC frame in a buffer of size max_frame_size.
76 //
77 // When max_frame_size is too small to safely fit any payload data, this
78 // function returns zero.
79 //
80 // This is NOT a perfect inverse of MaxEncodedFrameSize()! This is because
81 // increasing the frame size by one doesn't mean another payload byte can safely
82 // fit since it might need to be escaped.
MaxSafePayloadSize(size_t max_frame_size)83 constexpr size_t MaxSafePayloadSize(size_t max_frame_size) {
84 constexpr size_t kMaxConstantOverhead =
85 2 * sizeof(kFlag) + kMaxEscapedVarintAddressSize + kMaxEscapedFcsSize +
86 kMaxEscapedControlSize;
87 return max_frame_size < kMaxConstantOverhead
88 ? 0
89 : (max_frame_size - kMaxConstantOverhead) / 2;
90 }
91
92 } // namespace pw::hdlc
93