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