From e66b9a3958bfdc3fb495a189737265561e9a99bd Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 17 Jun 2021 10:23:25 -0700 Subject: [PATCH] [cmd-parser] simplify `ParseAsHexString()` & `Cli::UdpExample::ProcessSend()` (#6726) This commit enhances `CmdLineParser` methods that parse a hex string adding a new version which allows a (longer) hex string to be parsed in short segments sequentially. The unit test `test_cmd_line_parser` is also updated to verify the behavior of the newly added method. This commit also updates CLI `UdpExample` implementation to use the new method for parsing message payload data from input hex string and simplifies how `ProcessSend()` parses different arguments indicating message payload options. --- src/cli/cli_udp.cpp | 153 +++++++++++++++------------- src/cli/cli_udp.hpp | 11 +- src/core/utils/parse_cmdline.cpp | 123 ++++++++++++---------- src/core/utils/parse_cmdline.hpp | 71 +++++++------ tests/unit/test_cmd_line_parser.cpp | 103 ++++++++++++++----- 5 files changed, 274 insertions(+), 187 deletions(-) diff --git a/src/cli/cli_udp.cpp b/src/cli/cli_udp.cpp index 5c50a16ca..eae8582d2 100644 --- a/src/cli/cli_udp.cpp +++ b/src/cli/cli_udp.cpp @@ -114,88 +114,70 @@ otError UdpExample::ProcessOpen(uint8_t aArgsLength, Arg aArgs[]) otError UdpExample::ProcessSend(uint8_t aArgsLength, Arg aArgs[]) { - otError error = OT_ERROR_NONE; + otError error = OT_ERROR_NONE; + otMessage * message = nullptr; otMessageInfo messageInfo; - otMessage * message = nullptr; - uint8_t curArg = 0; - uint16_t payloadLength = 0; - PayloadType payloadType = kTypeText; + uint8_t argIndex = 0; otMessageSettings messageSettings = {mLinkSecurityEnabled, OT_MESSAGE_PRIORITY_NORMAL}; memset(&messageInfo, 0, sizeof(messageInfo)); + // Possible argument formats: + // + // send + // send + // send + // send + VerifyOrExit(aArgsLength >= 1 && aArgsLength <= 4, error = OT_ERROR_INVALID_ARGS); if (aArgsLength > 2) { - SuccessOrExit(error = aArgs[curArg++].ParseAsIp6Address(messageInfo.mPeerAddr)); - SuccessOrExit(error = aArgs[curArg++].ParseAsUint16(messageInfo.mPeerPort)); - } - - if (aArgsLength == 2 || aArgsLength == 4) - { - uint8_t typePos = curArg++; - - if (aArgs[typePos] == "-s") - { - payloadType = kTypeAutoSize; - SuccessOrExit(error = aArgs[curArg].ParseAsUint16(payloadLength)); - } - else if (aArgs[typePos] == "-x") - { - payloadLength = aArgs[curArg].GetLength(); - payloadType = kTypeHexString; - } - else if (aArgs[typePos] == "-t") - { - payloadType = kTypeText; - } + SuccessOrExit(error = aArgs[argIndex++].ParseAsIp6Address(messageInfo.mPeerAddr)); + SuccessOrExit(error = aArgs[argIndex++].ParseAsUint16(messageInfo.mPeerPort)); } message = otUdpNewMessage(mInterpreter.mInstance, &messageSettings); VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS); - switch (payloadType) + if (aArgs[argIndex] == "-s") { - case kTypeText: - SuccessOrExit(error = otMessageAppend(message, aArgs[curArg].GetCString(), aArgs[curArg].GetLength())); - break; - case kTypeAutoSize: - SuccessOrExit(error = WriteCharToBuffer(message, payloadLength)); - break; - case kTypeHexString: - { - uint8_t buf[50]; - uint16_t bufLen; - uint16_t conversionLength = 0; - const char *hexString = aArgs[curArg].GetCString(); + // Auto-generated payload with a given length - while (payloadLength > 0) + uint16_t payloadLength; + + argIndex++; + VerifyOrExit(argIndex < aArgsLength, error = OT_ERROR_INVALID_ARGS); + SuccessOrExit(error = aArgs[argIndex].ParseAsUint16(payloadLength)); + SuccessOrExit(error = PrepareAutoGeneratedPayload(*message, payloadLength)); + } + else if (aArgs[argIndex] == "-x") + { + // Binary hex data payload + + argIndex++; + VerifyOrExit(argIndex < aArgsLength, error = OT_ERROR_INVALID_ARGS); + SuccessOrExit(error = PrepareHexStringPaylod(*message, aArgs[argIndex].GetCString())); + } + else + { + // Text payload (same as without specifying the type) + + if (aArgs[argIndex] == "-t") { - bufLen = sizeof(buf); - SuccessOrExit(error = ParseAsHexString(hexString, bufLen, buf, Utils::CmdLineParser::kAllowTruncate)); - VerifyOrExit(bufLen > 0, error = OT_ERROR_INVALID_ARGS); - - conversionLength = static_cast(bufLen * 2); - - if ((payloadLength & 0x01) != 0) - { - conversionLength -= 1; - } - - hexString += conversionLength; - payloadLength -= conversionLength; - SuccessOrExit(error = otMessageAppend(message, buf, bufLen)); + argIndex++; } - break; - } + + VerifyOrExit(argIndex < aArgsLength, error = OT_ERROR_INVALID_ARGS); + SuccessOrExit(error = otMessageAppend(message, aArgs[argIndex].GetCString(), aArgs[argIndex].GetLength())); } - error = otUdpSend(mInterpreter.mInstance, &mSocket, message, &messageInfo); + SuccessOrExit(error = otUdpSend(mInterpreter.mInstance, &mSocket, message, &messageInfo)); + + message = nullptr; exit: - - if (error != OT_ERROR_NONE && message != nullptr) + if (message != nullptr) { otMessageFree(message); } @@ -219,26 +201,28 @@ otError UdpExample::ProcessLinkSecurity(uint8_t aArgsLength, Arg aArgs[]) return error; } -otError UdpExample::WriteCharToBuffer(otMessage *aMessage, uint16_t aMessageSize) +otError UdpExample::PrepareAutoGeneratedPayload(otMessage &aMessage, uint16_t aPayloadLength) { otError error = OT_ERROR_NONE; - uint8_t character = 0x30; // 0 + uint8_t character = '0'; - for (uint16_t index = 0; index < aMessageSize; index++) + for (; aPayloadLength != 0; aPayloadLength--) { - SuccessOrExit(error = otMessageAppend(aMessage, &character, 1)); - character++; + SuccessOrExit(error = otMessageAppend(&aMessage, &character, sizeof(character))); switch (character) { - case 0x3A: // 9 - character = 0x41; // A + case '9': + character = 'A'; break; - case 0x5B: // Z - character = 0x61; // a + case 'Z': + character = 'a'; break; - case 0x7B: // z - character = 0x30; // 0 + case 'z': + character = '0'; + break; + default: + character++; break; } } @@ -247,6 +231,33 @@ exit: return error; } +otError UdpExample::PrepareHexStringPaylod(otMessage &aMessage, const char *aHexString) +{ + enum : uint8_t + { + kBufferSize = 50, + }; + + otError error; + uint8_t buf[kBufferSize]; + uint16_t length; + bool done = false; + + while (!done) + { + length = sizeof(buf); + error = Utils::CmdLineParser::ParseAsHexStringSegment(aHexString, length, buf); + + VerifyOrExit((error == OT_ERROR_NONE) || (error == OT_ERROR_PENDING)); + done = (error == OT_ERROR_NONE); + + SuccessOrExit(error = otMessageAppend(&aMessage, buf, length)); + } + +exit: + return error; +} + otError UdpExample::Process(uint8_t aArgsLength, Arg aArgs[]) { otError error = OT_ERROR_INVALID_ARGS; diff --git a/src/cli/cli_udp.hpp b/src/cli/cli_udp.hpp index 79794a13d..a81a20341 100644 --- a/src/cli/cli_udp.hpp +++ b/src/cli/cli_udp.hpp @@ -79,13 +79,6 @@ private: otError (UdpExample::*mHandler)(uint8_t aArgsLength, Arg aArgs[]); }; - enum PayloadType - { - kTypeText = 0, - kTypeAutoSize = 1, - kTypeHexString = 2, - }; - otError ProcessHelp(uint8_t aArgsLength, Arg aArgs[]); otError ProcessBind(uint8_t aArgsLength, Arg aArgs[]); otError ProcessClose(uint8_t aArgsLength, Arg aArgs[]); @@ -93,7 +86,9 @@ private: otError ProcessOpen(uint8_t aArgsLength, Arg aArgs[]); otError ProcessSend(uint8_t aArgsLength, Arg aArgs[]); otError ProcessLinkSecurity(uint8_t aArgsLength, Arg aArgs[]); - otError WriteCharToBuffer(otMessage *aMessage, uint16_t aMessageSize); + + static otError PrepareAutoGeneratedPayload(otMessage &aMessage, uint16_t aPayloadLength); + static otError PrepareHexStringPaylod(otMessage &aMessage, const char *aHexString); static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); void HandleUdpReceive(otMessage *aMessage, const otMessageInfo *aMessageInfo); diff --git a/src/core/utils/parse_cmdline.cpp b/src/core/utils/parse_cmdline.cpp index 1893aeb36..5026b39a8 100644 --- a/src/core/utils/parse_cmdline.cpp +++ b/src/core/utils/parse_cmdline.cpp @@ -275,67 +275,88 @@ exit: } #endif // #if OPENTHREAD_FTD || OPENTHREAD_MTD -Error ParseAsHexString(const char *aString, uint8_t *aBuffer, uint16_t aSize) +enum HexStringParseMode { - Error error; - uint16_t readSize = aSize; + kModeExtactSize, // Parse hex string expecting an exact size (number of bytes when parsed). + kModeUpToSize, // Parse hex string expecting less than or equal a given size. + kModeAllowPartial, // Allow parsing of partial segments. +}; - SuccessOrExit(error = ParseAsHexString(aString, readSize, aBuffer, kDisallowTruncate)); - VerifyOrExit(readSize == aSize, error = kErrorInvalidArgs); +static Error ParseHexString(const char *&aString, uint16_t &aSize, uint8_t *aBuffer, HexStringParseMode aMode) +{ + Error error = kErrorNone; + size_t stringLength = strlen(aString); + size_t expectedSize = (stringLength + 1) / 2; + size_t parsedSize = 0; + bool skipFirstDigit; + + switch (aMode) + { + case kModeExtactSize: + VerifyOrExit(expectedSize == aSize, error = kErrorInvalidArgs); + break; + case kModeUpToSize: + VerifyOrExit(expectedSize <= aSize, error = kErrorInvalidArgs); + break; + case kModeAllowPartial: + break; + } + + // If number of chars in hex string is odd, we skip parsing + // the first digit. + + skipFirstDigit = ((stringLength & 1) != 0); + + while (parsedSize < expectedSize) + { + uint8_t digit; + + if ((aMode == kModeAllowPartial) && (parsedSize == aSize)) + { + // If partial parse mode is allowed, stop once we read the + // requested size. + ExitNow(error = kErrorPending); + } + + if (skipFirstDigit) + { + *aBuffer = 0; + skipFirstDigit = false; + } + else + { + SuccessOrExit(error = ParseHexDigit(*aString, digit)); + aString++; + *aBuffer = static_cast(digit << 4); + } + + SuccessOrExit(error = ParseHexDigit(*aString, digit)); + aString++; + *aBuffer |= digit; + + aBuffer++; + parsedSize++; + } + + aSize = static_cast(parsedSize); exit: return error; } -Error ParseAsHexString(const char *aString, uint16_t &aSize, uint8_t *aBuffer, HexStringParseMode aMode) +Error ParseAsHexString(const char *aString, uint8_t *aBuffer, uint16_t aSize) { - Error error = kErrorNone; - uint8_t byte = 0; - uint16_t readBytes = 0; - const char *hex = aString; - size_t hexLength = strlen(aString); - uint8_t numChars; + return ParseHexString(aString, aSize, aBuffer, kModeExtactSize); +} - if (aMode == kDisallowTruncate) - { - VerifyOrExit((hexLength + 1) / 2 <= aSize, error = kErrorInvalidArgs); - } +Error ParseAsHexString(const char *aString, uint16_t &aSize, uint8_t *aBuffer) +{ + return ParseHexString(aString, aSize, aBuffer, kModeUpToSize); +} - // Handle the case where number of chars in hex string is odd. - numChars = hexLength & 1; - - while (*hex != '\0') - { - uint8_t digit; - - SuccessOrExit(error = ParseHexDigit(*hex, digit)); - byte |= digit; - - hex++; - numChars++; - - if (numChars >= 2) - { - numChars = 0; - *aBuffer++ = byte; - byte = 0; - readBytes++; - - if (readBytes == aSize) - { - ExitNow(); - } - } - else - { - byte <<= 4; - } - } - - aSize = readBytes; - -exit: - return error; +Error ParseAsHexStringSegment(const char *&aString, uint16_t &aSize, uint8_t *aBuffer) +{ + return ParseHexString(aString, aSize, aBuffer, kModeAllowPartial); } void Arg::CopyArgsToStringArray(Arg aArgs[], uint8_t aArgsLength, char *aStrings[]) diff --git a/src/core/utils/parse_cmdline.hpp b/src/core/utils/parse_cmdline.hpp index 01c4fcb10..dda5fe696 100644 --- a/src/core/utils/parse_cmdline.hpp +++ b/src/core/utils/parse_cmdline.hpp @@ -53,16 +53,6 @@ namespace CmdLineParser { * @{ */ -/** - * This enumeration type represents the parse mode value used as a parameter in `ParseAsHexString()`. - * - */ -enum HexStringParseMode : uint8_t -{ - kDisallowTruncate, // Disallow truncation of hex string. - kAllowTruncate, // Allow truncation of hex string. -}; - /** * This function parses a string as a `uint8_t` value. * @@ -217,6 +207,9 @@ otError ParseAsIp6Prefix(const char *aString, otIp6Prefix &aPrefix); * there are fewer or more bytes in hex string that @p aSize, the parsed bytes (up to @p aSize) are copied into the * `aBuffer` and `kErrorInvalidArgs` is returned. * + * This function correctly handles hex strings with even or odd length. For example, "AABBCCDD" (with even length) is + * parsed as {0xaa, 0xbb, 0xcc, 0xdd} and "123" (with odd length) is parsed as {0x01, 0x23}. + * * @param[in] aString The string to parse. * @param[out] aBuffer A pointer to a buffer to output the parsed byte sequence. * @param[in] aSize The expected size of byte sequence (number of bytes after parsing). @@ -234,6 +227,9 @@ otError ParseAsHexString(const char *aString, uint8_t *aBuffer, uint16_t aSize); * If there are fewer or more bytes in hex string that @p kBufferSize, the parsed bytes (up to @p kBufferSize) are * copied into the `aBuffer` and `kErrorInvalidArgs` is returned. * + * This function correctly handles hex strings with even or odd length. For example, "AABBCCDD" (with even length) is + * parsed as {0xaa, 0xbb, 0xcc, 0xdd} and "123" (with odd length) is parsed as {0x01, 0x23}. + * * @tparam kBufferSize The byte array size (number of bytes). * * @param[in] aString The string to parse. @@ -251,24 +247,45 @@ template static otError ParseAsHexString(const char *aStr /** * This function parses a hex string into a byte array. * - * If @p aMode disallows truncation (`kDisallowTruncate`), this function verifies that parses hex string bytes fit in - * @p aBuffer with its given size in @aSize. Otherwise when @p aMode allows truncation, extra bytes after @p aSize bytes - * are ignored. + * This function verifies that the parsed hex string bytes fit in @p aBuffer with its given @p aSize. + * + * This function correctly handles hex strings with even or odd length. For example, "AABBCCDD" (with even length) is + * parsed as {0xaa, 0xbb, 0xcc, 0xdd} and "123" (with odd length) is parsed as {0x01, 0x23}. * * @param[in] aString The string to parse. * @param[inout] aSize On entry indicates the number of bytes in @p aBuffer (max size of @p aBuffer). - * On exit provides number of bytes parsed and copied into @p aBuffer + * On exit provides number of bytes parsed and copied into @p aBuffer. * @param[out] aBuffer A pointer to a buffer to output the parsed byte sequence. - * @param[in] aMode Indicates parsing mode whether to allow truncation or not. * * @retval kErrorNone The string was parsed successfully. - * @retval kErrorInvalidArgs The string does not contain valid format or too many bytes (if truncation not allowed) + * @retval kErrorInvalidArgs The string does not contain valid format or too many bytes. * */ -otError ParseAsHexString(const char * aString, - uint16_t & aSize, - uint8_t * aBuffer, - HexStringParseMode aMode = kDisallowTruncate); +otError ParseAsHexString(const char *aString, uint16_t &aSize, uint8_t *aBuffer); + +/** + * This function parses a segment of a hex string up to a given size. + * + * This function allows a longer hex string to be parsed and read in smaller segments into a given buffer. If the + * entire hex string bytes can fit in the given @p aBuffer with its @p aSize, they are copied into @p aBuffer and + * function returns `kErrorNone`. Otherwise, @p aSize bytes are read and copied and function returns `kErrorPending` + * to indicate that there are more bytes to parse. The @p aString is also updated to skip over the parsed segment. + * + * This function correctly handles hex strings with even or odd length. For example, "AABBCCDD" (with even length) is + * parsed as {0xaa, 0xbb, 0xcc, 0xdd} and "123" (with odd length) is parsed as {0x01, 0x23}. + * + * @param[inout] aString A reference to string to parse. On successful parse, updated to skip parsed digits. + * @param[inout] aSize On entry indicates the segment size (number of bytes in @p aBuffer). + * On exit provides number of bytes parsed and copied into @p aBuffer. + * @param[out] aBuffer A pointer to a buffer to output the parsed byte sequence. + * + * @retval kErrorNone The string was parsed successfully to the end of string. + * @retval kErrorPedning The string segment was parsed successfully, but there are additional bytes remaining + * to be parsed. + * @retval kErrorInvalidArgs The string does not contain valid format hex digits. + * + */ +otError ParseAsHexStringSegment(const char *&aString, uint16_t &aSize, uint8_t *aBuffer); /** * This class represents a single argument from an argument list. @@ -523,23 +540,19 @@ public: /** * This method parses the argument as a hex string into a byte array. * - * If @p aMode disallows truncation (`kDisallowTruncate`), this method verifies that parses hex string bytes fit in - * @p aBuffer with its given size in @aSize. Otherwise when @p aMode allows truncation, extra bytes after @p aSize - * bytes are ignored. + * This method verifies that the parsed hex string bytes fit in @p aBuffer with its given @p aSize. * * @param[inout] aSize On entry indicates the number of bytes in @p aBuffer (max size of @p aBuffer). - * On exit provides number of bytes parsed and copied into @p aBuffer + * On exit provides number of bytes parsed and copied into @p aBuffer. * @param[out] aBuffer A pointer to a buffer to output the parsed byte sequence. - * @param[in] aMode Indicates parsing mode whether to allow truncation or not. * * @retval kErrorNone The argument was parsed successfully. - * @retval kErrorInvalidArgs The argument does not contain valid format or too many bytes (if truncation not - * allowed) + * @retval kErrorInvalidArgs The argument does not contain valid format or too many bytes. * */ - otError ParseAsHexString(uint16_t &aSize, uint8_t *aBuffer, HexStringParseMode aMode = kDisallowTruncate) + otError ParseAsHexString(uint16_t &aSize, uint8_t *aBuffer) { - return CmdLineParser::ParseAsHexString(mString, aSize, aBuffer, aMode); + return CmdLineParser::ParseAsHexString(mString, aSize, aBuffer); } /** diff --git a/tests/unit/test_cmd_line_parser.cpp b/tests/unit/test_cmd_line_parser.cpp index 6dc64c2d1..396a6d376 100644 --- a/tests/unit/test_cmd_line_parser.cpp +++ b/tests/unit/test_cmd_line_parser.cpp @@ -35,10 +35,11 @@ #include "common/instance.hpp" #include "utils/parse_cmdline.hpp" -#include "test_util.h" +#include "test_util.hpp" using ot::Utils::CmdLineParser::ParseAsBool; using ot::Utils::CmdLineParser::ParseAsHexString; +using ot::Utils::CmdLineParser::ParseAsHexStringSegment; using ot::Utils::CmdLineParser::ParseAsInt16; using ot::Utils::CmdLineParser::ParseAsInt32; using ot::Utils::CmdLineParser::ParseAsInt8; @@ -242,12 +243,17 @@ void TestParsingInts(void) void TestParsingHexStrings(void) { - const char kHexString[] = "DeadBeefCafeBabe"; - const uint8_t kParsedArray[] = {0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xba, 0xbe}; + const char kEvenHexString[] = "DeadBeefCafeBabe"; + const uint8_t kEvenParsedArray[] = {0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xba, 0xbe}; - uint8_t buffer[sizeof(kParsedArray)]; - uint8_t buf3[3]; - uint16_t len; + const char kOddHexString[] = "abcdef9876543"; + const uint8_t kOddParsedArray[] = {0xa, 0xbc, 0xde, 0xf9, 0x87, 0x65, 0x43}; + + uint8_t buffer[sizeof(kEvenParsedArray)]; + uint8_t buf3[3]; + uint16_t len; + const char * string; + const uint8_t *bufPtr; // Verify `ParseAsHexString(const char *aString, uint8_t *aBuffer, uint16_t aSize)` @@ -272,37 +278,78 @@ void TestParsingHexStrings(void) VerifyOrQuit(ParseAsHexString("1122334", buf3) != OT_ERROR_NONE, "ParseAsHexString() passed with bad input"); VerifyOrQuit(ParseAsHexString("11223344", buf3) != OT_ERROR_NONE, "ParseAsHexString() passed with bad input"); SuccessOrQuit(ParseAsHexString("abbade", buf3), "ParseAsHexString() failed"); + VerifyOrQuit(buf3[0] == 0xab && buf3[1] == 0xba && buf3[2] == 0xde, "ParseAsHexString() parsed incorrectly"); + SuccessOrQuit(ParseAsHexString("012345", buf3), "ParseAsHexString() failed"); + VerifyOrQuit(buf3[0] == 0x01 && buf3[1] == 0x23 && buf3[2] == 0x45, "ParseAsHexString() parsed incorrectly"); + SuccessOrQuit(ParseAsHexString("12345", buf3), "ParseAsHexString() failed with odd length"); + VerifyOrQuit(buf3[0] == 0x01 && buf3[1] == 0x23 && buf3[2] == 0x45, "ParseAsHexString() parsed incorrectly"); - SuccessOrQuit(ParseAsHexString(kHexString, buffer), "ParseAsHexString failed"); - VerifyOrQuit(memcmp(buffer, kParsedArray, sizeof(buffer)) == 0, "ParseAsHexString() parsed incorrectly"); + SuccessOrQuit(ParseAsHexString(kEvenHexString, buffer), "ParseAsHexString failed"); + VerifyOrQuit(memcmp(buffer, kEvenParsedArray, sizeof(buffer)) == 0, "ParseAsHexString() parsed incorrectly"); - // Verify truncation + // Verify `ParseAsHexString(const char *aString, uint16_t &aSize, uint8_t *aBuffer)` + printf("----------------------------------------------------------\n"); len = sizeof(buffer); - SuccessOrQuit(ParseAsHexString(kHexString, len, buffer), "ParseAsHexString failed"); - VerifyOrQuit(len == sizeof(kParsedArray), "ParseAsHexString() parsed incorrectly"); - VerifyOrQuit(memcmp(buffer, kParsedArray, len) == 0, "ParseAsHexString() parsed incorrectly"); + SuccessOrQuit(ParseAsHexString(kEvenHexString, len, buffer), "ParseAsHexString() failed"); + VerifyOrQuit(len == sizeof(kEvenParsedArray), "ParseAsHexString() parsed incorrectly"); + VerifyOrQuit(memcmp(buffer, kEvenParsedArray, len) == 0, "ParseAsHexString() parsed incorrectly"); + DumpBuffer(kEvenHexString, buffer, len); - SuccessOrQuit(ParseAsHexString(kHexString + 1, len, buffer), "ParseAsHexString failed"); - VerifyOrQuit(len == sizeof(kParsedArray), "ParseAsHexString() parsed incorrectly"); - VerifyOrQuit(memcmp(buffer + 1, kParsedArray + 1, len - 1) == 0, "ParseAsHexString() parsed incorrectly"); - VerifyOrQuit(buffer[0] == 0xe, "ParseAsHexString() parsed incorrectly"); + SuccessOrQuit(ParseAsHexString(kOddHexString, len, buffer), "ParseAsHexString() failed"); + VerifyOrQuit(len == sizeof(kOddParsedArray), "ParseAsHexString() parsed incorrectly"); + VerifyOrQuit(memcmp(buffer, kOddParsedArray, len) == 0, "ParseAsHexString() parsed incorrectly"); + DumpBuffer(kOddHexString, buffer, len); - SuccessOrQuit(ParseAsHexString(kHexString + 2, len, buffer), "ParseAsHexString failed"); - VerifyOrQuit(len == sizeof(kParsedArray) - 1, "ParseAsHexString() parsed incorrectly"); - VerifyOrQuit(memcmp(buffer, kParsedArray + 1, len) == 0, "ParseAsHexString() parsed incorrectly"); + // Verify `ParseAsHexStringSegement()` - len = sizeof(buffer) - 1; - VerifyOrQuit(ParseAsHexString(kHexString, len, buffer, ot::Utils::CmdLineParser::kDisallowTruncate) != - OT_ERROR_NONE, - "ParseAsHexString passed with bad input"); - len = sizeof(buffer) - 1; - SuccessOrQuit(ParseAsHexString(kHexString, len, buffer, ot::Utils::CmdLineParser::kAllowTruncate), - "ParseAsHexString failed"); - VerifyOrQuit(len == sizeof(buffer) - 1, "ParseAsHexString() parsed incorrectly"); - VerifyOrQuit(memcmp(buffer, kParsedArray, len) == 0, "ParseAsHexString() parsed incorrectly"); + printf("----------------------------------------------------------\n"); + + for (uint8_t testIter = 0; testIter <= 1; testIter++) + { + for (uint8_t segmentLen = 1; segmentLen <= sizeof(buffer); segmentLen++) + { + if (testIter == 0) + { + string = kEvenHexString; + bufPtr = kEvenParsedArray; + } + else + { + string = kOddHexString; + bufPtr = kOddParsedArray; + } + + len = segmentLen; + + printf("\"%s\" segLen:%d -> ", string, segmentLen); + + while (true) + { + otError error = ParseAsHexStringSegment(string, len, buffer); + + printf("%d (\"%s\") ", len, string); + + if (error == OT_ERROR_NONE) + { + VerifyOrQuit(len <= segmentLen, "ParseAsHexStringSegment() parsed incorrectly"); + VerifyOrQuit(memcmp(buffer, bufPtr, len) == 0, "ParseAsHexStringSegment() parsed incorrectly"); + VerifyOrQuit(*string == '\0', + "ParseAsHexStringSegment() failed to update string pointer correctly"); + break; + } + + VerifyOrQuit(error == OT_ERROR_PENDING, "ParseAsHexStringSegment() failed"); + VerifyOrQuit(len == segmentLen, "ParseAsHexStringSegment() parsed incorrectly"); + VerifyOrQuit(memcmp(buffer, bufPtr, len) == 0, "ParseAsHexStringSegment() parsed incorrectly"); + bufPtr += len; + } + + printf("\n"); + } + } } int main(void)