[string] add StringParseUint8() helper function (#9026)

This commit adds a helper function to parse a decimal number from a
given string as `uint8_t` and skip over the parsed characters. This
is used to simplify the methods parsing IPv6/IPv4 address or prefix
from a string.
This commit is contained in:
Abtin Keshavarzian
2023-05-09 12:54:39 -07:00
committed by GitHub
parent 5081882bb9
commit a259d23e7f
6 changed files with 144 additions and 61 deletions
+26
View File
@@ -160,6 +160,32 @@ bool StringMatch(const char *aFirstString, const char *aSecondString, StringMatc
return Match(aFirstString, aSecondString, aMode) == kFullMatch;
}
Error StringParseUint8(const char *&aString, uint8_t &aUint8)
{
return StringParseUint8(aString, aUint8, NumericLimits<uint8_t>::kMax);
}
Error StringParseUint8(const char *&aString, uint8_t &aUint8, uint8_t aMaxValue)
{
Error error = kErrorParse;
const char *cur = aString;
uint16_t value = 0;
for (; (*cur >= '0') && (*cur <= '9'); cur++)
{
value *= 10;
value += static_cast<uint8_t>(*cur - '0');
VerifyOrExit(value <= aMaxValue, error = kErrorParse);
error = kErrorNone;
}
aString = cur;
aUint8 = static_cast<uint8_t>(value);
exit:
return error;
}
void StringConvertToLowercase(char *aString)
{
for (; *aString != kNullChar; aString++)
+40 -1
View File
@@ -144,7 +144,7 @@ bool StringEndsWith(const char *aString, char aChar);
bool StringEndsWith(const char *aString, const char *aSubString, StringMatchMode aMode = kStringExactMatch);
/**
* This method checks whether or not two null-terminated strings match.
* This function checks whether or not two null-terminated strings match.
*
* @param[in] aFirstString A pointer to the first string.
* @param[in] aSecondString A pointer to the second string.
@@ -156,6 +156,45 @@ bool StringEndsWith(const char *aString, const char *aSubString, StringMatchMode
*/
bool StringMatch(const char *aFirstString, const char *aSecondString, StringMatchMode aMode = kStringExactMatch);
/**
* This function parses a decimal number from a string as `uint8_t` and skips over the parsed characters.
*
* If the string does not start with a digit, `kErrorParse` is returned.
*
* All the digit characters in the string are parsed until reaching a non-digit character. The pointer `aString` is
* updated to point to the first non-digit character after the parsed digits.
*
* If the parsed number value is larger than @p aMaxValue, `kErrorParse` is returned.
*
* @param[in,out] aString A reference to a pointer to string to parse.
* @param[out] aUint8 A reference to return the parsed value.
* @param[in] aMaxValue Maximum allowed value for the parsed number.
*
* @retval kErrorNone Successfully parsed the number from string. @p aString and @p aUint8 are updated.
* @retval kErrorParse Failed to parse the number from @p aString, or parsed number is larger than @p aMaxValue.
*
*/
Error StringParseUint8(const char *&aString, uint8_t &aUint8, uint8_t aMaxValue);
/**
* This function parses a decimal number from a string as `uint8_t` and skips over the parsed characters.
*
* If the string does not start with a digit, `kErrorParse` is returned.
*
* All the digit characters in the string are parsed until reaching a non-digit character. The pointer `aString` is
* updated to point to the first non-digit character after the parsed digits.
*
* If the parsed number value is larger than maximum `uint8_t` value, `kErrorParse` is returned.
*
* @param[in,out] aString A reference to a pointer to string to parse.
* @param[out] aUint8 A reference to return the parsed value.
*
* @retval kErrorNone Successfully parsed the number from string. @p aString and @p aUint8 are updated.
* @retval kErrorParse Failed to parse the number from @p aString, or parsed number is out of range.
*
*/
Error StringParseUint8(const char *&aString, uint8_t &aUint8);
/**
* This function converts all uppercase letter characters in a given string to lowercase.
*
+13 -46
View File
@@ -41,39 +41,23 @@ Error Address::FromString(const char *aString, char aTerminatorChar)
{
constexpr char kSeparatorChar = '.';
Error error = kErrorParse;
Error error = kErrorParse;
const char *cur = aString;
for (uint8_t index = 0;; index++)
{
uint16_t value = 0;
uint8_t hasFirstDigit = false;
for (char digitChar = *aString;; ++aString, digitChar = *aString)
{
if ((digitChar < '0') || (digitChar > '9'))
{
break;
}
value = static_cast<uint16_t>((value * 10) + static_cast<uint8_t>(digitChar - '0'));
VerifyOrExit(value <= NumericLimits<uint8_t>::kMax);
hasFirstDigit = true;
}
VerifyOrExit(hasFirstDigit);
mFields.m8[index] = static_cast<uint8_t>(value);
SuccessOrExit(StringParseUint8(cur, mFields.m8[index]));
if (index == sizeof(Address) - 1)
{
break;
}
VerifyOrExit(*aString == kSeparatorChar);
aString++;
VerifyOrExit(*cur == kSeparatorChar);
cur++;
}
VerifyOrExit(*aString == aTerminatorChar);
VerifyOrExit(*cur == aTerminatorChar);
error = kErrorNone;
exit:
@@ -152,34 +136,17 @@ Error Cidr::FromString(const char *aString)
constexpr char kSlashChar = '/';
constexpr uint16_t kMaxCidrLength = 32;
Error error = kErrorParse;
Error error = kErrorParse;
const char *cur;
SuccessOrExit(AsCoreType(&mAddress).FromString(aString, kSlashChar));
aString = StringFind(aString, kSlashChar);
VerifyOrExit(aString != nullptr);
aString++;
cur = StringFind(aString, kSlashChar);
VerifyOrExit(cur != nullptr);
cur++;
{
uint8_t hasFirstDigit = false;
uint16_t value = 0;
for (char digitChar = *aString;; ++aString, digitChar = *aString)
{
if ((digitChar < '0') || (digitChar > '9'))
{
break;
}
value = static_cast<uint16_t>((value * 10) + static_cast<uint8_t>(digitChar - '0'));
VerifyOrExit(value <= kMaxCidrLength);
hasFirstDigit = true;
}
VerifyOrExit(*aString == kNullChar);
VerifyOrExit(hasFirstDigit);
mLength = static_cast<uint8_t>(value);
}
SuccessOrExit(StringParseUint8(cur, mLength, kMaxCidrLength));
VerifyOrExit(*cur == kNullChar);
error = kErrorNone;
+6 -14
View File
@@ -163,27 +163,19 @@ Error Prefix::FromString(const char *aString)
constexpr char kNullChar = '\0';
Error error = kErrorParse;
const char *slashPosition;
uint16_t plen = 0;
const char *cur;
VerifyOrExit(aString != nullptr);
slashPosition = StringFind(aString, kSlashChar);
VerifyOrExit(slashPosition != nullptr);
cur = StringFind(aString, kSlashChar);
VerifyOrExit(cur != nullptr);
SuccessOrExit(AsCoreType(&mPrefix).ParseFrom(aString, kSlashChar));
VerifyOrExit(slashPosition[1] != kNullChar);
cur++;
SuccessOrExit(StringParseUint8(cur, mLength, kMaxLength));
VerifyOrExit(*cur == kNullChar);
for (const char *cur = slashPosition + 1; *cur != kNullChar; cur++)
{
VerifyOrExit((*cur >= '0') && (*cur <= '9'));
plen *= 10;
plen += static_cast<uint8_t>(*cur - '0');
VerifyOrExit(plen <= kMaxLength);
}
SetLength(static_cast<uint8_t>(plen));
error = kErrorNone;
exit:
+1
View File
@@ -295,6 +295,7 @@ void TestIp4CidrFromString(void)
// valid address, invalid suffix
{"1.2.3.4/33", {0}, 0, ot::kErrorParse}, // Prefix length too large
{"1.2.3.4/12345678", {0}, 0, ot::kErrorParse}, // Prefix length too large?
{"1.2.3.4/12a", {0}, 0, ot::kErrorParse}, // Extra char after prefix length.
{"1.2.3.4/-1", {0}, 0, ot::kErrorParse}, // Not even a non-negative integer.
{"1.2.3.4/3.14", {0}, 0, ot::kErrorParse}, // Not even a integer.
{"1.2.3.4/abcd", {0}, 0, ot::kErrorParse}, // Not even a number.
+58
View File
@@ -309,6 +309,63 @@ void TestStringToLowercase(void)
printf(" -- PASS\n");
}
void TestStringParseUint8(void)
{
struct TestCase
{
const char *mString;
Error mError;
uint8_t mExpectedValue;
uint16_t mParsedLength;
};
static const TestCase kTestCases[] = {
{"0", kErrorNone, 0, 1},
{"1", kErrorNone, 1, 1},
{"12", kErrorNone, 12, 2},
{"91", kErrorNone, 91, 2},
{"200", kErrorNone, 200, 3},
{"00000", kErrorNone, 0, 5},
{"00000255", kErrorNone, 255, 8},
{"2 00", kErrorNone, 2, 1},
{"77a12", kErrorNone, 77, 2},
{"", kErrorParse}, // Does not start with digit char ['0'-'9']
{"a12", kErrorParse}, // Does not start with digit char ['0'-'9']
{" 12", kErrorParse}, // Does not start with digit char ['0'-'9']
{"256", kErrorParse}, // Larger than max `uint8_t`
{"1000", kErrorParse}, // Larger than max `uint8_t`
{"0256", kErrorParse}, // Larger than max `uint8_t`
};
printf("\nTest 11: TestStringParseUint8() function\n");
for (const TestCase &testCase : kTestCases)
{
const char *string = testCase.mString;
Error error;
uint8_t u8;
error = StringParseUint8(string, u8);
VerifyOrQuit(error == testCase.mError);
if (testCase.mError == kErrorNone)
{
printf("\n%-10s -> %-3u (expect: %-3u), len:%u (expect:%u)", testCase.mString, u8, testCase.mExpectedValue,
static_cast<uint8_t>(string - testCase.mString), testCase.mParsedLength);
VerifyOrQuit(u8 == testCase.mExpectedValue);
VerifyOrQuit(string - testCase.mString == testCase.mParsedLength);
}
else
{
printf("\n%-10s -> kErrorParse", testCase.mString);
}
}
printf("\n\n -- PASS\n");
}
// gcc-4 does not support constexpr function
#if __GNUC__ > 4
static_assert(ot::AreStringsInOrder("a", "b"), "AreStringsInOrder() failed");
@@ -331,6 +388,7 @@ int main(void)
ot::TestStringEndsWith();
ot::TestStringMatch();
ot::TestStringToLowercase();
ot::TestStringParseUint8();
printf("\nAll tests passed.\n");
return 0;
}