aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDerek Mauro <dmauro@google.com>2024-02-05 18:31:36 -0800
committerCopybara-Service <copybara-worker@google.com>2024-02-05 18:32:37 -0800
commit0e289dc594da4b30eb03cb7cb2aa097f5d5f6eb5 (patch)
treecbdd9e5f0b6a31d006a960bf1799afd325dfd73c
parentddcf8be90575d494e40fcd4e0c408f0237efe0da (diff)
downloadabseil-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
-rw-r--r--absl/strings/escaping.cc42
-rw-r--r--absl/strings/escaping.h14
-rw-r--r--absl/strings/escaping_test.cc29
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";