1 // Copyright 2018 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "absl/container/internal/hash_function_defaults.h"
16
17 #include <functional>
18 #include <type_traits>
19 #include <utility>
20
21 #include "gtest/gtest.h"
22 #include "absl/random/random.h"
23 #include "absl/strings/cord.h"
24 #include "absl/strings/cord_test_helpers.h"
25 #include "absl/strings/string_view.h"
26
27 #ifdef ABSL_HAVE_STD_STRING_VIEW
28 #include <string_view>
29 #endif
30
31 namespace absl {
32 ABSL_NAMESPACE_BEGIN
33 namespace container_internal {
34 namespace {
35
36 using ::testing::Types;
37
TEST(Eq,Int32)38 TEST(Eq, Int32) {
39 hash_default_eq<int32_t> eq;
40 EXPECT_TRUE(eq(1, 1u));
41 EXPECT_TRUE(eq(1, char{1}));
42 EXPECT_TRUE(eq(1, true));
43 EXPECT_TRUE(eq(1, double{1.1}));
44 EXPECT_FALSE(eq(1, char{2}));
45 EXPECT_FALSE(eq(1, 2u));
46 EXPECT_FALSE(eq(1, false));
47 EXPECT_FALSE(eq(1, 2.));
48 }
49
TEST(Hash,Int32)50 TEST(Hash, Int32) {
51 hash_default_hash<int32_t> hash;
52 auto h = hash(1);
53 EXPECT_EQ(h, hash(1u));
54 EXPECT_EQ(h, hash(char{1}));
55 EXPECT_EQ(h, hash(true));
56 EXPECT_EQ(h, hash(double{1.1}));
57 EXPECT_NE(h, hash(2u));
58 EXPECT_NE(h, hash(char{2}));
59 EXPECT_NE(h, hash(false));
60 EXPECT_NE(h, hash(2.));
61 }
62
63 enum class MyEnum { A, B, C, D };
64
TEST(Eq,Enum)65 TEST(Eq, Enum) {
66 hash_default_eq<MyEnum> eq;
67 EXPECT_TRUE(eq(MyEnum::A, MyEnum::A));
68 EXPECT_FALSE(eq(MyEnum::A, MyEnum::B));
69 }
70
TEST(Hash,Enum)71 TEST(Hash, Enum) {
72 hash_default_hash<MyEnum> hash;
73
74 for (MyEnum e : {MyEnum::A, MyEnum::B, MyEnum::C}) {
75 auto h = hash(e);
76 EXPECT_EQ(h, hash_default_hash<int>{}(static_cast<int>(e)));
77 EXPECT_NE(h, hash(MyEnum::D));
78 }
79 }
80
81 using StringTypes = ::testing::Types<std::string, absl::string_view>;
82
83 template <class T>
84 struct EqString : ::testing::Test {
85 hash_default_eq<T> key_eq;
86 };
87
88 TYPED_TEST_SUITE(EqString, StringTypes);
89
90 template <class T>
91 struct HashString : ::testing::Test {
92 hash_default_hash<T> hasher;
93 };
94
95 TYPED_TEST_SUITE(HashString, StringTypes);
96
TYPED_TEST(EqString,Works)97 TYPED_TEST(EqString, Works) {
98 auto eq = this->key_eq;
99 EXPECT_TRUE(eq("a", "a"));
100 EXPECT_TRUE(eq("a", absl::string_view("a")));
101 EXPECT_TRUE(eq("a", std::string("a")));
102 EXPECT_FALSE(eq("a", "b"));
103 EXPECT_FALSE(eq("a", absl::string_view("b")));
104 EXPECT_FALSE(eq("a", std::string("b")));
105 }
106
TYPED_TEST(HashString,Works)107 TYPED_TEST(HashString, Works) {
108 auto hash = this->hasher;
109 auto h = hash("a");
110 EXPECT_EQ(h, hash(absl::string_view("a")));
111 EXPECT_EQ(h, hash(std::string("a")));
112 EXPECT_NE(h, hash(absl::string_view("b")));
113 EXPECT_NE(h, hash(std::string("b")));
114 }
115
TEST(BasicStringViewTest,WStringEqWorks)116 TEST(BasicStringViewTest, WStringEqWorks) {
117 #ifndef ABSL_HAVE_STD_STRING_VIEW
118 GTEST_SKIP();
119 #else
120 hash_default_eq<std::wstring> eq;
121 EXPECT_TRUE(eq(L"a", L"a"));
122 EXPECT_TRUE(eq(L"a", std::wstring_view(L"a")));
123 EXPECT_TRUE(eq(L"a", std::wstring(L"a")));
124 EXPECT_FALSE(eq(L"a", L"b"));
125 EXPECT_FALSE(eq(L"a", std::wstring_view(L"b")));
126 EXPECT_FALSE(eq(L"a", std::wstring(L"b")));
127 #endif
128 }
129
TEST(BasicStringViewTest,WStringViewEqWorks)130 TEST(BasicStringViewTest, WStringViewEqWorks) {
131 #ifndef ABSL_HAVE_STD_STRING_VIEW
132 GTEST_SKIP();
133 #else
134 hash_default_eq<std::wstring_view> eq;
135 EXPECT_TRUE(eq(L"a", L"a"));
136 EXPECT_TRUE(eq(L"a", std::wstring_view(L"a")));
137 EXPECT_TRUE(eq(L"a", std::wstring(L"a")));
138 EXPECT_FALSE(eq(L"a", L"b"));
139 EXPECT_FALSE(eq(L"a", std::wstring_view(L"b")));
140 EXPECT_FALSE(eq(L"a", std::wstring(L"b")));
141 #endif
142 }
143
TEST(BasicStringViewTest,U16StringEqWorks)144 TEST(BasicStringViewTest, U16StringEqWorks) {
145 #ifndef ABSL_HAVE_STD_STRING_VIEW
146 GTEST_SKIP();
147 #else
148 hash_default_eq<std::u16string> eq;
149 EXPECT_TRUE(eq(u"a", u"a"));
150 EXPECT_TRUE(eq(u"a", std::u16string_view(u"a")));
151 EXPECT_TRUE(eq(u"a", std::u16string(u"a")));
152 EXPECT_FALSE(eq(u"a", u"b"));
153 EXPECT_FALSE(eq(u"a", std::u16string_view(u"b")));
154 EXPECT_FALSE(eq(u"a", std::u16string(u"b")));
155 #endif
156 }
157
TEST(BasicStringViewTest,U16StringViewEqWorks)158 TEST(BasicStringViewTest, U16StringViewEqWorks) {
159 #ifndef ABSL_HAVE_STD_STRING_VIEW
160 GTEST_SKIP();
161 #else
162 hash_default_eq<std::u16string_view> eq;
163 EXPECT_TRUE(eq(u"a", u"a"));
164 EXPECT_TRUE(eq(u"a", std::u16string_view(u"a")));
165 EXPECT_TRUE(eq(u"a", std::u16string(u"a")));
166 EXPECT_FALSE(eq(u"a", u"b"));
167 EXPECT_FALSE(eq(u"a", std::u16string_view(u"b")));
168 EXPECT_FALSE(eq(u"a", std::u16string(u"b")));
169 #endif
170 }
171
TEST(BasicStringViewTest,U32StringEqWorks)172 TEST(BasicStringViewTest, U32StringEqWorks) {
173 #ifndef ABSL_HAVE_STD_STRING_VIEW
174 GTEST_SKIP();
175 #else
176 hash_default_eq<std::u32string> eq;
177 EXPECT_TRUE(eq(U"a", U"a"));
178 EXPECT_TRUE(eq(U"a", std::u32string_view(U"a")));
179 EXPECT_TRUE(eq(U"a", std::u32string(U"a")));
180 EXPECT_FALSE(eq(U"a", U"b"));
181 EXPECT_FALSE(eq(U"a", std::u32string_view(U"b")));
182 EXPECT_FALSE(eq(U"a", std::u32string(U"b")));
183 #endif
184 }
185
TEST(BasicStringViewTest,U32StringViewEqWorks)186 TEST(BasicStringViewTest, U32StringViewEqWorks) {
187 #ifndef ABSL_HAVE_STD_STRING_VIEW
188 GTEST_SKIP();
189 #else
190 hash_default_eq<std::u32string_view> eq;
191 EXPECT_TRUE(eq(U"a", U"a"));
192 EXPECT_TRUE(eq(U"a", std::u32string_view(U"a")));
193 EXPECT_TRUE(eq(U"a", std::u32string(U"a")));
194 EXPECT_FALSE(eq(U"a", U"b"));
195 EXPECT_FALSE(eq(U"a", std::u32string_view(U"b")));
196 EXPECT_FALSE(eq(U"a", std::u32string(U"b")));
197 #endif
198 }
199
TEST(BasicStringViewTest,WStringHashWorks)200 TEST(BasicStringViewTest, WStringHashWorks) {
201 #ifndef ABSL_HAVE_STD_STRING_VIEW
202 GTEST_SKIP();
203 #else
204 hash_default_hash<std::wstring> hash;
205 auto h = hash(L"a");
206 EXPECT_EQ(h, hash(std::wstring_view(L"a")));
207 EXPECT_EQ(h, hash(std::wstring(L"a")));
208 EXPECT_NE(h, hash(std::wstring_view(L"b")));
209 EXPECT_NE(h, hash(std::wstring(L"b")));
210 #endif
211 }
212
TEST(BasicStringViewTest,WStringViewHashWorks)213 TEST(BasicStringViewTest, WStringViewHashWorks) {
214 #ifndef ABSL_HAVE_STD_STRING_VIEW
215 GTEST_SKIP();
216 #else
217 hash_default_hash<std::wstring_view> hash;
218 auto h = hash(L"a");
219 EXPECT_EQ(h, hash(std::wstring_view(L"a")));
220 EXPECT_EQ(h, hash(std::wstring(L"a")));
221 EXPECT_NE(h, hash(std::wstring_view(L"b")));
222 EXPECT_NE(h, hash(std::wstring(L"b")));
223 #endif
224 }
225
TEST(BasicStringViewTest,U16StringHashWorks)226 TEST(BasicStringViewTest, U16StringHashWorks) {
227 #ifndef ABSL_HAVE_STD_STRING_VIEW
228 GTEST_SKIP();
229 #else
230 hash_default_hash<std::u16string> hash;
231 auto h = hash(u"a");
232 EXPECT_EQ(h, hash(std::u16string_view(u"a")));
233 EXPECT_EQ(h, hash(std::u16string(u"a")));
234 EXPECT_NE(h, hash(std::u16string_view(u"b")));
235 EXPECT_NE(h, hash(std::u16string(u"b")));
236 #endif
237 }
238
TEST(BasicStringViewTest,U16StringViewHashWorks)239 TEST(BasicStringViewTest, U16StringViewHashWorks) {
240 #ifndef ABSL_HAVE_STD_STRING_VIEW
241 GTEST_SKIP();
242 #else
243 hash_default_hash<std::u16string_view> hash;
244 auto h = hash(u"a");
245 EXPECT_EQ(h, hash(std::u16string_view(u"a")));
246 EXPECT_EQ(h, hash(std::u16string(u"a")));
247 EXPECT_NE(h, hash(std::u16string_view(u"b")));
248 EXPECT_NE(h, hash(std::u16string(u"b")));
249 #endif
250 }
251
TEST(BasicStringViewTest,U32StringHashWorks)252 TEST(BasicStringViewTest, U32StringHashWorks) {
253 #ifndef ABSL_HAVE_STD_STRING_VIEW
254 GTEST_SKIP();
255 #else
256 hash_default_hash<std::u32string> hash;
257 auto h = hash(U"a");
258 EXPECT_EQ(h, hash(std::u32string_view(U"a")));
259 EXPECT_EQ(h, hash(std::u32string(U"a")));
260 EXPECT_NE(h, hash(std::u32string_view(U"b")));
261 EXPECT_NE(h, hash(std::u32string(U"b")));
262 #endif
263 }
264
TEST(BasicStringViewTest,U32StringViewHashWorks)265 TEST(BasicStringViewTest, U32StringViewHashWorks) {
266 #ifndef ABSL_HAVE_STD_STRING_VIEW
267 GTEST_SKIP();
268 #else
269 hash_default_hash<std::u32string_view> hash;
270 auto h = hash(U"a");
271 EXPECT_EQ(h, hash(std::u32string_view(U"a")));
272 EXPECT_EQ(h, hash(std::u32string(U"a")));
273 EXPECT_NE(h, hash(std::u32string_view(U"b")));
274 EXPECT_NE(h, hash(std::u32string(U"b")));
275 #endif
276 }
277
278 struct NoDeleter {
279 template <class T>
operator ()absl::container_internal::__anon6dfd19760111::NoDeleter280 void operator()(const T* ptr) const {}
281 };
282
283 using PointerTypes =
284 ::testing::Types<const int*, int*, std::unique_ptr<const int>,
285 std::unique_ptr<const int, NoDeleter>,
286 std::unique_ptr<int>, std::unique_ptr<int, NoDeleter>,
287 std::shared_ptr<const int>, std::shared_ptr<int>>;
288
289 template <class T>
290 struct EqPointer : ::testing::Test {
291 hash_default_eq<T> key_eq;
292 };
293
294 TYPED_TEST_SUITE(EqPointer, PointerTypes);
295
296 template <class T>
297 struct HashPointer : ::testing::Test {
298 hash_default_hash<T> hasher;
299 };
300
301 TYPED_TEST_SUITE(HashPointer, PointerTypes);
302
TYPED_TEST(EqPointer,Works)303 TYPED_TEST(EqPointer, Works) {
304 int dummy;
305 auto eq = this->key_eq;
306 auto sptr = std::make_shared<int>();
307 std::shared_ptr<const int> csptr = sptr;
308 int* ptr = sptr.get();
309 const int* cptr = ptr;
310 std::unique_ptr<int, NoDeleter> uptr(ptr);
311 std::unique_ptr<const int, NoDeleter> cuptr(ptr);
312
313 EXPECT_TRUE(eq(ptr, cptr));
314 EXPECT_TRUE(eq(ptr, sptr));
315 EXPECT_TRUE(eq(ptr, uptr));
316 EXPECT_TRUE(eq(ptr, csptr));
317 EXPECT_TRUE(eq(ptr, cuptr));
318 EXPECT_FALSE(eq(&dummy, cptr));
319 EXPECT_FALSE(eq(&dummy, sptr));
320 EXPECT_FALSE(eq(&dummy, uptr));
321 EXPECT_FALSE(eq(&dummy, csptr));
322 EXPECT_FALSE(eq(&dummy, cuptr));
323 }
324
TEST(Hash,DerivedAndBase)325 TEST(Hash, DerivedAndBase) {
326 struct Base {};
327 struct Derived : Base {};
328
329 hash_default_hash<Base*> hasher;
330
331 Base base;
332 Derived derived;
333 EXPECT_NE(hasher(&base), hasher(&derived));
334 EXPECT_EQ(hasher(static_cast<Base*>(&derived)), hasher(&derived));
335
336 auto dp = std::make_shared<Derived>();
337 EXPECT_EQ(hasher(static_cast<Base*>(dp.get())), hasher(dp));
338 }
339
TEST(Hash,FunctionPointer)340 TEST(Hash, FunctionPointer) {
341 using Func = int (*)();
342 hash_default_hash<Func> hasher;
343 hash_default_eq<Func> eq;
344
345 Func p1 = [] { return 1; }, p2 = [] { return 2; };
346 EXPECT_EQ(hasher(p1), hasher(p1));
347 EXPECT_TRUE(eq(p1, p1));
348
349 EXPECT_NE(hasher(p1), hasher(p2));
350 EXPECT_FALSE(eq(p1, p2));
351 }
352
TYPED_TEST(HashPointer,Works)353 TYPED_TEST(HashPointer, Works) {
354 int dummy;
355 auto hash = this->hasher;
356 auto sptr = std::make_shared<int>();
357 std::shared_ptr<const int> csptr = sptr;
358 int* ptr = sptr.get();
359 const int* cptr = ptr;
360 std::unique_ptr<int, NoDeleter> uptr(ptr);
361 std::unique_ptr<const int, NoDeleter> cuptr(ptr);
362
363 EXPECT_EQ(hash(ptr), hash(cptr));
364 EXPECT_EQ(hash(ptr), hash(sptr));
365 EXPECT_EQ(hash(ptr), hash(uptr));
366 EXPECT_EQ(hash(ptr), hash(csptr));
367 EXPECT_EQ(hash(ptr), hash(cuptr));
368 EXPECT_NE(hash(&dummy), hash(cptr));
369 EXPECT_NE(hash(&dummy), hash(sptr));
370 EXPECT_NE(hash(&dummy), hash(uptr));
371 EXPECT_NE(hash(&dummy), hash(csptr));
372 EXPECT_NE(hash(&dummy), hash(cuptr));
373 }
374
TEST(EqCord,Works)375 TEST(EqCord, Works) {
376 hash_default_eq<absl::Cord> eq;
377 const absl::string_view a_string_view = "a";
378 const absl::Cord a_cord(a_string_view);
379 const absl::string_view b_string_view = "b";
380 const absl::Cord b_cord(b_string_view);
381
382 EXPECT_TRUE(eq(a_cord, a_cord));
383 EXPECT_TRUE(eq(a_cord, a_string_view));
384 EXPECT_TRUE(eq(a_string_view, a_cord));
385 EXPECT_FALSE(eq(a_cord, b_cord));
386 EXPECT_FALSE(eq(a_cord, b_string_view));
387 EXPECT_FALSE(eq(b_string_view, a_cord));
388 }
389
TEST(HashCord,Works)390 TEST(HashCord, Works) {
391 hash_default_hash<absl::Cord> hash;
392 const absl::string_view a_string_view = "a";
393 const absl::Cord a_cord(a_string_view);
394 const absl::string_view b_string_view = "b";
395 const absl::Cord b_cord(b_string_view);
396
397 EXPECT_EQ(hash(a_cord), hash(a_cord));
398 EXPECT_EQ(hash(b_cord), hash(b_cord));
399 EXPECT_EQ(hash(a_string_view), hash(a_cord));
400 EXPECT_EQ(hash(b_string_view), hash(b_cord));
401 EXPECT_EQ(hash(absl::Cord("")), hash(""));
402 EXPECT_EQ(hash(absl::Cord()), hash(absl::string_view()));
403
404 EXPECT_NE(hash(a_cord), hash(b_cord));
405 EXPECT_NE(hash(a_cord), hash(b_string_view));
406 EXPECT_NE(hash(a_string_view), hash(b_cord));
407 EXPECT_NE(hash(a_string_view), hash(b_string_view));
408 }
409
NoOpReleaser(absl::string_view data,void * arg)410 void NoOpReleaser(absl::string_view data, void* arg) {}
411
TEST(HashCord,FragmentedCordWorks)412 TEST(HashCord, FragmentedCordWorks) {
413 hash_default_hash<absl::Cord> hash;
414 absl::Cord c = absl::MakeFragmentedCord({"a", "b", "c"});
415 EXPECT_FALSE(c.TryFlat().has_value());
416 EXPECT_EQ(hash(c), hash("abc"));
417 }
418
TEST(HashCord,FragmentedLongCordWorks)419 TEST(HashCord, FragmentedLongCordWorks) {
420 hash_default_hash<absl::Cord> hash;
421 // Crete some large strings which do not fit on the stack.
422 std::string a(65536, 'a');
423 std::string b(65536, 'b');
424 absl::Cord c = absl::MakeFragmentedCord({a, b});
425 EXPECT_FALSE(c.TryFlat().has_value());
426 EXPECT_EQ(hash(c), hash(a + b));
427 }
428
TEST(HashCord,RandomCord)429 TEST(HashCord, RandomCord) {
430 hash_default_hash<absl::Cord> hash;
431 auto bitgen = absl::BitGen();
432 for (int i = 0; i < 1000; ++i) {
433 const int number_of_segments = absl::Uniform(bitgen, 0, 10);
434 std::vector<std::string> pieces;
435 for (size_t s = 0; s < number_of_segments; ++s) {
436 std::string str;
437 str.resize(absl::Uniform(bitgen, 0, 4096));
438 // MSVC needed the explicit return type in the lambda.
439 std::generate(str.begin(), str.end(), [&]() -> char {
440 return static_cast<char>(absl::Uniform<unsigned char>(bitgen));
441 });
442 pieces.push_back(str);
443 }
444 absl::Cord c = absl::MakeFragmentedCord(pieces);
445 EXPECT_EQ(hash(c), hash(std::string(c)));
446 }
447 }
448
449 // Cartesian product of (std::string, absl::string_view)
450 // with (std::string, absl::string_view, const char*, absl::Cord).
451 using StringTypesCartesianProduct = Types<
452 // clang-format off
453 std::pair<absl::Cord, std::string>,
454 std::pair<absl::Cord, absl::string_view>,
455 std::pair<absl::Cord, absl::Cord>,
456 std::pair<absl::Cord, const char*>,
457
458 std::pair<std::string, absl::Cord>,
459 std::pair<absl::string_view, absl::Cord>,
460
461 std::pair<absl::string_view, std::string>,
462 std::pair<absl::string_view, absl::string_view>,
463 std::pair<absl::string_view, const char*>>;
464 // clang-format on
465
466 constexpr char kFirstString[] = "abc123";
467 constexpr char kSecondString[] = "ijk456";
468
469 template <typename T>
470 struct StringLikeTest : public ::testing::Test {
471 typename T::first_type a1{kFirstString};
472 typename T::second_type b1{kFirstString};
473 typename T::first_type a2{kSecondString};
474 typename T::second_type b2{kSecondString};
475 hash_default_eq<typename T::first_type> eq;
476 hash_default_hash<typename T::first_type> hash;
477 };
478
479 TYPED_TEST_SUITE_P(StringLikeTest);
480
TYPED_TEST_P(StringLikeTest,Eq)481 TYPED_TEST_P(StringLikeTest, Eq) {
482 EXPECT_TRUE(this->eq(this->a1, this->b1));
483 EXPECT_TRUE(this->eq(this->b1, this->a1));
484 }
485
TYPED_TEST_P(StringLikeTest,NotEq)486 TYPED_TEST_P(StringLikeTest, NotEq) {
487 EXPECT_FALSE(this->eq(this->a1, this->b2));
488 EXPECT_FALSE(this->eq(this->b2, this->a1));
489 }
490
TYPED_TEST_P(StringLikeTest,HashEq)491 TYPED_TEST_P(StringLikeTest, HashEq) {
492 EXPECT_EQ(this->hash(this->a1), this->hash(this->b1));
493 EXPECT_EQ(this->hash(this->a2), this->hash(this->b2));
494 // It would be a poor hash function which collides on these strings.
495 EXPECT_NE(this->hash(this->a1), this->hash(this->b2));
496 }
497
498 TYPED_TEST_SUITE(StringLikeTest, StringTypesCartesianProduct);
499
500 } // namespace
501 } // namespace container_internal
502 ABSL_NAMESPACE_END
503 } // namespace absl
504
505 enum Hash : size_t {
506 kStd = 0x1, // std::hash
507 #ifdef _MSC_VER
508 kExtension = kStd, // In MSVC, std::hash == ::hash
509 #else // _MSC_VER
510 kExtension = 0x2, // ::hash (GCC extension)
511 #endif // _MSC_VER
512 };
513
514 // H is a bitmask of Hash enumerations.
515 // Hashable<H> is hashable via all means specified in H.
516 template <int H>
517 struct Hashable {
HashableByHashable518 static constexpr bool HashableBy(Hash h) { return h & H; }
519 };
520
521 namespace std {
522 template <int H>
523 struct hash<Hashable<H>> {
524 template <class E = Hashable<H>,
525 class = typename std::enable_if<E::HashableBy(kStd)>::type>
operator ()std::hash526 size_t operator()(E) const {
527 return kStd;
528 }
529 };
530 } // namespace std
531
532 namespace absl {
533 ABSL_NAMESPACE_BEGIN
534 namespace container_internal {
535 namespace {
536
537 template <class T>
Hash(const T & v)538 size_t Hash(const T& v) {
539 return hash_default_hash<T>()(v);
540 }
541
TEST(Delegate,HashDispatch)542 TEST(Delegate, HashDispatch) {
543 EXPECT_EQ(Hash(kStd), Hash(Hashable<kStd>()));
544 }
545
546 } // namespace
547 } // namespace container_internal
548 ABSL_NAMESPACE_END
549 } // namespace absl
550