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  * This enumeration 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 static constexpr char kNullChar = '\0'; ///< null character.
71 
72 /**
73  * This function returns the number of characters that precede the terminating null character.
74  *
75  * @param[in] aString      A pointer to the string.
76  * @param[in] aMaxLength   The maximum length in bytes.
77  *
78  * @returns The number of characters that precede the terminating null character or @p aMaxLength, whichever is
79  *          smaller.
80  *
81  */
82 uint16_t StringLength(const char *aString, uint16_t aMaxLength);
83 
84 /**
85  * This function finds the first occurrence of a given character in a null-terminated string.
86  *
87  * @param[in] aString     A pointer to the string.
88  * @param[in] aChar       A char to search for in the string.
89  *
90  * @returns The pointer to first occurrence of the @p aChar in @p aString, or `nullptr` if cannot be found.
91  *
92  */
93 const char *StringFind(const char *aString, char aChar);
94 
95 /**
96  * This function finds the first occurrence of a given sub-string in a null-terminated string.
97  *
98  * @param[in] aString     A pointer to the string.
99  * @param[in] aSubString  A sub-string to search for.
100  * @param[in] aMode       The string comparison mode, exact match or case insensitive match.
101  *
102  * @returns The pointer to first match of the @p aSubString in @p aString (using comparison @p aMode), or `nullptr` if
103  *          cannot be found.
104  *
105  */
106 const char *StringFind(const char *aString, const char *aSubString, StringMatchMode aMode = kStringExactMatch);
107 
108 /**
109  * This function checks whether a null-terminated string starts with a given prefix string.
110  *
111  * @param[in] aString         A pointer to the string.
112  * @param[in] aPrefixString   A prefix string.
113  * @param[in] aMode           The string comparison mode, exact match or case insensitive match.
114  *
115  * @retval TRUE   If @p aString starts with @p aPrefixString.
116  * @retval FALSE  If @p aString does not start with @p aPrefixString.
117  *
118  */
119 bool StringStartsWith(const char *aString, const char *aPrefixString, StringMatchMode aMode = kStringExactMatch);
120 
121 /**
122  * This function checks whether a null-terminated string ends with a given character.
123  *
124  * @param[in] aString  A pointer to the string.
125  * @param[in] aChar    A char to check.
126  *
127  * @retval TRUE   If @p aString ends with character @p aChar.
128  * @retval FALSE  If @p aString does not end with character @p aChar.
129  *
130  */
131 bool StringEndsWith(const char *aString, char aChar);
132 
133 /**
134  * This function checks whether a null-terminated string ends with a given sub-string.
135  *
136  * @param[in] aString      A pointer to the string.
137  * @param[in] aSubString   A sub-string to check against.
138  * @param[in] aMode        The string comparison mode, exact match or case insensitive match.
139  *
140  * @retval TRUE   If @p aString ends with sub-string @p aSubString.
141  * @retval FALSE  If @p aString does not end with sub-string @p aSubString.
142  *
143  */
144 bool StringEndsWith(const char *aString, const char *aSubString, StringMatchMode aMode = kStringExactMatch);
145 
146 /**
147  * This function checks whether or not two null-terminated strings match.
148  *
149  * @param[in] aFirstString   A pointer to the first string.
150  * @param[in] aSecondString  A pointer to the second string.
151  * @param[in] aMode          The string comparison mode, exact match or case insensitive match.
152  *
153  * @retval TRUE   If @p aFirstString matches @p aSecondString using match mode @p aMode.
154  * @retval FALSE  If @p aFirstString does not match @p aSecondString using match mode @p aMode.
155  *
156  */
157 bool StringMatch(const char *aFirstString, const char *aSecondString, StringMatchMode aMode = kStringExactMatch);
158 
159 /**
160  * This function parses a decimal number from a string as `uint8_t` and skips over the parsed characters.
161  *
162  * If the string does not start with a digit, `kErrorParse` is returned.
163  *
164  * All the digit characters in the string are parsed until reaching a non-digit character. The pointer `aString` is
165  * updated to point to the first non-digit character after the parsed digits.
166  *
167  * If the parsed number value is larger than @p aMaxValue, `kErrorParse` is returned.
168  *
169  * @param[in,out] aString    A reference to a pointer to string to parse.
170  * @param[out]    aUint8     A reference to return the parsed value.
171  * @param[in]     aMaxValue  Maximum allowed value for the parsed number.
172  *
173  * @retval kErrorNone   Successfully parsed the number from string. @p aString and @p aUint8 are updated.
174  * @retval kErrorParse  Failed to parse the number from @p aString, or parsed number is larger than @p aMaxValue.
175  *
176  */
177 Error StringParseUint8(const char *&aString, uint8_t &aUint8, uint8_t aMaxValue);
178 
179 /**
180  * This function parses a decimal number from a string as `uint8_t` and skips over the parsed characters.
181  *
182  * If the string does not start with a digit, `kErrorParse` is returned.
183  *
184  * All the digit characters in the string are parsed until reaching a non-digit character. The pointer `aString` is
185  * updated to point to the first non-digit character after the parsed digits.
186  *
187  * If the parsed number value is larger than maximum `uint8_t` value, `kErrorParse` is returned.
188  *
189  * @param[in,out] aString    A reference to a pointer to string to parse.
190  * @param[out]    aUint8     A reference to return the parsed value.
191  *
192  * @retval kErrorNone   Successfully parsed the number from string. @p aString and @p aUint8 are updated.
193  * @retval kErrorParse  Failed to parse the number from @p aString, or parsed number is out of range.
194  *
195  */
196 Error StringParseUint8(const char *&aString, uint8_t &aUint8);
197 
198 /**
199  * This function converts all uppercase letter characters in a given string to lowercase.
200  *
201  * @param[in,out] aString   A pointer to the string to convert.
202  *
203  */
204 void StringConvertToLowercase(char *aString);
205 
206 /**
207  * This function converts all lowercase letter characters in a given string to uppercase.
208  *
209  * @param[in,out] aString   A pointer to the string to convert.
210  *
211  */
212 void StringConvertToUppercase(char *aString);
213 
214 /**
215  * This function converts an uppercase letter character to lowercase.
216  *
217  * If @p aChar is uppercase letter it is converted lowercase. Otherwise, it remains unchanged.
218  *
219  * @param[in] aChar   The character to convert
220  *
221  * @returns The character converted to lowercase.
222  *
223  */
224 char ToLowercase(char aChar);
225 
226 /**
227  * This function converts a lowercase letter character to uppercase.
228  *
229  * If @p aChar is lowercase letter it is converted uppercase. Otherwise, it remains unchanged.
230  *
231  * @param[in] aChar   The character to convert
232  *
233  * @returns The character converted to uppercase.
234  *
235  */
236 char ToUppercase(char aChar);
237 
238 /**
239  * This function coverts a boolean to "yes" or "no" string.
240  *
241  * @param[in] aBool  A boolean value to convert.
242  *
243  * @returns The converted string representation of @p aBool ("yes" for TRUE and "no" for FALSE).
244  *
245  */
246 const char *ToYesNo(bool aBool);
247 
248 /**
249  * This function validates whether a given byte sequence (string) follows UTF-8 encoding.
250  * Control characters are not allowed.
251  *
252  * @param[in]  aString  A null-terminated byte sequence.
253  *
254  * @retval TRUE   The sequence is a valid UTF-8 string.
255  * @retval FALSE  The sequence is not a valid UTF-8 string.
256  *
257  */
258 bool IsValidUtf8String(const char *aString);
259 
260 /**
261  * This function validates whether a given byte sequence (string) follows UTF-8 encoding.
262  * Control characters are not allowed.
263  *
264  * @param[in]  aString  A byte sequence.
265  * @param[in]  aLength  Length of the sequence.
266  *
267  * @retval TRUE   The sequence is a valid UTF-8 string.
268  * @retval FALSE  The sequence is not a valid UTF-8 string.
269  *
270  */
271 bool IsValidUtf8String(const char *aString, size_t aLength);
272 
273 /**
274  * This `constexpr` function checks whether two given C strings are in order (alphabetical order).
275  *
276  * This is intended for use from `static_assert`, e.g., checking if a lookup table entries are sorted. It is not
277  * recommended to use this function in other situations as it uses recursion so that it can be `constexpr`.
278  *
279  * @param[in] aFirst    The first string.
280  * @param[in] aSecond   The second string.
281  *
282  * @retval TRUE  If first string is strictly before second string (alphabetical order).
283  * @retval FALSE If first string is not strictly before second string (alphabetical order).
284  *
285  */
AreStringsInOrder(const char * aFirst,const char * aSecond)286 inline constexpr bool AreStringsInOrder(const char *aFirst, const char *aSecond)
287 {
288     return (*aFirst < *aSecond)
289                ? true
290                : ((*aFirst > *aSecond) || (*aFirst == '\0') ? false : AreStringsInOrder(aFirst + 1, aSecond + 1));
291 }
292 
293 /**
294  * This class implements writing to a string buffer.
295  *
296  */
297 class StringWriter
298 {
299 public:
300     /**
301      * This constructor initializes the object as cleared on the provided buffer.
302      *
303      * @param[in] aBuffer  A pointer to the char buffer to write into.
304      * @param[in] aSize    The size of @p aBuffer.
305      *
306      */
307     StringWriter(char *aBuffer, uint16_t aSize);
308 
309     /**
310      * This method clears the string writer.
311      *
312      * @returns The string writer.
313      *
314      */
315     StringWriter &Clear(void);
316 
317     /**
318      * This method returns whether the output is truncated.
319      *
320      * @note If the output is truncated, the buffer is still null-terminated.
321      *
322      * @retval  true    The output is truncated.
323      * @retval  false   The output is not truncated.
324      *
325      */
IsTruncated(void) const326     bool IsTruncated(void) const { return mLength >= mSize; }
327 
328     /**
329      * This method gets the length of the wanted string.
330      *
331      * Similar to `strlen()` the length does not include the null character at the end of the string.
332      *
333      * @returns The string length.
334      *
335      */
GetLength(void) const336     uint16_t GetLength(void) const { return mLength; }
337 
338     /**
339      * This method returns the size (number of chars) in the buffer.
340      *
341      * @returns The size of the buffer.
342      *
343      */
GetSize(void) const344     uint16_t GetSize(void) const { return mSize; }
345 
346     /**
347      * This method appends `printf()` style formatted data to the buffer.
348      *
349      * @param[in] aFormat    A pointer to the format string.
350      * @param[in] ...        Arguments for the format specification.
351      *
352      * @returns The string writer.
353      *
354      */
355     StringWriter &Append(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3);
356 
357     /**
358      * This method appends `printf()` style formatted data to the buffer.
359      *
360      * @param[in] aFormat    A pointer to the format string.
361      * @param[in] aArgs      Arguments for the format specification (as `va_list`).
362      *
363      * @returns The string writer.
364      *
365      */
366     StringWriter &AppendVarArgs(const char *aFormat, va_list aArgs);
367 
368     /**
369      * This method appends an array of bytes in hex representation (using "%02x" style) to the buffer.
370      *
371      * @param[in] aBytes    A pointer to buffer containing the bytes to append.
372      * @param[in] aLength   The length of @p aBytes buffer (in bytes).
373      *
374      * @returns The string writer.
375      *
376      */
377     StringWriter &AppendHexBytes(const uint8_t *aBytes, uint16_t aLength);
378 
379     /**
380      * This method converts all uppercase letter characters in the string to lowercase.
381      *
382      */
ConvertToLowercase(void)383     void ConvertToLowercase(void) { StringConvertToLowercase(mBuffer); }
384 
385     /**
386      * This method converts all lowercase letter characters in the string to uppercase.
387      *
388      */
ConvertToUppercase(void)389     void ConvertToUppercase(void) { StringConvertToUppercase(mBuffer); }
390 
391 private:
392     char          *mBuffer;
393     uint16_t       mLength;
394     const uint16_t mSize;
395 };
396 
397 /**
398  * This class defines a fixed-size string.
399  *
400  */
401 template <uint16_t kSize> class String : public StringWriter
402 {
403     static_assert(kSize > 0, "String buffer cannot be empty.");
404 
405 public:
406     /**
407      * This constructor initializes the string as empty.
408      *
409      */
String(void)410     String(void)
411         : StringWriter(mBuffer, sizeof(mBuffer))
412     {
413     }
414 
415     /**
416      * This method returns the string as a null-terminated C string.
417      *
418      * @returns The null-terminated C string.
419      *
420      */
AsCString(void) const421     const char *AsCString(void) const { return mBuffer; }
422 
423 private:
424     char mBuffer[kSize];
425 };
426 
427 /**
428  * This class provides helper methods to convert from a set of `uint16_t` values (e.g., a non-sequential `enum`) to
429  * string using binary search in a lookup table.
430  *
431  */
432 class Stringify : public BinarySearch
433 {
434 public:
435     /**
436      * This class represents a entry in the lookup table.
437      *
438      */
439     class Entry
440     {
441         friend class BinarySearch;
442 
443     public:
444         uint16_t    mKey;    ///< The key value.
445         const char *mString; ///< The associated string.
446 
447     private:
Compare(uint16_t aKey) const448         int Compare(uint16_t aKey) const { return ThreeWayCompare(aKey, mKey); }
449 
AreInOrder(const Entry & aFirst,const Entry & aSecond)450         constexpr static bool AreInOrder(const Entry &aFirst, const Entry &aSecond)
451         {
452             return aFirst.mKey < aSecond.mKey;
453         }
454     };
455 
456     /**
457      * This static method looks up a key in a given sorted table array (using binary search) and return the associated
458      * strings with the key.
459      *
460      * @note This method requires the array to be sorted, otherwise its behavior is undefined.
461      *
462      * @tparam kLength     The array length (number of entries in the array).
463      *
464      * @param[in] aKey       The key to search for within the table.
465      * @param[in] aTable     A reference to an array of `kLength` entries.
466      * @param[in] aNotFound  A C string to return if @p aKey was not found in the table.
467      *
468      * @returns The associated string with @p aKey in @p aTable if found, or @p aNotFound otherwise.
469      *
470      */
471     template <uint16_t kLength>
Lookup(uint16_t aKey,const Entry (& aTable)[kLength],const char * aNotFound="unknown")472     static const char *Lookup(uint16_t aKey, const Entry (&aTable)[kLength], const char *aNotFound = "unknown")
473     {
474         const Entry *entry = BinarySearch::Find(aKey, aTable);
475 
476         return (entry != nullptr) ? entry->mString : aNotFound;
477     }
478 
479     Stringify(void) = delete;
480 };
481 
482 /**
483  * @}
484  *
485  */
486 
487 } // namespace ot
488 
489 #endif // STRING_HPP_
490