diff options
author | Derek Mauro <dmauro@google.com> | 2024-02-05 18:31:36 -0800 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2024-02-05 18:32:37 -0800 |
commit | 0e289dc594da4b30eb03cb7cb2aa097f5d5f6eb5 (patch) | |
tree | cbdd9e5f0b6a31d006a960bf1799afd325dfd73c /absl/strings | |
parent | ddcf8be90575d494e40fcd4e0c408f0237efe0da (diff) | |
download | abseil-0e289dc594da4b30eb03cb7cb2aa097f5d5f6eb5.tar.gz abseil-0e289dc594da4b30eb03cb7cb2aa097f5d5f6eb5.tar.bz2 abseil-0e289dc594da4b30eb03cb7cb2aa097f5d5f6eb5.zip |
Add a version of absl::HexStringToBytes() that returns a bool
to validate that the input was actually valid hexadecimal data.
Mark the version of absl::HexStringToBytes() that does not validate
the input as deprecated.
Fixes #141
PiperOrigin-RevId: 604495678
Change-Id: Iac3020c33c9dbc6d8e31a43b746783fb345edaa7
Diffstat (limited to 'absl/strings')
-rw-r--r-- | absl/strings/escaping.cc | 42 | ||||
-rw-r--r-- | absl/strings/escaping.h | 14 | ||||
-rw-r--r-- | absl/strings/escaping_test.cc | 29 |
3 files changed, 84 insertions, 1 deletions
diff --git a/absl/strings/escaping.cc b/absl/strings/escaping.cc index 5de12afa..bb58f99b 100644 --- a/absl/strings/escaping.cc +++ b/absl/strings/escaping.cc @@ -837,6 +837,24 @@ constexpr char kHexValueLenient[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; +constexpr signed char kHexValueStrict[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // '0'..'9' + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 'A'..'F' + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 'a'..'f' + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; /* clang-format on */ // This is a templated function so that T can be either a char* @@ -931,6 +949,30 @@ std::string WebSafeBase64Escape(absl::string_view src) { return dest; } +bool HexStringToBytes(absl::string_view hex, + absl::Nonnull<std::string*> bytes) { + size_t num_bytes = hex.size() / 2; + bytes->clear(); + if (hex.size() != num_bytes * 2) { + return false; + } + + absl::strings_internal::STLStringResizeUninitialized(bytes, num_bytes); + auto hex_p = hex.cbegin(); + for (std::string::iterator bin_p = bytes->begin(); + bin_p != bytes->end(); ++bin_p) { + int h1 = absl::kHexValueStrict[static_cast<size_t>(*hex_p++)]; + int h2 = absl::kHexValueStrict[static_cast<size_t>(*hex_p++)]; + if (h1 == -1 || h2 == -1) { + bytes->resize(static_cast<size_t>(bin_p - bytes->begin())); + return false; + } + *bin_p = static_cast<char>((h1 << 4) + h2); + } + + return true; +} + std::string HexStringToBytes(absl::string_view from) { std::string result; const auto num = from.size() / 2; diff --git a/absl/strings/escaping.h b/absl/strings/escaping.h index 8f980178..3f34fbfc 100644 --- a/absl/strings/escaping.h +++ b/absl/strings/escaping.h @@ -27,6 +27,7 @@ #include <string> #include <vector> +#include "absl/base/attributes.h" #include "absl/base/macros.h" #include "absl/base/nullability.h" #include "absl/strings/ascii.h" @@ -158,8 +159,19 @@ bool WebSafeBase64Unescape(absl::string_view src, // HexStringToBytes() // +// Converts the hexadecimal encoded data in `hex` into raw bytes in the `bytes` +// output string. If `hex` does not consist of valid hexadecimal data, this +// function returns false and leaves `bytes` in an unspecified state. Returns +// true on success. +ABSL_MUST_USE_RESULT bool HexStringToBytes(absl::string_view hex, + absl::Nonnull<std::string*> bytes); + +// HexStringToBytes() +// // Converts an ASCII hex string into bytes, returning binary data of length -// `from.size()/2`. +// `from.size()/2`. The input must be valid hexadecimal data, otherwise the +// return value is unspecified. +ABSL_DEPRECATED("Use the HexStringToBytes() that returns a bool") std::string HexStringToBytes(absl::string_view from); // BytesToHexString() diff --git a/absl/strings/escaping_test.cc b/absl/strings/escaping_test.cc index ca1ee45c..ae3466a7 100644 --- a/absl/strings/escaping_test.cc +++ b/absl/strings/escaping_test.cc @@ -689,6 +689,35 @@ TEST(Base64, DISABLED_HugeData) { EXPECT_EQ(huge, unescaped); } +TEST(Escaping, HexStringToBytesBackToHex) { + std::string bytes, hex; + + constexpr absl::string_view kTestHexLower = "1c2f0032f40123456789abcdef"; + constexpr absl::string_view kTestHexUpper = "1C2F0032F40123456789ABCDEF"; + constexpr absl::string_view kTestBytes = absl::string_view( + "\x1c\x2f\x00\x32\xf4\x01\x23\x45\x67\x89\xab\xcd\xef", 13); + + EXPECT_TRUE(absl::HexStringToBytes(kTestHexLower, &bytes)); + EXPECT_EQ(bytes, kTestBytes); + + EXPECT_TRUE(absl::HexStringToBytes(kTestHexUpper, &bytes)); + EXPECT_EQ(bytes, kTestBytes); + + hex = absl::BytesToHexString(kTestBytes); + EXPECT_EQ(hex, kTestHexLower); + + // Length not a multiple of two. + EXPECT_FALSE(absl::HexStringToBytes("1c2f003", &bytes)); + + // Not hex. + EXPECT_FALSE(absl::HexStringToBytes("1c2f00ft", &bytes)); + + // Empty input. + bytes = "abc"; + EXPECT_TRUE(absl::HexStringToBytes("", &bytes)); + EXPECT_EQ("", bytes); // Results in empty output. +} + TEST(HexAndBack, HexStringToBytes_and_BytesToHexString) { std::string hex_mixed = "0123456789abcdefABCDEF"; std::string bytes_expected = "\x01\x23\x45\x67\x89\xab\xcd\xef\xAB\xCD\xEF"; |