blob: 4940076fc53161cc52c570748bbf2d772db94f4f [file] [log] [blame]
/*
* 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