• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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