diff options
-rw-r--r-- | base/string_util.cc | 38 | ||||
-rw-r--r-- | base/string_util.h | 7 | ||||
-rw-r--r-- | base/string_util_unittest.cc | 52 |
3 files changed, 97 insertions, 0 deletions
diff --git a/base/string_util.cc b/base/string_util.cc index 5a06b64..036e61d 100644 --- a/base/string_util.cc +++ b/base/string_util.cc @@ -1409,6 +1409,44 @@ bool HexStringToInt(const std::wstring& input, int* output) { input, reinterpret_cast<long*>(output)); } +template<class CHAR> +bool HexDigitToIntT(const CHAR digit, uint8* val) { + if (digit >= '0' && digit <= '9') + *val = digit - '0'; + else if (digit >= 'a' && digit <= 'f') + *val = 10 + digit - 'a'; + else if (digit >= 'A' && digit <= 'F') + *val = 10 + digit - 'A'; + else + return false; + return true; +} + +template<typename STR> +bool HexStringToBytesT(const STR& input, std::vector<uint8>* output) { + DCHECK(output->size() == 0); + int count = input.size(); + if (count == 0 || (count % 2) != 0) + return false; + for (int i = 0; i < count / 2; ++i) { + uint8 msb = 0; // most significant 4 bits + uint8 lsb = 0; // least significant 4 bits + if (!HexDigitToIntT(input[i * 2], &msb) || + !HexDigitToIntT(input[i * 2 + 1], &lsb)) + return false; + output->push_back((msb << 4) | lsb); + } + return true; +} + +bool HexStringToBytes(const std::string& input, std::vector<uint8>* output) { + return HexStringToBytesT(input, output); +} + +bool HexStringToBytes(const std::wstring& input, std::vector<uint8>* output) { + return HexStringToBytesT(input, output); +} + int StringToInt(const std::string& value) { int result; StringToInt(value, &result); diff --git a/base/string_util.h b/base/string_util.h index 68142cb..28c5d35 100644 --- a/base/string_util.h +++ b/base/string_util.h @@ -377,6 +377,13 @@ bool StringToInt64(const std::wstring& input, int64* output); bool HexStringToInt(const std::string& input, int* output); bool HexStringToInt(const std::wstring& input, int* output); +// Similar to the previous functions, except that output is a vector of bytes. +// |*output| will contain as many bytes as were successfully parsed prior to the +// error. There is no overflow, but input.size() must be evenly divisible by 2. +// Leading 0x or +/- are not allowed. +bool HexStringToBytes(const std::string& input, std::vector<uint8>* output); +bool HexStringToBytes(const std::wstring& input, std::vector<uint8>* output); + // For floating-point conversions, only conversions of input strings in decimal // form are defined to work. Behavior with strings representing floating-point // numbers in hexadecimal, and strings representing non-fininte values (such as diff --git a/base/string_util_unittest.cc b/base/string_util_unittest.cc index a3ca7ee..7676f81 100644 --- a/base/string_util_unittest.cc +++ b/base/string_util_unittest.cc @@ -945,6 +945,58 @@ TEST(StringUtilTest, HexStringToInt) { EXPECT_EQ(0xc0ffee, output); } +TEST(StringUtilTest, HexStringToBytes) { + static const struct { + const std::string input; + const char* output; + size_t output_len; + bool success; + } cases[] = { + {"0", "", 0, false}, // odd number of characters fails + {"00", "\0", 1, true}, + {"42", "\x42", 1, true}, + {"-42", "", 0, false}, // any non-hex value fails + {"+42", "", 0, false}, + {"7fffffff", "\x7f\xff\xff\xff", 4, true}, + {"80000000", "\x80\0\0\0", 4, true}, + {"deadbeef", "\xde\xad\xbe\xef", 4, true}, + {"DeadBeef", "\xde\xad\xbe\xef", 4, true}, + {"0x42", "", 0, false}, // leading 0x fails (x is not hex) + {"0f", "\xf", 1, true}, + {"45 ", "\x45", 1, false}, + {"efgh", "\xef", 1, false}, + {"", "", 0, false}, + {"0123456789ABCDEF", "\x01\x23\x45\x67\x89\xAB\xCD\xEF", 8, true}, + {"0123456789ABCDEF012345", + "\x01\x23\x45\x67\x89\xAB\xCD\xEF\x01\x23\x45", 11, true}, + }; + + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + std::vector<uint8> output; + std::vector<uint8> compare; + EXPECT_EQ(cases[i].success, HexStringToBytes(cases[i].input, &output)) << + i << ": " << cases[i].input; + for (size_t j = 0; j < cases[i].output_len; ++j) + compare.push_back(static_cast<uint8>(cases[i].output[j])); + ASSERT_EQ(output.size(), compare.size()) << i << ": " << cases[i].input; + EXPECT_TRUE(std::equal(output.begin(), output.end(), compare.begin())) << + i << ": " << cases[i].input; + + output.clear(); + compare.clear(); + + std::wstring wide_input = ASCIIToWide(cases[i].input); + EXPECT_EQ(cases[i].success, HexStringToBytes(wide_input, &output)) << + i << ": " << cases[i].input; + for (size_t j = 0; j < cases[i].output_len; ++j) + compare.push_back(static_cast<uint8>(cases[i].output[j])); + ASSERT_EQ(output.size(), compare.size()) << i << ": " << cases[i].input; + EXPECT_TRUE(std::equal(output.begin(), output.end(), compare.begin())) << + i << ": " << cases[i].input; + } +} + TEST(StringUtilTest, StringToDouble) { static const struct { std::string input; |