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 implements OpenThread String class and functions.
32 */
33
34 #include "string.hpp"
35 #include "debug.hpp"
36
37 #include <string.h>
38
39 namespace ot {
40
41 namespace {
42
43 // The definitions below are included in an unnamed namespace
44 // to limit their scope to this translation unit (this file).
45
46 enum MatchType : uint8_t
47 {
48 kNoMatch,
49 kPrefixMatch,
50 kFullMatch,
51 };
52
Match(const char * aString,const char * aPrefixString,StringMatchMode aMode)53 MatchType Match(const char *aString, const char *aPrefixString, StringMatchMode aMode)
54 {
55 // This is file private function that is used by other functions.
56 // It matches @p aString with @p aPrefixString using match @ aMode.
57 //
58 // If @p aString and @p aPrefixString match and have the
59 // same length `kFullMatch` is returned. If @p aString starts
60 // with @p aPrefixString but contains more characters, then
61 // `kPrefixMatch` is returned. Otherwise `kNoMatch` is returned.
62
63 MatchType match = kNoMatch;
64
65 switch (aMode)
66 {
67 case kStringExactMatch:
68 while (*aPrefixString != kNullChar)
69 {
70 VerifyOrExit(*aString++ == *aPrefixString++);
71 }
72 break;
73
74 case kStringCaseInsensitiveMatch:
75 while (*aPrefixString != kNullChar)
76 {
77 VerifyOrExit(ToLowercase(*aString++) == ToLowercase(*aPrefixString++));
78 }
79 break;
80 }
81
82 match = (*aString == kNullChar) ? kFullMatch : kPrefixMatch;
83
84 exit:
85 return match;
86 }
87
88 } // namespace
89
StringLength(const char * aString,uint16_t aMaxLength)90 uint16_t StringLength(const char *aString, uint16_t aMaxLength)
91 {
92 uint16_t ret = 0;
93
94 VerifyOrExit(aString != nullptr);
95
96 for (; (ret < aMaxLength) && (aString[ret] != kNullChar); ret++)
97 {
98 // Empty loop.
99 }
100
101 exit:
102 return ret;
103 }
104
StringFind(const char * aString,char aChar)105 const char *StringFind(const char *aString, char aChar)
106 {
107 const char *ret = nullptr;
108
109 for (; *aString != kNullChar; aString++)
110 {
111 if (*aString == aChar)
112 {
113 ret = aString;
114 break;
115 }
116 }
117
118 return ret;
119 }
120
StringFind(const char * aString,const char * aSubString,StringMatchMode aMode)121 const char *StringFind(const char *aString, const char *aSubString, StringMatchMode aMode)
122 {
123 const char *ret = nullptr;
124 size_t len = strlen(aString);
125 size_t subLen = strlen(aSubString);
126
127 VerifyOrExit(subLen <= len);
128
129 for (size_t index = 0; index <= static_cast<size_t>(len - subLen); index++)
130 {
131 if (Match(&aString[index], aSubString, aMode) != kNoMatch)
132 {
133 ExitNow(ret = &aString[index]);
134 }
135 }
136
137 exit:
138 return ret;
139 }
140
StringStartsWith(const char * aString,const char * aPrefixString,StringMatchMode aMode)141 bool StringStartsWith(const char *aString, const char *aPrefixString, StringMatchMode aMode)
142 {
143 return Match(aString, aPrefixString, aMode) != kNoMatch;
144 }
145
StringEndsWith(const char * aString,char aChar)146 bool StringEndsWith(const char *aString, char aChar)
147 {
148 size_t len = strlen(aString);
149
150 return (len > 0) && (aString[len - 1] == aChar);
151 }
152
StringEndsWith(const char * aString,const char * aSubString,StringMatchMode aMode)153 bool StringEndsWith(const char *aString, const char *aSubString, StringMatchMode aMode)
154 {
155 size_t len = strlen(aString);
156 size_t subLen = strlen(aSubString);
157
158 return (subLen > 0) && (len >= subLen) && (Match(&aString[len - subLen], aSubString, aMode) != kNoMatch);
159 }
160
StringMatch(const char * aFirstString,const char * aSecondString)161 bool StringMatch(const char *aFirstString, const char *aSecondString)
162 {
163 return Match(aFirstString, aSecondString, kStringExactMatch) == kFullMatch;
164 }
165
StringMatch(const char * aFirstString,const char * aSecondString,StringMatchMode aMode)166 bool StringMatch(const char *aFirstString, const char *aSecondString, StringMatchMode aMode)
167 {
168 return Match(aFirstString, aSecondString, aMode) == kFullMatch;
169 }
170
StringCopy(char * aTargetBuffer,uint16_t aTargetSize,const char * aSource,StringEncodingCheck aEncodingCheck)171 Error StringCopy(char *aTargetBuffer, uint16_t aTargetSize, const char *aSource, StringEncodingCheck aEncodingCheck)
172 {
173 Error error = kErrorNone;
174 uint16_t length;
175
176 if (aSource == nullptr)
177 {
178 aTargetBuffer[0] = kNullChar;
179 ExitNow();
180 }
181
182 length = StringLength(aSource, aTargetSize);
183 VerifyOrExit(length < aTargetSize, error = kErrorInvalidArgs);
184
185 switch (aEncodingCheck)
186 {
187 case kStringNoEncodingCheck:
188 break;
189 case kStringCheckUtf8Encoding:
190 VerifyOrExit(IsValidUtf8String(aSource), error = kErrorParse);
191 break;
192 }
193
194 memcpy(aTargetBuffer, aSource, length + 1); // `+ 1` to also copy null char.
195
196 exit:
197 return error;
198 }
199
StringParseUint8(const char * & aString,uint8_t & aUint8)200 Error StringParseUint8(const char *&aString, uint8_t &aUint8)
201 {
202 return StringParseUint8(aString, aUint8, NumericLimits<uint8_t>::kMax);
203 }
204
StringParseUint8(const char * & aString,uint8_t & aUint8,uint8_t aMaxValue)205 Error StringParseUint8(const char *&aString, uint8_t &aUint8, uint8_t aMaxValue)
206 {
207 Error error = kErrorParse;
208 const char *cur = aString;
209 uint16_t value = 0;
210 uint8_t digit;
211
212 while (ParseDigit(*cur, digit) == kErrorNone)
213 {
214 value *= 10;
215 value += digit;
216 VerifyOrExit(value <= aMaxValue, error = kErrorParse);
217 error = kErrorNone;
218 cur++;
219 }
220
221 aString = cur;
222 aUint8 = static_cast<uint8_t>(value);
223
224 exit:
225 return error;
226 }
227
StringConvertToLowercase(char * aString)228 void StringConvertToLowercase(char *aString)
229 {
230 for (; *aString != kNullChar; aString++)
231 {
232 *aString = ToLowercase(*aString);
233 }
234 }
235
StringConvertToUppercase(char * aString)236 void StringConvertToUppercase(char *aString)
237 {
238 for (; *aString != kNullChar; aString++)
239 {
240 *aString = ToUppercase(*aString);
241 }
242 }
243
ToLowercase(char aChar)244 char ToLowercase(char aChar)
245 {
246 if (IsUppercase(aChar))
247 {
248 aChar += 'a' - 'A';
249 }
250
251 return aChar;
252 }
253
ToUppercase(char aChar)254 char ToUppercase(char aChar)
255 {
256 if (IsLowercase(aChar))
257 {
258 aChar -= 'a' - 'A';
259 }
260
261 return aChar;
262 }
263
IsDigit(char aChar)264 bool IsDigit(char aChar) { return ('0' <= aChar && aChar <= '9'); }
265
IsUppercase(char aChar)266 bool IsUppercase(char aChar) { return ('A' <= aChar && aChar <= 'Z'); }
267
IsLowercase(char aChar)268 bool IsLowercase(char aChar) { return ('a' <= aChar && aChar <= 'z'); }
269
ParseDigit(char aDigitChar,uint8_t & aValue)270 Error ParseDigit(char aDigitChar, uint8_t &aValue)
271 {
272 Error error = kErrorNone;
273
274 VerifyOrExit(IsDigit(aDigitChar), error = kErrorInvalidArgs);
275 aValue = static_cast<uint8_t>(aDigitChar - '0');
276
277 exit:
278 return error;
279 }
280
ParseHexDigit(char aHexChar,uint8_t & aValue)281 Error ParseHexDigit(char aHexChar, uint8_t &aValue)
282 {
283 Error error = kErrorNone;
284
285 if (('A' <= aHexChar) && (aHexChar <= 'F'))
286 {
287 ExitNow(aValue = static_cast<uint8_t>(aHexChar - 'A' + 10));
288 }
289
290 if (('a' <= aHexChar) && (aHexChar <= 'f'))
291 {
292 ExitNow(aValue = static_cast<uint8_t>(aHexChar - 'a' + 10));
293 }
294
295 error = ParseDigit(aHexChar, aValue);
296
297 exit:
298 return error;
299 }
300
ToYesNo(bool aBool)301 const char *ToYesNo(bool aBool)
302 {
303 static const char *const kYesNoStrings[] = {"no", "yes"};
304
305 return kYesNoStrings[aBool];
306 }
307
StringWriter(char * aBuffer,uint16_t aSize)308 StringWriter::StringWriter(char *aBuffer, uint16_t aSize)
309 : mBuffer(aBuffer)
310 , mLength(0)
311 , mSize(aSize)
312 {
313 mBuffer[0] = kNullChar;
314 }
315
Clear(void)316 StringWriter &StringWriter::Clear(void)
317 {
318 mBuffer[0] = kNullChar;
319 mLength = 0;
320 return *this;
321 }
322
Append(const char * aFormat,...)323 StringWriter &StringWriter::Append(const char *aFormat, ...)
324 {
325 va_list args;
326 va_start(args, aFormat);
327 AppendVarArgs(aFormat, args);
328 va_end(args);
329
330 return *this;
331 }
332
AppendVarArgs(const char * aFormat,va_list aArgs)333 StringWriter &StringWriter::AppendVarArgs(const char *aFormat, va_list aArgs)
334 {
335 int len;
336
337 len = vsnprintf(mBuffer + mLength, (mSize > mLength ? (mSize - mLength) : 0), aFormat, aArgs);
338 OT_ASSERT(len >= 0);
339
340 mLength += static_cast<uint16_t>(len);
341
342 if (IsTruncated())
343 {
344 mBuffer[mSize - 1] = kNullChar;
345 }
346
347 return *this;
348 }
349
AppendHexBytes(const uint8_t * aBytes,uint16_t aLength)350 StringWriter &StringWriter::AppendHexBytes(const uint8_t *aBytes, uint16_t aLength)
351 {
352 while (aLength--)
353 {
354 Append("%02x", *aBytes++);
355 }
356
357 return *this;
358 }
359
AppendCharMultipleTimes(char aChar,uint16_t aCount)360 StringWriter &StringWriter::AppendCharMultipleTimes(char aChar, uint16_t aCount)
361 {
362 while (aCount--)
363 {
364 Append("%c", aChar);
365 }
366
367 return *this;
368 }
369
IsValidUtf8String(const char * aString)370 bool IsValidUtf8String(const char *aString) { return IsValidUtf8String(aString, strlen(aString)); }
371
IsValidUtf8String(const char * aString,size_t aLength)372 bool IsValidUtf8String(const char *aString, size_t aLength)
373 {
374 bool ret = true;
375 uint8_t byte;
376 uint8_t continuationBytes = 0;
377 size_t position = 0;
378
379 while (position < aLength)
380 {
381 byte = *reinterpret_cast<const uint8_t *>(aString + position);
382 ++position;
383
384 if ((byte & 0x80) == 0)
385 {
386 // We don't allow control characters.
387 VerifyOrExit(!iscntrl(byte), ret = false);
388 continue;
389 }
390
391 // This is a leading byte 1xxx-xxxx.
392
393 if ((byte & 0x40) == 0) // 10xx-xxxx
394 {
395 // We got a continuation byte pattern without seeing a leading byte earlier.
396 ExitNow(ret = false);
397 }
398 else if ((byte & 0x20) == 0) // 110x-xxxx
399 {
400 continuationBytes = 1;
401 }
402 else if ((byte & 0x10) == 0) // 1110-xxxx
403 {
404 continuationBytes = 2;
405 }
406 else if ((byte & 0x08) == 0) // 1111-0xxx
407 {
408 continuationBytes = 3;
409 }
410 else // 1111-1xxx (invalid pattern).
411 {
412 ExitNow(ret = false);
413 }
414
415 while (continuationBytes-- != 0)
416 {
417 VerifyOrExit(position < aLength, ret = false);
418
419 byte = *reinterpret_cast<const uint8_t *>(aString + position);
420 ++position;
421
422 // Verify the continuation byte pattern 10xx-xxxx
423 VerifyOrExit((byte & 0xc0) == 0x80, ret = false);
424 }
425 }
426
427 exit:
428 return ret;
429 }
430
431 } // namespace ot
432