Line data Source code
1 : /**
2 : * Copyright (c) 2025 Project CHIP Authors
3 : *
4 : * Licensed under the Apache License, Version 2.0 (the "License");
5 : * you may not use this file except in compliance with the License.
6 : * You may obtain a copy of the License at
7 : *
8 : * http://www.apache.org/licenses/LICENSE-2.0
9 : *
10 : * Unless required by applicable law or agreed to in writing, software
11 : * distributed under the License is distributed on an "AS IS" BASIS,
12 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : * See the License for the specific language governing permissions and
14 : * limitations under the License.
15 : */
16 : #pragma once
17 :
18 : #include <app/ConcreteAttributePath.h>
19 : #include <lib/core/CHIPEncoding.h>
20 : #include <lib/core/CHIPError.h>
21 : #include <lib/core/CHIPSafeCasts.h>
22 : #include <lib/support/CodeUtils.h>
23 : #include <lib/support/Span.h>
24 :
25 : #include <algorithm>
26 : #include <cstdint>
27 :
28 : namespace chip {
29 : namespace app {
30 : namespace Storage {
31 :
32 : /// Describes how to set/get the length of pascal strings
33 : /// given a PREFIX_LEN in bytes.
34 : template <size_t PREFIX_LEN>
35 : struct PascalPrefixOperations;
36 :
37 : template <>
38 : struct PascalPrefixOperations<1>
39 : {
40 : using LengthType = uint8_t;
41 : static constexpr LengthType kInvalidLength = 0xFF;
42 :
43 0 : static LengthType GetLength(const uint8_t * buffer) { return *buffer; }
44 : static void SetLength(uint8_t * buffer, LengthType size) { *buffer = static_cast<uint8_t>(size); }
45 :
46 : // Casts for chars as well
47 : static LengthType GetLength(const char * buffer) { return GetLength(Uint8::from_const_char(buffer)); }
48 : static void SetLength(char * buffer, LengthType size) { SetLength(Uint8::from_char(buffer), size); }
49 : };
50 :
51 : template <>
52 : struct PascalPrefixOperations<2>
53 : {
54 : using LengthType = uint16_t;
55 : static constexpr LengthType kInvalidLength = 0xFFFF;
56 :
57 0 : static LengthType GetLength(const uint8_t * buffer) { return Encoding::LittleEndian::Get16(buffer); }
58 : static void SetLength(uint8_t * buffer, LengthType size) { Encoding::LittleEndian::Put16(buffer, size); }
59 :
60 : // Casts for chars as well
61 : static LengthType GetLength(const char * buffer) { return GetLength(Uint8::from_const_char(buffer)); }
62 : static void SetLength(char * buffer, LengthType size) { SetLength(Uint8::from_char(buffer), size); }
63 : };
64 :
65 : /// Interprets a byte buffer as a pascal buffer:
66 : /// - a prefix that specifies the length of the data
67 : /// - the following characters that contain the data
68 : ///
69 : /// Parameters:
70 : /// T - the underlying data type, generally uint8_t for bytes or char for strings
71 : /// PREFIX_LEN - the size of the pascal prefix (generally 1 or 2 bytes)
72 : template <typename T, uint8_t PREFIX_LEN>
73 : class PascalBuffer
74 : {
75 : public:
76 : using LengthType = typename PascalPrefixOperations<PREFIX_LEN>::LengthType;
77 : static constexpr LengthType kInvalidLength = PascalPrefixOperations<PREFIX_LEN>::kInvalidLength;
78 :
79 : static_assert(sizeof(T) == 1);
80 :
81 : PascalBuffer(PascalBuffer &&) = default;
82 : PascalBuffer(const PascalBuffer &) = default;
83 :
84 : template <size_t N>
85 : PascalBuffer(T (&data)[N]) : mData(data), mMaxSize(N - PREFIX_LEN)
86 : {
87 : static_assert(N >= PREFIX_LEN);
88 : static_assert(N <= kInvalidLength);
89 : }
90 :
91 : /// Returns the content of the pascal string.
92 : /// Uses the prefix size information
93 : Span<T> Content() { return { mData + PREFIX_LEN, GetLength() }; }
94 : Span<const T> Content() const { return { mData + PREFIX_LEN, GetLength() }; }
95 :
96 : /// Accesses the "PASCAL" string (i.e. valid data including the string prefix)
97 : Span<const T> PascalContent() const { return { mData, static_cast<size_t>(GetLength() + PREFIX_LEN) }; }
98 :
99 : /// Access to the full buffer. does NOT take into account current size
100 : /// and includes the "size prefix"
101 : Span<T> Buffer() { return { mData, static_cast<size_t>(mMaxSize + PREFIX_LEN) }; }
102 :
103 : LengthType GetLength() const
104 : {
105 : const LengthType length = PascalPrefixOperations<PREFIX_LEN>::GetLength(mData);
106 : if (length == kInvalidLength)
107 : {
108 : return 0;
109 : }
110 : return std::min<LengthType>(mMaxSize, length);
111 : }
112 :
113 : // Returns true if the length was valid and could be set
114 : bool SetLength(LengthType len)
115 : {
116 : if (len != kInvalidLength)
117 : {
118 : VerifyOrReturnError(len <= mMaxSize, false);
119 : }
120 : PascalPrefixOperations<PREFIX_LEN>::SetLength(mData, len);
121 : return true;
122 : }
123 : void SetNull() { (void) SetLength(kInvalidLength); }
124 : bool IsNull() const { return PascalPrefixOperations<PREFIX_LEN>::GetLength(mData) == kInvalidLength; }
125 :
126 : // Returns true if the length of the input buffer fit in the
127 : // pascal buffer (and could be set)
128 : bool SetValue(Span<const T> value)
129 : {
130 : VerifyOrReturnValue(value.size() < kInvalidLength, false);
131 : VerifyOrReturnValue(SetLength(static_cast<LengthType>(value.size())), false);
132 : memcpy(mData + PREFIX_LEN, value.data(), value.size());
133 : return true;
134 : }
135 :
136 : /// Checks if the given span is a valid Pascal string: i.e. its size prefix
137 : /// is either Invalid (i.e. null marker) or it has a size that fits in the buffer
138 0 : static bool IsValid(Span<const T> span)
139 : {
140 0 : VerifyOrReturnValue(span.size() >= PREFIX_LEN, false);
141 0 : LengthType len = PascalPrefixOperations<PREFIX_LEN>::GetLength(span.data());
142 0 : return len == kInvalidLength || (static_cast<size_t>(len + PREFIX_LEN) <= span.size());
143 : }
144 :
145 : private:
146 : T * mData;
147 : const LengthType mMaxSize;
148 : };
149 :
150 : using ShortPascalString = PascalBuffer<char, 1>;
151 : using ShortPascalBytes = PascalBuffer<uint8_t, 1>;
152 : using LongPascalString = PascalBuffer<char, 2>;
153 : using LongPascalBytes = PascalBuffer<uint8_t, 2>;
154 :
155 : } // namespace Storage
156 : } // namespace app
157 : } // namespace chip
|