mirror of
https://github.com/espressif/openthread.git
synced 2026-08-02 00:57:47 +00:00
[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:
@@ -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++)
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user