1414
1515#include " google/cloud/spanner/uuid.h"
1616#include " google/cloud/internal/make_status.h"
17+ #include " absl/strings/match.h"
1718#include " absl/strings/str_format.h"
1819#include " absl/strings/strip.h"
19- #include < unordered_map>
20+ #include < cctype>
21+ #include < cstring>
2022
2123namespace google {
2224namespace cloud {
2325namespace spanner {
2426GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
25- namespace {
2627
27- // Helper function to parse a single hexadecimal block of a UUID.
28- // A hexadecimal block is a 16-digit hexadecimal number, which is represented
29- // as 8 bytes.
30- StatusOr<std::uint64_t > ParseHexBlock (absl::string_view& str,
31- absl::string_view original_str) {
32- constexpr int kUuidNumberOfHexDigits = 32 ;
33- constexpr int kMaxUuidBlockLength = 16 ;
34- static auto const * char_to_hex = new std::unordered_map<char , std::uint8_t >(
35- {{' 0' , 0x00 }, {' 1' , 0x01 }, {' 2' , 0x02 }, {' 3' , 0x03 }, {' 4' , 0x04 },
36- {' 5' , 0x05 }, {' 6' , 0x06 }, {' 7' , 0x07 }, {' 8' , 0x08 }, {' 9' , 0x09 },
37- {' a' , 0x0a }, {' b' , 0x0b }, {' c' , 0x0c }, {' d' , 0x0d }, {' e' , 0x0e },
38- {' f' , 0x0f }, {' A' , 0x0a }, {' B' , 0x0b }, {' C' , 0x0c }, {' D' , 0x0d },
39- {' E' , 0x0e }, {' F' , 0x0f }});
40- std::uint64_t block = 0 ;
41- for (int j = 0 ; j < kMaxUuidBlockLength ; ++j) {
42- absl::ConsumePrefix (&str, " -" );
43- if (str.empty ()) {
44- return internal::InvalidArgumentError (
45- absl::StrFormat (" UUID must contain %d hexadecimal digits: %s" ,
46- kUuidNumberOfHexDigits , original_str),
47- GCP_ERROR_INFO ());
48- }
49- auto it = char_to_hex->find (str[0 ]);
50- if (it == char_to_hex->end ()) {
51- if (str[0 ] == ' -' ) {
52- return internal::InvalidArgumentError (
53- absl::StrFormat (" UUID cannot contain consecutive hyphens: %s" ,
54- original_str),
55- GCP_ERROR_INFO ());
56- }
57-
58- return internal::InvalidArgumentError (
59- absl::StrFormat (" UUID contains invalid character (%c): %s" , str[0 ],
60- original_str),
61- GCP_ERROR_INFO ());
62- }
63- block = (block << 4 ) + it->second ;
64- str.remove_prefix (1 );
65- }
66- return block;
67- }
68- } // namespace
69-
70- Uuid::Uuid (absl::uint128 value) : uuid_(value) {}
28+ constexpr char kHexDigits [] = " 0123456789abcdef" ;
7129
7230Uuid::Uuid (std::uint64_t high_bits, std::uint64_t low_bits)
7331 : Uuid(absl::MakeUint128(high_bits, low_bits)) {}
@@ -76,72 +34,59 @@ std::pair<std::uint64_t, std::uint64_t> Uuid::As64BitPair() const {
7634 return std::make_pair (Uint128High64 (uuid_), Uint128Low64 (uuid_));
7735}
7836
79- // TODO(#15043): Refactor to handle all 128 bits at once instead of splitting
80- // into a pair of unsigned 64-bit integers.
8137Uuid::operator std::string () const {
82- constexpr int kUuidStringLen = 36 ;
83- constexpr int kChunkLength [] = {8 , 4 , 4 , 4 , 12 };
84- auto to_hex = [](std::uint64_t v, int start_index, int end_index, char * out) {
85- static constexpr char kHexChar [] = {' 0' , ' 1' , ' 2' , ' 3' , ' 4' , ' 5' , ' 6' , ' 7' ,
86- ' 8' , ' 9' , ' a' , ' b' , ' c' , ' d' , ' e' , ' f' };
87- for (int i = start_index; i >= end_index; --i) {
88- *out++ = kHexChar [(v >> (i * 4 )) & 0xf ];
89- }
90- return start_index - end_index + 1 ;
91- };
92-
93- std::string output;
94- output.resize (kUuidStringLen );
95- char * target = const_cast <char *>(output.data ());
96- char * const last = &((output)[output.size ()]);
97- auto bits = Uint128High64 (uuid_);
98- int start = 16 ;
99- for (auto length : kChunkLength ) {
100- int end = start - length;
101- target += to_hex (bits, start - 1 , end, target);
102- // Only hyphens write to valid addresses.
103- if (target < last) *(target++) = ' -' ;
104- if (end == 0 ) {
105- start = 16 ;
106- bits = Uint128Low64 (uuid_);
38+ constexpr char kTemplate [] = " 00000000-0000-0000-0000-000000000000" ;
39+ char buf[sizeof kTemplate ];
40+ auto uuid = uuid_;
41+ for (auto j = sizeof buf; j-- != 0 ;) {
42+ if (kTemplate [j] != ' 0' ) {
43+ buf[j] = kTemplate [j];
10744 } else {
108- start = end;
45+ buf[j] = kHexDigits [static_cast <int >(uuid & 0xf )];
46+ uuid >>= 4 ;
10947 }
11048 }
111- return output ;
49+ return buf ;
11250}
11351
11452StatusOr<Uuid> MakeUuid (absl::string_view str) {
115- absl::string_view original_str = str;
116- // Check and remove optional braces
117- if (absl::ConsumePrefix (&str, " {" )) {
118- if (!absl::ConsumeSuffix (&str, " }" )) {
53+ absl::uint128 uuid = 0 ;
54+ auto const original_str = str;
55+ if (absl::StartsWith (str, " {" ) && absl::ConsumeSuffix (&str, " }" )) {
56+ str.remove_prefix (1 );
57+ }
58+ if (absl::StartsWithIgnoreCase (str, " 0x" )) {
59+ str.remove_prefix (2 );
60+ }
61+ constexpr int kUuidNumberOfHexDigits = 32 ;
62+ for (int j = 0 ; j != kUuidNumberOfHexDigits ; ++j) {
63+ if (j != 0 ) absl::ConsumePrefix (&str, " -" );
64+ if (str.empty ()) {
11965 return internal::InvalidArgumentError (
120- absl::StrFormat (" UUID missing closing '}': %s" , original_str),
66+ absl::StrFormat (" UUID must contain %d hexadecimal digits: %s" ,
67+ kUuidNumberOfHexDigits , original_str),
12168 GCP_ERROR_INFO ());
12269 }
70+ auto const * dp = std::strchr (
71+ kHexDigits , std::tolower (static_cast <unsigned char >(str.front ())));
72+ if (dp == nullptr ) {
73+ return internal::InvalidArgumentError (
74+ absl::StrFormat (
75+ " UUID contains invalid character '%c' at position %d: %s" ,
76+ str.front (), str.data () - original_str.data (), original_str),
77+ GCP_ERROR_INFO ());
78+ }
79+ uuid <<= 4 ;
80+ uuid += dp - kHexDigits ;
81+ str.remove_prefix (1 );
12382 }
124-
125- // Check for leading hyphen after stripping any surrounding braces.
126- if (absl::StartsWith (str, " -" )) {
127- return internal::InvalidArgumentError (
128- absl::StrFormat (" UUID cannot begin with '-': %s" , original_str),
129- GCP_ERROR_INFO ());
130- }
131-
132- // TODO(#15043): Refactor to parse all the bits at once.
133- auto high_bits = ParseHexBlock (str, original_str);
134- if (!high_bits.ok ()) return std::move (high_bits).status ();
135- auto low_bits = ParseHexBlock (str, original_str);
136- if (!low_bits.ok ()) return std::move (low_bits).status ();
137-
13883 if (!str.empty ()) {
13984 return internal::InvalidArgumentError (
140- absl::StrFormat (" Extra characters found after parsing UUID: %s" , str),
85+ absl::StrFormat (" Extra characters \" %s\" found after parsing UUID: %s" ,
86+ str, original_str),
14187 GCP_ERROR_INFO ());
14288 }
143-
144- return Uuid (absl::MakeUint128 (*high_bits, *low_bits));
89+ return Uuid{uuid};
14590}
14691
14792GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
0 commit comments