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