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,StringMatchMode aMode)161 bool StringMatch(const char *aFirstString, const char *aSecondString, StringMatchMode aMode)
162 {
163 return Match(aFirstString, aSecondString, aMode) == kFullMatch;
164 }
165
StringCopy(char * aTargetBuffer,uint16_t aTargetSize,const char * aSource,StringEncodingCheck aEncodingCheck)166 Error StringCopy(char *aTargetBuffer, uint16_t aTargetSize, const char *aSource, StringEncodingCheck aEncodingCheck)
167 {
168 Error error = kErrorNone;
169 uint16_t length;
170
171 if (aSource == nullptr)
172 {
173 aTargetBuffer[0] = kNullChar;
174 ExitNow();
175 }
176
177 length = StringLength(aSource, aTargetSize);
178 VerifyOrExit(length < aTargetSize, error = kErrorInvalidArgs);
179
180 switch (aEncodingCheck)
181 {
182 case kStringNoEncodingCheck:
183 break;
184 case kStringCheckUtf8Encoding:
185 VerifyOrExit(IsValidUtf8String(aSource), error = kErrorParse);
186 break;
187 }
188
189 memcpy(aTargetBuffer, aSource, length + 1); // `+ 1` to also copy null char.
190
191 exit:
192 return error;
193 }
194
StringParseUint8(const char * & aString,uint8_t & aUint8)195 Error StringParseUint8(const char *&aString, uint8_t &aUint8)
196 {
197 return StringParseUint8(aString, aUint8, NumericLimits<uint8_t>::kMax);
198 }
199
StringParseUint8(const char * & aString,uint8_t & aUint8,uint8_t aMaxValue)200 Error StringParseUint8(const char *&aString, uint8_t &aUint8, uint8_t aMaxValue)
201 {
202 Error error = kErrorParse;
203 const char *cur = aString;
204 uint16_t value = 0;
205
206 for (; (*cur >= '0') && (*cur <= '9'); cur++)
207 {
208 value *= 10;
209 value += static_cast<uint8_t>(*cur - '0');
210 VerifyOrExit(value <= aMaxValue, error = kErrorParse);
211 error = kErrorNone;
212 }
213
214 aString = cur;
215 aUint8 = static_cast<uint8_t>(value);
216
217 exit:
218 return error;
219 }
220
StringConvertToLowercase(char * aString)221 void StringConvertToLowercase(char *aString)
222 {
223 for (; *aString != kNullChar; aString++)
224 {
225 *aString = ToLowercase(*aString);
226 }
227 }
228
StringConvertToUppercase(char * aString)229 void StringConvertToUppercase(char *aString)
230 {
231 for (; *aString != kNullChar; aString++)
232 {
233 *aString = ToUppercase(*aString);
234 }
235 }
236
ToLowercase(char aChar)237 char ToLowercase(char aChar)
238 {
239 if ((aChar >= 'A') && (aChar <= 'Z'))
240 {
241 aChar += 'a' - 'A';
242 }
243
244 return aChar;
245 }
246
ToUppercase(char aChar)247 char ToUppercase(char aChar)
248 {
249 if ((aChar >= 'a') && (aChar <= 'z'))
250 {
251 aChar -= 'a' - 'A';
252 }
253
254 return aChar;
255 }
256
ToYesNo(bool aBool)257 const char *ToYesNo(bool aBool)
258 {
259 static const char *const kYesNoStrings[] = {"no", "yes"};
260
261 return kYesNoStrings[aBool];
262 }
263
StringWriter(char * aBuffer,uint16_t aSize)264 StringWriter::StringWriter(char *aBuffer, uint16_t aSize)
265 : mBuffer(aBuffer)
266 , mLength(0)
267 , mSize(aSize)
268 {
269 mBuffer[0] = kNullChar;
270 }
271
Clear(void)272 StringWriter &StringWriter::Clear(void)
273 {
274 mBuffer[0] = kNullChar;
275 mLength = 0;
276 return *this;
277 }
278
Append(const char * aFormat,...)279 StringWriter &StringWriter::Append(const char *aFormat, ...)
280 {
281 va_list args;
282 va_start(args, aFormat);
283 AppendVarArgs(aFormat, args);
284 va_end(args);
285
286 return *this;
287 }
288
AppendVarArgs(const char * aFormat,va_list aArgs)289 StringWriter &StringWriter::AppendVarArgs(const char *aFormat, va_list aArgs)
290 {
291 int len;
292
293 len = vsnprintf(mBuffer + mLength, (mSize > mLength ? (mSize - mLength) : 0), aFormat, aArgs);
294 OT_ASSERT(len >= 0);
295
296 mLength += static_cast<uint16_t>(len);
297
298 if (IsTruncated())
299 {
300 mBuffer[mSize - 1] = kNullChar;
301 }
302
303 return *this;
304 }
305
AppendHexBytes(const uint8_t * aBytes,uint16_t aLength)306 StringWriter &StringWriter::AppendHexBytes(const uint8_t *aBytes, uint16_t aLength)
307 {
308 while (aLength--)
309 {
310 Append("%02x", *aBytes++);
311 }
312
313 return *this;
314 }
315
AppendCharMultipleTimes(char aChar,uint16_t aCount)316 StringWriter &StringWriter::AppendCharMultipleTimes(char aChar, uint16_t aCount)
317 {
318 while (aCount--)
319 {
320 Append("%c", aChar);
321 }
322
323 return *this;
324 }
325
IsValidUtf8String(const char * aString)326 bool IsValidUtf8String(const char *aString) { return IsValidUtf8String(aString, strlen(aString)); }
327
IsValidUtf8String(const char * aString,size_t aLength)328 bool IsValidUtf8String(const char *aString, size_t aLength)
329 {
330 bool ret = true;
331 uint8_t byte;
332 uint8_t continuationBytes = 0;
333 size_t position = 0;
334
335 while (position < aLength)
336 {
337 byte = *reinterpret_cast<const uint8_t *>(aString + position);
338 ++position;
339
340 if ((byte & 0x80) == 0)
341 {
342 // We don't allow control characters.
343 VerifyOrExit(!iscntrl(byte), ret = false);
344 continue;
345 }
346
347 // This is a leading byte 1xxx-xxxx.
348
349 if ((byte & 0x40) == 0) // 10xx-xxxx
350 {
351 // We got a continuation byte pattern without seeing a leading byte earlier.
352 ExitNow(ret = false);
353 }
354 else if ((byte & 0x20) == 0) // 110x-xxxx
355 {
356 continuationBytes = 1;
357 }
358 else if ((byte & 0x10) == 0) // 1110-xxxx
359 {
360 continuationBytes = 2;
361 }
362 else if ((byte & 0x08) == 0) // 1111-0xxx
363 {
364 continuationBytes = 3;
365 }
366 else // 1111-1xxx (invalid pattern).
367 {
368 ExitNow(ret = false);
369 }
370
371 while (continuationBytes-- != 0)
372 {
373 VerifyOrExit(position < aLength, ret = false);
374
375 byte = *reinterpret_cast<const uint8_t *>(aString + position);
376 ++position;
377
378 // Verify the continuation byte pattern 10xx-xxxx
379 VerifyOrExit((byte & 0xc0) == 0x80, ret = false);
380 }
381 }
382
383 exit:
384 return ret;
385 }
386
387 } // namespace ot
388