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