1 /*
2 * Copyright (c) 2018, 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 defines OpenThread String class.
32 */
33
34 #ifndef STRING_HPP_
35 #define STRING_HPP_
36
37 #include "openthread-core-config.h"
38
39 #include <stdarg.h>
40 #include <stdint.h>
41 #include <stdio.h>
42
43 #include "common/binary_search.hpp"
44 #include "common/code_utils.hpp"
45 #include "common/error.hpp"
46 #include "common/num_utils.hpp"
47
48 namespace ot {
49
50 /**
51 * @addtogroup core-string
52 *
53 * @brief
54 * This module includes definitions for OpenThread String class.
55 *
56 * @{
57 *
58 */
59
60 /**
61 * Represents comparison mode when matching strings.
62 *
63 */
64 enum StringMatchMode : uint8_t
65 {
66 kStringExactMatch, ///< Exact match of characters.
67 kStringCaseInsensitiveMatch, ///< Case insensitive match (uppercase and lowercase characters are treated as equal).
68 };
69
70 /**
71 * Represents string encoding check when copying string.
72 *
73 */
74 enum StringEncodingCheck : uint8_t
75 {
76 kStringNoEncodingCheck, ///< Do not check the string encoding.
77 kStringCheckUtf8Encoding, ///< Validate that string follows UTF-8 encoding.
78 };
79
80 static constexpr char kNullChar = '\0'; ///< null character.
81
82 /**
83 * Returns the number of characters that precede the terminating null character.
84 *
85 * @param[in] aString A pointer to the string.
86 * @param[in] aMaxLength The maximum length in bytes.
87 *
88 * @returns The number of characters that precede the terminating null character or @p aMaxLength,
89 * whichever is smaller. `0` if @p aString is `nullptr`.
90 *
91 */
92 uint16_t StringLength(const char *aString, uint16_t aMaxLength);
93
94 /**
95 * Finds the first occurrence of a given character in a null-terminated string.
96 *
97 * @param[in] aString A pointer to the string.
98 * @param[in] aChar A char to search for in the string.
99 *
100 * @returns The pointer to first occurrence of the @p aChar in @p aString, or `nullptr` if cannot be found.
101 *
102 */
103 const char *StringFind(const char *aString, char aChar);
104
105 /**
106 * Finds the first occurrence of a given sub-string in a null-terminated string.
107 *
108 * @param[in] aString A pointer to the string.
109 * @param[in] aSubString A sub-string to search for.
110 * @param[in] aMode The string comparison mode, exact match or case insensitive match.
111 *
112 * @returns The pointer to first match of the @p aSubString in @p aString (using comparison @p aMode), or `nullptr` if
113 * cannot be found.
114 *
115 */
116 const char *StringFind(const char *aString, const char *aSubString, StringMatchMode aMode = kStringExactMatch);
117
118 /**
119 * Checks whether a null-terminated string starts with a given prefix string.
120 *
121 * @param[in] aString A pointer to the string.
122 * @param[in] aPrefixString A prefix string.
123 * @param[in] aMode The string comparison mode, exact match or case insensitive match.
124 *
125 * @retval TRUE If @p aString starts with @p aPrefixString.
126 * @retval FALSE If @p aString does not start with @p aPrefixString.
127 *
128 */
129 bool StringStartsWith(const char *aString, const char *aPrefixString, StringMatchMode aMode = kStringExactMatch);
130
131 /**
132 * Checks whether a null-terminated string ends with a given character.
133 *
134 * @param[in] aString A pointer to the string.
135 * @param[in] aChar A char to check.
136 *
137 * @retval TRUE If @p aString ends with character @p aChar.
138 * @retval FALSE If @p aString does not end with character @p aChar.
139 *
140 */
141 bool StringEndsWith(const char *aString, char aChar);
142
143 /**
144 * Checks whether a null-terminated string ends with a given sub-string.
145 *
146 * @param[in] aString A pointer to the string.
147 * @param[in] aSubString A sub-string to check against.
148 * @param[in] aMode The string comparison mode, exact match or case insensitive match.
149 *
150 * @retval TRUE If @p aString ends with sub-string @p aSubString.
151 * @retval FALSE If @p aString does not end with sub-string @p aSubString.
152 *
153 */
154 bool StringEndsWith(const char *aString, const char *aSubString, StringMatchMode aMode = kStringExactMatch);
155
156 /**
157 * Checks whether or not two null-terminated strings match.
158 *
159 * @param[in] aFirstString A pointer to the first string.
160 * @param[in] aSecondString A pointer to the second string.
161 * @param[in] aMode The string comparison mode, exact match or case insensitive match.
162 *
163 * @retval TRUE If @p aFirstString matches @p aSecondString using match mode @p aMode.
164 * @retval FALSE If @p aFirstString does not match @p aSecondString using match mode @p aMode.
165 *
166 */
167 bool StringMatch(const char *aFirstString, const char *aSecondString, StringMatchMode aMode = kStringExactMatch);
168
169 /**
170 * Copies a string into a given target buffer with a given size if it fits.
171 *
172 * @param[out] aTargetBuffer A pointer to the target buffer to copy into.
173 * @param[out] aTargetSize The size (number of characters) in @p aTargetBuffer array.
174 * @param[in] aSource A pointer to null-terminated string to copy from. Can be `nullptr` which treated as "".
175 * @param[in] aEncodingCheck Specifies the encoding format check (e.g., UTF-8) to perform.
176 *
177 * @retval kErrorNone The @p aSource fits in the given buffer. @p aTargetBuffer is updated.
178 * @retval kErrorInvalidArgs The @p aSource does not fit in the given buffer.
179 * @retval kErrorParse The @p aSource does not follow the encoding format specified by @p aEncodingCheck.
180 *
181 */
182 Error StringCopy(char *TargetBuffer, uint16_t aTargetSize, const char *aSource, StringEncodingCheck aEncodingCheck);
183
184 /**
185 * Copies a string into a given target buffer with a given size if it fits.
186 *
187 * @tparam kSize The size of buffer.
188 *
189 * @param[out] aTargetBuffer A reference to the target buffer array to copy into.
190 * @param[in] aSource A pointer to null-terminated string to copy from. Can be `nullptr` which treated as "".
191 * @param[in] aEncodingCheck Specifies the encoding format check (e.g., UTF-8) to perform.
192 *
193 * @retval kErrorNone The @p aSource fits in the given buffer. @p aTargetBuffer is updated.
194 * @retval kErrorInvalidArgs The @p aSource does not fit in the given buffer.
195 * @retval kErrorParse The @p aSource does not follow the encoding format specified by @p aEncodingCheck.
196 *
197 */
198 template <uint16_t kSize>
StringCopy(char (& aTargetBuffer)[kSize],const char * aSource,StringEncodingCheck aEncodingCheck=kStringNoEncodingCheck)199 Error StringCopy(char (&aTargetBuffer)[kSize],
200 const char *aSource,
201 StringEncodingCheck aEncodingCheck = kStringNoEncodingCheck)
202 {
203 return StringCopy(aTargetBuffer, kSize, aSource, aEncodingCheck);
204 }
205
206 /**
207 * Parses a decimal number from a string as `uint8_t` and skips over the parsed characters.
208 *
209 * If the string does not start with a digit, `kErrorParse` is returned.
210 *
211 * All the digit characters in the string are parsed until reaching a non-digit character. The pointer `aString` is
212 * updated to point to the first non-digit character after the parsed digits.
213 *
214 * If the parsed number value is larger than @p aMaxValue, `kErrorParse` is returned.
215 *
216 * @param[in,out] aString A reference to a pointer to string to parse.
217 * @param[out] aUint8 A reference to return the parsed value.
218 * @param[in] aMaxValue Maximum allowed value for the parsed number.
219 *
220 * @retval kErrorNone Successfully parsed the number from string. @p aString and @p aUint8 are updated.
221 * @retval kErrorParse Failed to parse the number from @p aString, or parsed number is larger than @p aMaxValue.
222 *
223 */
224 Error StringParseUint8(const char *&aString, uint8_t &aUint8, uint8_t aMaxValue);
225
226 /**
227 * Parses a decimal number from a string as `uint8_t` and skips over the parsed characters.
228 *
229 * If the string does not start with a digit, `kErrorParse` is returned.
230 *
231 * All the digit characters in the string are parsed until reaching a non-digit character. The pointer `aString` is
232 * updated to point to the first non-digit character after the parsed digits.
233 *
234 * If the parsed number value is larger than maximum `uint8_t` value, `kErrorParse` is returned.
235 *
236 * @param[in,out] aString A reference to a pointer to string to parse.
237 * @param[out] aUint8 A reference to return the parsed value.
238 *
239 * @retval kErrorNone Successfully parsed the number from string. @p aString and @p aUint8 are updated.
240 * @retval kErrorParse Failed to parse the number from @p aString, or parsed number is out of range.
241 *
242 */
243 Error StringParseUint8(const char *&aString, uint8_t &aUint8);
244
245 /**
246 * Converts all uppercase letter characters in a given string to lowercase.
247 *
248 * @param[in,out] aString A pointer to the string to convert.
249 *
250 */
251 void StringConvertToLowercase(char *aString);
252
253 /**
254 * Converts all lowercase letter characters in a given string to uppercase.
255 *
256 * @param[in,out] aString A pointer to the string to convert.
257 *
258 */
259 void StringConvertToUppercase(char *aString);
260
261 /**
262 * Converts an uppercase letter character to lowercase.
263 *
264 * If @p aChar is uppercase letter it is converted lowercase. Otherwise, it remains unchanged.
265 *
266 * @param[in] aChar The character to convert
267 *
268 * @returns The character converted to lowercase.
269 *
270 */
271 char ToLowercase(char aChar);
272
273 /**
274 * Converts a lowercase letter character to uppercase.
275 *
276 * If @p aChar is lowercase letter it is converted uppercase. Otherwise, it remains unchanged.
277 *
278 * @param[in] aChar The character to convert
279 *
280 * @returns The character converted to uppercase.
281 *
282 */
283 char ToUppercase(char aChar);
284
285 /**
286 * Coverts a boolean to "yes" or "no" string.
287 *
288 * @param[in] aBool A boolean value to convert.
289 *
290 * @returns The converted string representation of @p aBool ("yes" for TRUE and "no" for FALSE).
291 *
292 */
293 const char *ToYesNo(bool aBool);
294
295 /**
296 * Validates whether a given byte sequence (string) follows UTF-8 encoding.
297 * Control characters are not allowed.
298 *
299 * @param[in] aString A null-terminated byte sequence.
300 *
301 * @retval TRUE The sequence is a valid UTF-8 string.
302 * @retval FALSE The sequence is not a valid UTF-8 string.
303 *
304 */
305 bool IsValidUtf8String(const char *aString);
306
307 /**
308 * Validates whether a given byte sequence (string) follows UTF-8 encoding.
309 * Control characters are not allowed.
310 *
311 * @param[in] aString A byte sequence.
312 * @param[in] aLength Length of the sequence.
313 *
314 * @retval TRUE The sequence is a valid UTF-8 string.
315 * @retval FALSE The sequence is not a valid UTF-8 string.
316 *
317 */
318 bool IsValidUtf8String(const char *aString, size_t aLength);
319
320 /**
321 * This `constexpr` function checks whether two given C strings are in order (alphabetical order).
322 *
323 * This is intended for use from `static_assert`, e.g., checking if a lookup table entries are sorted. It is not
324 * recommended to use this function in other situations as it uses recursion so that it can be `constexpr`.
325 *
326 * @param[in] aFirst The first string.
327 * @param[in] aSecond The second string.
328 *
329 * @retval TRUE If first string is strictly before second string (alphabetical order).
330 * @retval FALSE If first string is not strictly before second string (alphabetical order).
331 *
332 */
AreStringsInOrder(const char * aFirst,const char * aSecond)333 inline constexpr bool AreStringsInOrder(const char *aFirst, const char *aSecond)
334 {
335 return (*aFirst < *aSecond)
336 ? true
337 : ((*aFirst > *aSecond) || (*aFirst == '\0') ? false : AreStringsInOrder(aFirst + 1, aSecond + 1));
338 }
339
340 /**
341 * Implements writing to a string buffer.
342 *
343 */
344 class StringWriter
345 {
346 public:
347 /**
348 * Initializes the object as cleared on the provided buffer.
349 *
350 * @param[in] aBuffer A pointer to the char buffer to write into.
351 * @param[in] aSize The size of @p aBuffer.
352 *
353 */
354 StringWriter(char *aBuffer, uint16_t aSize);
355
356 /**
357 * Clears the string writer.
358 *
359 * @returns The string writer.
360 *
361 */
362 StringWriter &Clear(void);
363
364 /**
365 * Returns whether the output is truncated.
366 *
367 * @note If the output is truncated, the buffer is still null-terminated.
368 *
369 * @retval true The output is truncated.
370 * @retval false The output is not truncated.
371 *
372 */
IsTruncated(void) const373 bool IsTruncated(void) const { return mLength >= mSize; }
374
375 /**
376 * Gets the length of the wanted string.
377 *
378 * Similar to `strlen()` the length does not include the null character at the end of the string.
379 *
380 * @returns The string length.
381 *
382 */
GetLength(void) const383 uint16_t GetLength(void) const { return mLength; }
384
385 /**
386 * Returns the size (number of chars) in the buffer.
387 *
388 * @returns The size of the buffer.
389 *
390 */
GetSize(void) const391 uint16_t GetSize(void) const { return mSize; }
392
393 /**
394 * Appends `printf()` style formatted data to the buffer.
395 *
396 * @param[in] aFormat A pointer to the format string.
397 * @param[in] ... Arguments for the format specification.
398 *
399 * @returns The string writer.
400 *
401 */
402 StringWriter &Append(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3);
403
404 /**
405 * Appends `printf()` style formatted data to the buffer.
406 *
407 * @param[in] aFormat A pointer to the format string.
408 * @param[in] aArgs Arguments for the format specification (as `va_list`).
409 *
410 * @returns The string writer.
411 *
412 */
413 StringWriter &AppendVarArgs(const char *aFormat, va_list aArgs);
414
415 /**
416 * Appends an array of bytes in hex representation (using "%02x" style) to the buffer.
417 *
418 * @param[in] aBytes A pointer to buffer containing the bytes to append.
419 * @param[in] aLength The length of @p aBytes buffer (in bytes).
420 *
421 * @returns The string writer.
422 *
423 */
424 StringWriter &AppendHexBytes(const uint8_t *aBytes, uint16_t aLength);
425
426 /**
427 * Appends a given character a given number of times.
428 *
429 * @param[in] aChar The character to append.
430 * @param[in] aCount Number of times to append @p aChar.
431 *
432 */
433 StringWriter &AppendCharMultipleTimes(char aChar, uint16_t aCount);
434
435 /**
436 * Converts all uppercase letter characters in the string to lowercase.
437 *
438 */
ConvertToLowercase(void)439 void ConvertToLowercase(void) { StringConvertToLowercase(mBuffer); }
440
441 /**
442 * Converts all lowercase letter characters in the string to uppercase.
443 *
444 */
ConvertToUppercase(void)445 void ConvertToUppercase(void) { StringConvertToUppercase(mBuffer); }
446
447 private:
448 char *mBuffer;
449 uint16_t mLength;
450 const uint16_t mSize;
451 };
452
453 /**
454 * Defines a fixed-size string.
455 *
456 */
457 template <uint16_t kSize> class String : public StringWriter
458 {
459 static_assert(kSize > 0, "String buffer cannot be empty.");
460
461 public:
462 /**
463 * Initializes the string as empty.
464 *
465 */
String(void)466 String(void)
467 : StringWriter(mBuffer, sizeof(mBuffer))
468 {
469 }
470
471 /**
472 * Returns the string as a null-terminated C string.
473 *
474 * @returns The null-terminated C string.
475 *
476 */
AsCString(void) const477 const char *AsCString(void) const { return mBuffer; }
478
479 private:
480 char mBuffer[kSize];
481 };
482
483 /**
484 * Provides helper methods to convert from a set of `uint16_t` values (e.g., a non-sequential `enum`) to
485 * string using binary search in a lookup table.
486 *
487 */
488 class Stringify : public BinarySearch
489 {
490 public:
491 /**
492 * Represents a entry in the lookup table.
493 *
494 */
495 class Entry
496 {
497 friend class BinarySearch;
498
499 public:
500 uint16_t mKey; ///< The key value.
501 const char *mString; ///< The associated string.
502
503 private:
Compare(uint16_t aKey) const504 int Compare(uint16_t aKey) const { return ThreeWayCompare(aKey, mKey); }
505
AreInOrder(const Entry & aFirst,const Entry & aSecond)506 constexpr static bool AreInOrder(const Entry &aFirst, const Entry &aSecond)
507 {
508 return aFirst.mKey < aSecond.mKey;
509 }
510 };
511
512 /**
513 * Looks up a key in a given sorted table array (using binary search) and return the associated
514 * strings with the key.
515 *
516 * @note This method requires the array to be sorted, otherwise its behavior is undefined.
517 *
518 * @tparam kLength The array length (number of entries in the array).
519 *
520 * @param[in] aKey The key to search for within the table.
521 * @param[in] aTable A reference to an array of `kLength` entries.
522 * @param[in] aNotFound A C string to return if @p aKey was not found in the table.
523 *
524 * @returns The associated string with @p aKey in @p aTable if found, or @p aNotFound otherwise.
525 *
526 */
527 template <uint16_t kLength>
Lookup(uint16_t aKey,const Entry (& aTable)[kLength],const char * aNotFound="unknown")528 static const char *Lookup(uint16_t aKey, const Entry (&aTable)[kLength], const char *aNotFound = "unknown")
529 {
530 const Entry *entry = BinarySearch::Find(aKey, aTable);
531
532 return (entry != nullptr) ? entry->mString : aNotFound;
533 }
534
535 Stringify(void) = delete;
536 };
537
538 /**
539 * @}
540 *
541 */
542
543 } // namespace ot
544
545 #endif // STRING_HPP_
546