1 /* 2 * Copyright (c) 2021, The OpenThread Authors. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. Neither the name of the copyright holder nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /** 30 * @file 31 * This file includes definitions for a `Data` and `MutableData`. 32 */ 33 34 #ifndef DATA_HPP_ 35 #define DATA_HPP_ 36 37 #include "openthread-core-config.h" 38 39 #include <stdint.h> 40 #include <string.h> 41 42 #include "common/clearable.hpp" 43 #include "common/code_utils.hpp" 44 #include "common/const_cast.hpp" 45 #include "common/equatable.hpp" 46 #include "common/error.hpp" 47 #include "common/type_traits.hpp" 48 49 namespace ot { 50 51 /** 52 * This enumeration type is used as the template parameter in `Data` and `MutableData` to indicate the `uint` type to 53 * use for the data length. 54 * 55 */ 56 enum DataLengthType : uint8_t 57 { 58 kWithUint8Length, ///< Use `uint8_t` for data length. 59 kWithUint16Length, ///< Use `uint16_t` for data length 60 }; 61 62 /** 63 * This type specifies a function pointer which matches two given bytes. 64 * 65 * Such a function is used as a parameter in `Data::MatchesByteIn()` method. This can be used to relax the definition 66 * of a match when comparing data bytes, e.g., can be used for case-insensitive string comparison. 67 * 68 * @param[in] aFirst A first byte. 69 * @param[in] aSecond A second byte. 70 * 71 * @retval TRUE if @p aFirst matches @p aSecond. 72 * @retval FALSE if @p aFirst does not match @p aSecond. 73 * 74 */ 75 typedef bool (*ByteMatcher)(uint8_t aFirst, uint8_t aSecond); 76 77 /** 78 * This class implements common utility methods used by `Data` and `MutableData`. 79 * 80 */ 81 class DataUtils 82 { 83 protected: 84 DataUtils(void) = default; 85 static bool MatchBytes(const uint8_t *aFirstBuffer, 86 const uint8_t *aSecondBuffer, 87 uint16_t aLength, 88 ByteMatcher aMatcher); 89 }; 90 91 template <DataLengthType kDataLengthType> class MutableData; 92 93 /** 94 * This type represents a generic `Data` which is simply a wrapper over a pointer to a buffer with a given data length. 95 * 96 * The data length can be either `uint8_t` or `uint16_t` (determined by the template parameter `kDataLengthType`). 97 * 98 * While a `Data` instance itself can change (for example, it can be updated to point to another buffer), it always 99 * treats the content of the buffer as immutable. 100 * 101 * A `Data` instance MUST be initialized (using any of the `Init()` methods) before calling any other methods on the 102 * instance (e.g., `GetBytes()` or `GetLength()`), otherwise the behavior is undefined. 103 * 104 * @tparam kDataLengthType Determines the data length type (`uint8_t` or `uint16_t`). 105 * 106 */ 107 template <DataLengthType kDataLengthType> 108 class Data : public Clearable<Data<kDataLengthType>>, public Unequatable<Data<kDataLengthType>>, private DataUtils 109 { 110 friend class MutableData<kDataLengthType>; 111 112 public: 113 /** 114 * This type represents the data length type (`uint8_t` or `uint16_t`). 115 * 116 */ 117 using LengthType = typename TypeTraits::Conditional<kDataLengthType == kWithUint8Length, uint8_t, uint16_t>::Type; 118 119 /** 120 * This method initializes the `Data` to point to a given buffer with a given length. 121 * 122 * @param[in] aBuffer A pointer to a buffer containing the data. 123 * @param[in] aLength The data length (number of bytes in @p aBuffer) 124 * 125 */ Init(const void * aBuffer,LengthType aLength)126 void Init(const void *aBuffer, LengthType aLength) 127 { 128 mBuffer = static_cast<const uint8_t *>(aBuffer); 129 mLength = aLength; 130 } 131 132 /** 133 * This method initializes the `Data` to point to a range of bytes in a given buffer. 134 * 135 * The range is specified by the pointers to its start @p aStart and its end @p aEnd. `Data` will point to the 136 * bytes in the buffer from @p aStart up to but excluding @p aEnd (i.e., `aStart <= bytes < aEnd`). 137 * 138 * @param[in] aStart Pointer to the start of the range. 139 * @param[in] aEnd Pointer to the end of the range. 140 * 141 */ InitFromRange(const uint8_t * aStart,const uint8_t * aEnd)142 void InitFromRange(const uint8_t *aStart, const uint8_t *aEnd) 143 { 144 Init(aStart, static_cast<LengthType>(aEnd - aStart)); 145 } 146 147 /** 148 * This template method initializes the `Data` to point to the content of an object. 149 * 150 * @tparm ObjectType The object type (MUST not be a pointer type). 151 * 152 * @param[in] aObject The object to initialize the `Data` with. 153 * 154 */ InitFrom(const ObjectType & aObject)155 template <typename ObjectType> void InitFrom(const ObjectType &aObject) 156 { 157 static_assert(!TypeTraits::IsPointer<ObjectType>::kValue, "ObjectType MUST not be a pointer"); 158 Init(&aObject, sizeof(aObject)); 159 } 160 161 /** 162 * This method returns a pointer to the data bytes buffer. 163 * 164 * @returns A pointer to the data bytes buffer (can be `nullptr` if `Data` is cleared). 165 * 166 */ GetBytes(void) const167 const uint8_t *GetBytes(void) const { return mBuffer; } 168 169 /** 170 * This method returns the data length. 171 * 172 * @returns The data length (number of bytes). 173 * 174 */ GetLength(void) const175 LengthType GetLength(void) const { return mLength; } 176 177 /** 178 * This method sets the data length. 179 * 180 * @param[in] aLength The data length (number of bytes). 181 * 182 */ SetLength(LengthType aLength)183 void SetLength(LengthType aLength) { mLength = aLength; } 184 185 /** 186 * This method copies the `Data` bytes to a given buffer. 187 * 188 * It is up to the caller to ensure that @p aBuffer has enough space for the current data length. 189 * 190 * @param[out] aBuffer The buffer to copy the bytes into. 191 * 192 */ CopyBytesTo(void * aBuffer) const193 void CopyBytesTo(void *aBuffer) const { memcpy(aBuffer, mBuffer, mLength); } 194 195 /** 196 * This method compares the `Data` content with the bytes from a given buffer. 197 * 198 * It is up to the caller to ensure that @p aBuffer has enough bytes to compare with the current data length. 199 * 200 * @param[in] aBuffer A pointer to a buffer to compare with the data. 201 * 202 * @retval TRUE The `Data` content matches the bytes in @p aBuffer. 203 * @retval FALSE The `Data` content does not match the byes in @p aBuffer. 204 * 205 */ MatchesBytesIn(const void * aBuffer) const206 bool MatchesBytesIn(const void *aBuffer) const { return memcmp(mBuffer, aBuffer, mLength) == 0; } 207 208 /** 209 * This method compares the `Data` content with the bytes from a given buffer using a given `Matcher` function. 210 * 211 * It is up to the caller to ensure that @p aBuffer has enough bytes to compare with the current data length. 212 * 213 * @param[in] aBuffer A pointer to a buffer to compare with the data. 214 * @param[in] aMatcher A `ByteMatcher` function to match the bytes. If `nullptr`, bytes are compared directly. 215 * 216 * @retval TRUE The `Data` content matches the bytes in @p aBuffer. 217 * @retval FALSE The `Data` content does not match the byes in @p aBuffer. 218 * 219 */ MatchesBytesIn(const void * aBuffer,ByteMatcher aMatcher)220 bool MatchesBytesIn(const void *aBuffer, ByteMatcher aMatcher) 221 { 222 return MatchBytes(mBuffer, static_cast<const uint8_t *>(aBuffer), mLength, aMatcher); 223 } 224 225 /** 226 * This method overloads operator `==` to compare the `Data` content with the content from another one. 227 * 228 * @param[in] aOtherData The other `Data` to compare with. 229 * 230 * @retval TRUE The two `Data` instances have matching content (same length and same bytes). 231 * @retval FALSE The two `Data` instances do not have matching content. 232 * 233 */ operator ==(const Data & aOtherData) const234 bool operator==(const Data &aOtherData) const 235 { 236 return (mLength == aOtherData.mLength) && MatchesBytesIn(aOtherData.mBuffer); 237 } 238 239 /** 240 * This method checks whether the `Data` starts with the same byte content as from another `Data` instance. 241 * 242 * This method checks that the `Data` instance contains the same bytes as @p aOtherData but allows it to have 243 * additional bytes at the end. 244 * 245 * @param[in] aOtherData The other `Data` to compare with. 246 * 247 * @retval TRUE This `Data` starts with the same byte content as in @p aOtherData. 248 * @retval FALSE This `Data` does not start with the same byte content as in @p aOtherData. 249 * 250 */ StartsWith(const Data & aOtherData) const251 bool StartsWith(const Data &aOtherData) const 252 { 253 return (mLength >= aOtherData.mLength) && aOtherData.MatchesBytesIn(mBuffer); 254 } 255 256 private: 257 const uint8_t *mBuffer; 258 LengthType mLength; 259 }; 260 261 /** 262 * This type represents a generic `MutableData` which is simply a wrapper over a pointer to a buffer with a given data 263 * length. 264 * 265 * It inherits from `Data` but unlike `Data` which treats its buffer content as immutable, `MutableData` allows its 266 * data buffer content to be changed. 267 * 268 * A `MutableData` instance MUST be initialized (using any of the `Init()` methods) before calling any other methods 269 * (e.g., `GetBytes()` or `GetLength()`), otherwise the behavior is undefined. 270 271 * 272 */ 273 template <DataLengthType kDataLengthType> class MutableData : public Data<kDataLengthType> 274 { 275 using Base = Data<kDataLengthType>; 276 using Base::mBuffer; 277 using Base::mLength; 278 279 public: 280 /** 281 * This type represents the data length type (`uint8_t` or `uint16_t`). 282 * 283 */ 284 using LengthType = typename Base::LengthType; 285 286 /** 287 * This method initializes the `MutableData` to point to a given buffer with a given length. 288 * 289 * @param[in] aBuffer A pointer to a buffer containing the data. 290 * @param[in] aLength The data length (number of bytes in @p aBuffer) 291 * 292 */ Init(void * aBuffer,LengthType aLength)293 void Init(void *aBuffer, LengthType aLength) { Base::Init(aBuffer, aLength); } 294 295 /** 296 * This method initializes the `MutableData` to point to a range of bytes in a given buffer. 297 * 298 * The range is specified by the pointers to its start @p aStart and its end @p aEnd. `Data` will point to the 299 * bytes in the buffer from @p aStart up to but excluding @p aEnd (i.e., `aStart <= bytes < aEnd`). 300 * 301 * @param[in] aStart Pointer to the start of the range. 302 * @param[in] aEnd Pointer to the end of the range. 303 * 304 */ InitFormRange(uint8_t * aStart,uint8_t * aEnd)305 void InitFormRange(uint8_t *aStart, uint8_t *aEnd) { Base::InitFormRange(aStart, aEnd); } 306 307 /** 308 * This template method initializes the `MutableData` to point to the content of an object. 309 * 310 * @tparm ObjectType The object type (MUST not be a pointer type). 311 * 312 * @param[in] aObject The object to initialize the `MutableData` with. 313 * 314 */ InitFrom(ObjectType & aObject)315 template <typename ObjectType> void InitFrom(ObjectType &aObject) 316 { 317 static_assert(!TypeTraits::IsPointer<ObjectType>::kValue, "ObjectType MUST not be a pointer"); 318 Init(&aObject, sizeof(aObject)); 319 } 320 321 /** 322 * This method returns a pointer to the data bytes buffer. 323 * 324 * @returns A pointer to the data bytes buffer (can be `nullptr` if `Data` is empty or uninitialized). 325 * 326 */ GetBytes(void)327 uint8_t *GetBytes(void) { return AsNonConst(Base::GetBytes()); } 328 329 /** 330 * This method returns a pointer to the data bytes buffer. 331 * 332 * @returns A pointer to the data bytes buffer (can be `nullptr` if `Data` is empty or uninitialized). 333 * 334 */ GetBytes(void) const335 const uint8_t *GetBytes(void) const { return Base::GetBytes(); } 336 337 /** 338 * This method clears all the bytes (sets them to zero) in the buffer pointed by the `MutableData`. 339 * 340 */ ClearBytes(void)341 void ClearBytes(void) { memset(GetBytes(), 0, mLength); } 342 343 /** 344 * This method copies the bytes from a given buffer into the `MutableData` buffer. 345 * 346 * If the current `MutableData` length is larger than or equal to @p aLength, then all the bytes are copied 347 * from @p aBuffer into the buffer of `MutableData` and the `MutableData`'s length is changed to @p aLength. 348 * 349 * If the current `MutableData` length is smaller than @p aLength, then the method returns `kErrorNoBufs` but still 350 * copies as many bytes as can fit. 351 * 352 * @param[in] aBuffer A pointer to a buffer to copy from. 353 * @param[in] aLength The length of @p aBuffer (number of bytes). 354 * 355 * @retval kErrorNone Successfully copied the bytes into `MutableData` buffer and adjusted its length. 356 * @retval kErrorNoBufs `MutableData` buffer cannot fit the given @p aLength bytes. 357 * 358 */ CopyBytesFrom(const uint8_t * aBuffer,LengthType aLength)359 Error CopyBytesFrom(const uint8_t *aBuffer, LengthType aLength) 360 { 361 Error error = (mLength >= aLength) ? kErrorNone : kErrorNoBufs; 362 363 mLength = OT_MIN(mLength, aLength); 364 memcpy(AsNonConst(mBuffer), aBuffer, mLength); 365 366 return error; 367 } 368 369 /** 370 * This method copies the bytes from an given `Data` instance into the `MutableData` buffer. 371 * 372 * If the current `MutableData` length is larger than or equal to the @p aData length, then all the bytes are copied 373 * from @p aData into the buffer of `MutableData` and the `MutableData`'s length is adjusted accordingly. 374 * 375 * If the current `MutableData` length is smaller than @p aData length, then as many bytes as can fit are copied 376 * and the method returns `kErrorNoBufs`. 377 * 378 * @param[in] aData A `Data` instance to copy the content from. 379 * 380 * @retval kErrorNone Successfully copied the bytes into `MutableData` buffer and adjusted its length. 381 * @retval kErrorNoBufs `MutableData` buffer cannot fit the given @p aData bytes. 382 * 383 */ CopyBytesFrom(const Data<kDataLengthType> & aData)384 Error CopyBytesFrom(const Data<kDataLengthType> &aData) 385 { 386 return CopyBytesFrom(aData.GetBytes(), aData.GetLength()); 387 } 388 }; 389 390 } // namespace ot 391 392 #endif // DATA_HPP_ 393