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