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