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