Skip to content

Commit

Permalink
[string] add ParseDigit(), ParseHexDigit(), and other helper func…
Browse files Browse the repository at this point in the history
…tions (#10282)

This commit adds a set of helper functions to parse a given digit or
hex digit character to its numeric value, or to check if a given
character is a digit, uppercase, or lowercase letter.
  • Loading branch information
abtink committed May 22, 2024
1 parent 31ac626 commit a57d927
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 50 deletions.
47 changes: 43 additions & 4 deletions src/core/common/string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,13 +202,15 @@ Error StringParseUint8(const char *&aString, uint8_t &aUint8, uint8_t aMaxValue)
Error error = kErrorParse;
const char *cur = aString;
uint16_t value = 0;
uint8_t digit;

for (; (*cur >= '0') && (*cur <= '9'); cur++)
while (ParseDigit(*cur, digit) == kErrorNone)
{
value *= 10;
value += static_cast<uint8_t>(*cur - '0');
value += digit;
VerifyOrExit(value <= aMaxValue, error = kErrorParse);
error = kErrorNone;
cur++;
}

aString = cur;
Expand Down Expand Up @@ -236,7 +238,7 @@ void StringConvertToUppercase(char *aString)

char ToLowercase(char aChar)
{
if ((aChar >= 'A') && (aChar <= 'Z'))
if (IsUppercase(aChar))
{
aChar += 'a' - 'A';
}
Expand All @@ -246,14 +248,51 @@ char ToLowercase(char aChar)

char ToUppercase(char aChar)
{
if ((aChar >= 'a') && (aChar <= 'z'))
if (IsLowercase(aChar))
{
aChar -= 'a' - 'A';
}

return aChar;
}

bool IsDigit(char aChar) { return ('0' <= aChar && aChar <= '9'); }

bool IsUppercase(char aChar) { return ('A' <= aChar && aChar <= 'Z'); }

bool IsLowercase(char aChar) { return ('a' <= aChar && aChar <= 'z'); }

Error ParseDigit(char aDigitChar, uint8_t &aValue)
{
Error error = kErrorNone;

VerifyOrExit(IsDigit(aDigitChar), error = kErrorInvalidArgs);
aValue = static_cast<uint8_t>(aDigitChar - '0');

exit:
return error;
}

Error ParseHexDigit(char aHexChar, uint8_t &aValue)
{
Error error = kErrorNone;

if (('A' <= aHexChar) && (aHexChar <= 'F'))
{
ExitNow(aValue = static_cast<uint8_t>(aHexChar - 'A' + 10));
}

if (('a' <= aHexChar) && (aHexChar <= 'f'))
{
ExitNow(aValue = static_cast<uint8_t>(aHexChar - 'a' + 10));
}

error = ParseDigit(aHexChar, aValue);

exit:
return error;
}

const char *ToYesNo(bool aBool)
{
static const char *const kYesNoStrings[] = {"no", "yes"};
Expand Down
57 changes: 57 additions & 0 deletions src/core/common/string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,63 @@ char ToLowercase(char aChar);
*/
char ToUppercase(char aChar);

/**
* Checks whether a given character is an uppercase letter ('A'-'Z').
*
* @param[in] aChar The character to check.
*
* @retval TRUE @p aChar is an uppercase letter.
* @retval FALSE @p aChar is not an uppercase letter.
*
*/
bool IsUppercase(char aChar);

/**
* Checks whether a given character is a lowercase letter ('a'-'z').
*
* @param[in] aChar The character to check.
*
* @retval TRUE @p aChar is a lowercase letter.
* @retval FALSE @p aChar is not a lowercase letter.
*
*/
bool IsLowercase(char aChar);

/**
* Checks whether a given character is a digit character ('0'-'9').
*
* @param[in] aChar The character to check.
*
* @retval TRUE @p aChar is a digit character.
* @retval FALSE @p aChar is not a digit character.
*
*/
bool IsDigit(char aChar);

/**
* Parse a given digit character to its numeric value.
*
* @param[in] aDigitChar The digit character to parse.
* @param[out] aValue A reference to return the parsed value on success.
*
* @retval kErrorNone Successfully parsed the digit, @p aValue is updated.
* @retval kErrorInvalidArgs @p aDigitChar is not a valid digit character.
*
*/
Error ParseDigit(char aDigitChar, uint8_t &aValue);

/**
* Parse a given hex digit character ('0'-'9', 'A'-'F', or 'a'-'f') to its numeric value.
*
* @param[in] aHexChar The hex digit character to parse.
* @param[out] aValue A reference to return the parsed value on success.
*
* @retval kErrorNone Successfully parsed the digit, @p aValue is updated.
* @retval kErrorInvalidArgs @p aHexChar is not a valid hex digit character.
*
*/
Error ParseHexDigit(char aHexChar, uint8_t &aValue);

/**
* Coverts a boolean to "yes" or "no" string.
*
Expand Down
2 changes: 1 addition & 1 deletion src/core/meshcop/meshcop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ bool JoinerPskd::IsPskdValid(const char *aPskdString)
{
char c = aPskdString[i];

VerifyOrExit(isdigit(c) || isupper(c));
VerifyOrExit(IsDigit(c) || IsUppercase(c));
VerifyOrExit(c != 'I' && c != 'O' && c != 'Q' && c != 'Z');
}

Expand Down
15 changes: 1 addition & 14 deletions src/core/net/ip6_address.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -586,22 +586,9 @@ Error Address::ParseFrom(const char *aString, char aTerminatorChar)

while (true)
{
char c = *aString;
uint8_t digit;

if (('A' <= c) && (c <= 'F'))
{
digit = static_cast<uint8_t>(c - 'A' + 10);
}
else if (('a' <= c) && (c <= 'f'))
{
digit = static_cast<uint8_t>(c - 'a' + 10);
}
else if (('0' <= c) && (c <= '9'))
{
digit = static_cast<uint8_t>(c - '0');
}
else
if (ParseHexDigit(*aString, digit) != kErrorNone)
{
break;
}
Expand Down
31 changes: 0 additions & 31 deletions src/core/utils/parse_cmdline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,37 +48,6 @@ static bool IsSeparator(char aChar) { return (aChar == ' ') || (aChar == '\t') |

static bool IsEscapable(char aChar) { return IsSeparator(aChar) || (aChar == '\\'); }

static Error ParseDigit(char aDigitChar, uint8_t &aValue)
{
Error error = kErrorNone;

VerifyOrExit(('0' <= aDigitChar) && (aDigitChar <= '9'), error = kErrorInvalidArgs);
aValue = static_cast<uint8_t>(aDigitChar - '0');

exit:
return error;
}

static Error ParseHexDigit(char aHexChar, uint8_t &aValue)
{
Error error = kErrorNone;

if (('A' <= aHexChar) && (aHexChar <= 'F'))
{
ExitNow(aValue = static_cast<uint8_t>(aHexChar - 'A' + 10));
}

if (('a' <= aHexChar) && (aHexChar <= 'f'))
{
ExitNow(aValue = static_cast<uint8_t>(aHexChar - 'a' + 10));
}

error = ParseDigit(aHexChar, aValue);

exit:
return error;
}

Error ParseCmd(char *aCommandString, Arg aArgs[], uint8_t aArgsMaxLength)
{
Error error = kErrorNone;
Expand Down
60 changes: 60 additions & 0 deletions tests/unit/test_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,8 @@ void TestStringParseUint8(void)
{"0256", kErrorParse}, // Larger than max `uint8_t`
};

uint8_t digit;

printf("\nTest 11: TestStringParseUint8() function\n");

for (const TestCase &testCase : kTestCases)
Expand All @@ -363,6 +365,64 @@ void TestStringParseUint8(void)
}
}

for (char c = '0'; c <= '9'; c++)
{
VerifyOrQuit(IsDigit(c));
VerifyOrQuit(!IsUppercase(c));
VerifyOrQuit(!IsLowercase(c));

SuccessOrQuit(ParseDigit(c, digit));
VerifyOrQuit(digit == (c - '0'));

SuccessOrQuit(ParseHexDigit(c, digit));
VerifyOrQuit(digit == (c - '0'));
}

for (char c = 'A'; c <= 'Z'; c++)
{
VerifyOrQuit(!IsDigit(c));
VerifyOrQuit(IsUppercase(c));
VerifyOrQuit(!IsLowercase(c));
VerifyOrQuit(ParseDigit(c, digit) != kErrorNone);

if (c <= 'F')
{
SuccessOrQuit(ParseHexDigit(c, digit));
VerifyOrQuit(digit == (c - 'A' + 10));
}
else
{
VerifyOrQuit(ParseHexDigit(c, digit) != kErrorNone);
}
}

for (char c = 'a'; c <= 'z'; c++)
{
VerifyOrQuit(!IsDigit(c));
VerifyOrQuit(!IsUppercase(c));
VerifyOrQuit(IsLowercase(c));
VerifyOrQuit(ParseDigit(c, digit) != kErrorNone);

if (c <= 'f')
{
SuccessOrQuit(ParseHexDigit(c, digit));
VerifyOrQuit(digit == (c - 'a' + 10));
}
else
{
VerifyOrQuit(ParseHexDigit(c, digit) != kErrorNone);
}
}

VerifyOrQuit(!IsDigit(static_cast<char>('0' - 1)));
VerifyOrQuit(!IsDigit(static_cast<char>('9' + 1)));

VerifyOrQuit(!IsUppercase(static_cast<char>('A' - 1)));
VerifyOrQuit(!IsUppercase(static_cast<char>('Z' + 1)));

VerifyOrQuit(!IsLowercase(static_cast<char>('a' - 1)));
VerifyOrQuit(!IsLowercase(static_cast<char>('z' + 1)));

printf("\n\n -- PASS\n");
}

Expand Down

0 comments on commit a57d927

Please sign in to comment.