• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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