mirror of
https://github.com/espressif/openthread.git
synced 2026-10-02 13:57:38 +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;
|
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)
|
void StringConvertToLowercase(char *aString)
|
||||||
{
|
{
|
||||||
for (; *aString != kNullChar; 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);
|
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] aFirstString A pointer to the first string.
|
||||||
* @param[in] aSecondString A pointer to the second 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);
|
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.
|
* 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 = '.';
|
constexpr char kSeparatorChar = '.';
|
||||||
|
|
||||||
Error error = kErrorParse;
|
Error error = kErrorParse;
|
||||||
|
const char *cur = aString;
|
||||||
|
|
||||||
for (uint8_t index = 0;; index++)
|
for (uint8_t index = 0;; index++)
|
||||||
{
|
{
|
||||||
uint16_t value = 0;
|
SuccessOrExit(StringParseUint8(cur, mFields.m8[index]));
|
||||||
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);
|
|
||||||
|
|
||||||
if (index == sizeof(Address) - 1)
|
if (index == sizeof(Address) - 1)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
VerifyOrExit(*aString == kSeparatorChar);
|
VerifyOrExit(*cur == kSeparatorChar);
|
||||||
aString++;
|
cur++;
|
||||||
}
|
}
|
||||||
|
|
||||||
VerifyOrExit(*aString == aTerminatorChar);
|
VerifyOrExit(*cur == aTerminatorChar);
|
||||||
error = kErrorNone;
|
error = kErrorNone;
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
@@ -152,34 +136,17 @@ Error Cidr::FromString(const char *aString)
|
|||||||
constexpr char kSlashChar = '/';
|
constexpr char kSlashChar = '/';
|
||||||
constexpr uint16_t kMaxCidrLength = 32;
|
constexpr uint16_t kMaxCidrLength = 32;
|
||||||
|
|
||||||
Error error = kErrorParse;
|
Error error = kErrorParse;
|
||||||
|
const char *cur;
|
||||||
|
|
||||||
SuccessOrExit(AsCoreType(&mAddress).FromString(aString, kSlashChar));
|
SuccessOrExit(AsCoreType(&mAddress).FromString(aString, kSlashChar));
|
||||||
|
|
||||||
aString = StringFind(aString, kSlashChar);
|
cur = StringFind(aString, kSlashChar);
|
||||||
VerifyOrExit(aString != nullptr);
|
VerifyOrExit(cur != nullptr);
|
||||||
aString++;
|
cur++;
|
||||||
|
|
||||||
{
|
SuccessOrExit(StringParseUint8(cur, mLength, kMaxCidrLength));
|
||||||
uint8_t hasFirstDigit = false;
|
VerifyOrExit(*cur == kNullChar);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
error = kErrorNone;
|
error = kErrorNone;
|
||||||
|
|
||||||
|
|||||||
@@ -163,27 +163,19 @@ Error Prefix::FromString(const char *aString)
|
|||||||
constexpr char kNullChar = '\0';
|
constexpr char kNullChar = '\0';
|
||||||
|
|
||||||
Error error = kErrorParse;
|
Error error = kErrorParse;
|
||||||
const char *slashPosition;
|
const char *cur;
|
||||||
uint16_t plen = 0;
|
|
||||||
|
|
||||||
VerifyOrExit(aString != nullptr);
|
VerifyOrExit(aString != nullptr);
|
||||||
|
|
||||||
slashPosition = StringFind(aString, kSlashChar);
|
cur = StringFind(aString, kSlashChar);
|
||||||
VerifyOrExit(slashPosition != nullptr);
|
VerifyOrExit(cur != nullptr);
|
||||||
|
|
||||||
SuccessOrExit(AsCoreType(&mPrefix).ParseFrom(aString, kSlashChar));
|
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;
|
error = kErrorNone;
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
|
|||||||
@@ -295,6 +295,7 @@ void TestIp4CidrFromString(void)
|
|||||||
// valid address, invalid suffix
|
// valid address, invalid suffix
|
||||||
{"1.2.3.4/33", {0}, 0, ot::kErrorParse}, // Prefix length too large
|
{"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/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/-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/3.14", {0}, 0, ot::kErrorParse}, // Not even a integer.
|
||||||
{"1.2.3.4/abcd", {0}, 0, ot::kErrorParse}, // Not even a number.
|
{"1.2.3.4/abcd", {0}, 0, ot::kErrorParse}, // Not even a number.
|
||||||
|
|||||||
@@ -309,6 +309,63 @@ void TestStringToLowercase(void)
|
|||||||
printf(" -- PASS\n");
|
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
|
// gcc-4 does not support constexpr function
|
||||||
#if __GNUC__ > 4
|
#if __GNUC__ > 4
|
||||||
static_assert(ot::AreStringsInOrder("a", "b"), "AreStringsInOrder() failed");
|
static_assert(ot::AreStringsInOrder("a", "b"), "AreStringsInOrder() failed");
|
||||||
@@ -331,6 +388,7 @@ int main(void)
|
|||||||
ot::TestStringEndsWith();
|
ot::TestStringEndsWith();
|
||||||
ot::TestStringMatch();
|
ot::TestStringMatch();
|
||||||
ot::TestStringToLowercase();
|
ot::TestStringToLowercase();
|
||||||
|
ot::TestStringParseUint8();
|
||||||
printf("\nAll tests passed.\n");
|
printf("\nAll tests passed.\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user