| /* |
| * Copyright (c) 2018, The OpenThread Authors. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the copyright holder nor the |
| * names of its contributors may be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /** |
| * @file |
| * This file implements OpenThread String class and functions. |
| */ |
| |
| #include "string.hpp" |
| #include "debug.hpp" |
| |
| #include <string.h> |
| |
| namespace ot { |
| |
| namespace { |
| |
| // The definitions below are included in an unnamed namespace |
| // to limit their scope to this translation unit (this file). |
| |
| enum MatchType : uint8_t |
| { |
| kNoMatch, |
| kPrefixMatch, |
| kFullMatch, |
| }; |
| |
| MatchType Match(const char *aString, const char *aPrefixString, StringMatchMode aMode) |
| { |
| // This is file private function that is used by other functions. |
| // It matches @p aString with @p aPrefixString using match @ aMode. |
| // |
| // If @p aString and @p aPrefixString match and have the |
| // same length `kFullMatch` is returned. If @p aString starts |
| // with @p aPrefixString but contains more characters, then |
| // `kPrefixMatch` is returned. Otherwise `kNoMatch` is returned. |
| |
| MatchType match = kNoMatch; |
| |
| switch (aMode) |
| { |
| case kStringExactMatch: |
| while (*aPrefixString != kNullChar) |
| { |
| VerifyOrExit(*aString++ == *aPrefixString++); |
| } |
| break; |
| |
| case kStringCaseInsensitiveMatch: |
| while (*aPrefixString != kNullChar) |
| { |
| VerifyOrExit(ToLowercase(*aString++) == ToLowercase(*aPrefixString++)); |
| } |
| break; |
| } |
| |
| match = (*aString == kNullChar) ? kFullMatch : kPrefixMatch; |
| |
| exit: |
| return match; |
| } |
| |
| } // namespace |
| |
| uint16_t StringLength(const char *aString, uint16_t aMaxLength) |
| { |
| uint16_t ret; |
| |
| for (ret = 0; (ret < aMaxLength) && (aString[ret] != kNullChar); ret++) |
| { |
| // Empty loop. |
| } |
| |
| return ret; |
| } |
| |
| const char *StringFind(const char *aString, char aChar) |
| { |
| const char *ret = nullptr; |
| |
| for (; *aString != kNullChar; aString++) |
| { |
| if (*aString == aChar) |
| { |
| ret = aString; |
| break; |
| } |
| } |
| |
| return ret; |
| } |
| |
| const char *StringFind(const char *aString, const char *aSubString, StringMatchMode aMode) |
| { |
| const char *ret = nullptr; |
| size_t len = strlen(aString); |
| size_t subLen = strlen(aSubString); |
| |
| VerifyOrExit(subLen <= len); |
| |
| for (size_t index = 0; index <= static_cast<size_t>(len - subLen); index++) |
| { |
| if (Match(&aString[index], aSubString, aMode) != kNoMatch) |
| { |
| ExitNow(ret = &aString[index]); |
| } |
| } |
| |
| exit: |
| return ret; |
| } |
| |
| bool StringStartsWith(const char *aString, const char *aPrefixString, StringMatchMode aMode) |
| { |
| return Match(aString, aPrefixString, aMode) != kNoMatch; |
| } |
| |
| bool StringEndsWith(const char *aString, char aChar) |
| { |
| size_t len = strlen(aString); |
| |
| return (len > 0) && (aString[len - 1] == aChar); |
| } |
| |
| bool StringEndsWith(const char *aString, const char *aSubString, StringMatchMode aMode) |
| { |
| size_t len = strlen(aString); |
| size_t subLen = strlen(aSubString); |
| |
| return (subLen > 0) && (len >= subLen) && (Match(&aString[len - subLen], aSubString, aMode) != kNoMatch); |
| } |
| |
| bool StringMatch(const char *aFirstString, const char *aSecondString, StringMatchMode aMode) |
| { |
| return Match(aFirstString, aSecondString, aMode) == kFullMatch; |
| } |
| |
| void StringConvertToLowercase(char *aString) |
| { |
| for (; *aString != kNullChar; aString++) |
| { |
| *aString = ToLowercase(*aString); |
| } |
| } |
| |
| void StringConvertToUppercase(char *aString) |
| { |
| for (; *aString != kNullChar; aString++) |
| { |
| *aString = ToUppercase(*aString); |
| } |
| } |
| |
| char ToLowercase(char aChar) |
| { |
| if ((aChar >= 'A') && (aChar <= 'Z')) |
| { |
| aChar += 'a' - 'A'; |
| } |
| |
| return aChar; |
| } |
| |
| char ToUppercase(char aChar) |
| { |
| if ((aChar >= 'a') && (aChar <= 'z')) |
| { |
| aChar -= 'a' - 'A'; |
| } |
| |
| return aChar; |
| } |
| |
| const char *ToYesNo(bool aBool) |
| { |
| static const char *const kYesNoStrings[] = {"no", "yes"}; |
| |
| return kYesNoStrings[aBool]; |
| } |
| |
| StringWriter::StringWriter(char *aBuffer, uint16_t aSize) |
| : mBuffer(aBuffer) |
| , mLength(0) |
| , mSize(aSize) |
| { |
| mBuffer[0] = kNullChar; |
| } |
| |
| StringWriter &StringWriter::Clear(void) |
| { |
| mBuffer[0] = kNullChar; |
| mLength = 0; |
| return *this; |
| } |
| |
| StringWriter &StringWriter::Append(const char *aFormat, ...) |
| { |
| va_list args; |
| va_start(args, aFormat); |
| AppendVarArgs(aFormat, args); |
| va_end(args); |
| |
| return *this; |
| } |
| |
| StringWriter &StringWriter::AppendVarArgs(const char *aFormat, va_list aArgs) |
| { |
| int len; |
| |
| len = vsnprintf(mBuffer + mLength, (mSize > mLength ? (mSize - mLength) : 0), aFormat, aArgs); |
| OT_ASSERT(len >= 0); |
| |
| mLength += static_cast<uint16_t>(len); |
| |
| if (IsTruncated()) |
| { |
| mBuffer[mSize - 1] = kNullChar; |
| } |
| |
| return *this; |
| } |
| |
| StringWriter &StringWriter::AppendHexBytes(const uint8_t *aBytes, uint16_t aLength) |
| { |
| while (aLength--) |
| { |
| Append("%02x", *aBytes++); |
| } |
| |
| return *this; |
| } |
| |
| bool IsValidUtf8String(const char *aString) |
| { |
| return IsValidUtf8String(aString, strlen(aString)); |
| } |
| |
| bool IsValidUtf8String(const char *aString, size_t aLength) |
| { |
| bool ret = true; |
| uint8_t byte; |
| uint8_t continuationBytes = 0; |
| size_t position = 0; |
| |
| while (position < aLength) |
| { |
| byte = *reinterpret_cast<const uint8_t *>(aString + position); |
| ++position; |
| |
| if ((byte & 0x80) == 0) |
| { |
| // We don't allow control characters. |
| VerifyOrExit(!iscntrl(byte), ret = false); |
| continue; |
| } |
| |
| // This is a leading byte 1xxx-xxxx. |
| |
| if ((byte & 0x40) == 0) // 10xx-xxxx |
| { |
| // We got a continuation byte pattern without seeing a leading byte earlier. |
| ExitNow(ret = false); |
| } |
| else if ((byte & 0x20) == 0) // 110x-xxxx |
| { |
| continuationBytes = 1; |
| } |
| else if ((byte & 0x10) == 0) // 1110-xxxx |
| { |
| continuationBytes = 2; |
| } |
| else if ((byte & 0x08) == 0) // 1111-0xxx |
| { |
| continuationBytes = 3; |
| } |
| else // 1111-1xxx (invalid pattern). |
| { |
| ExitNow(ret = false); |
| } |
| |
| while (continuationBytes-- != 0) |
| { |
| VerifyOrExit(position < aLength, ret = false); |
| |
| byte = *reinterpret_cast<const uint8_t *>(aString + position); |
| ++position; |
| |
| // Verify the continuation byte pattern 10xx-xxxx |
| VerifyOrExit((byte & 0xc0) == 0x80, ret = false); |
| } |
| } |
| |
| exit: |
| return ret; |
| } |
| |
| } // namespace ot |