-pull/366/head 2018120044b0fafc62
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -926bfeb9ff
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -13327debeb
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -3088e76c59
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -f6ae816808
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -a06c4a1d90
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -7b46e1d31a
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -070f6e47b3
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -7990fd459e
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -f95179062e
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -cc8dcd307b
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -a705aa78dc
Merge pull request #194 from Mizux/windows by Xiaoyi Zhang <zhangxy988@gmail.com> -a4c3ffff11
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -0117457865
Merge pull request #201 from ccawley2011/fix-byteswap by Matt Calabrese <38107210+mattcalabrese-google@users.noreply.github.com> -f86f941385
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -94c298e2a0
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -0884a6a04e
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -c16d5557cd
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -45221ccc4e
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -2019e17a52
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -5b70a8910b
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -a00bdd176d
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -f340f773ed
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -445998d7ac
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -e821380d69
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -f21d187b80
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -5441bbe1db
Fix code snippet in comment (#174) by Loo Rong Jie <loorongjie@gmail.com> -5aae0cffae
Fix CMake build (#173) by Stephan Dollberg <stephan.dollberg@gmail.com> -48cd2c3f35
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -e291c279e4
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -e01d95528e
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -8ff1374008
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -02451914b9
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -921fd5cf02
Merge pull request #166 from rongjiecomputer/cmake-test by Gennadiy Civil <gennadiycivil@users.noreply.github.com> -fb462224c0
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -c075ad3216
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -0f4bc96675
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -6c7e5ffc43
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -d6df769173
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -28080f5f05
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -9c987f429b
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -5e7d459eec
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -bed5bd6e18
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -fefc83638f
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -d8cfe9f2a7
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -ad5c960b2e
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -86f0fe93ad
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -f0f15c2778
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -29ff6d4860
Removed "warning treated as error" flag from MSVC (#153) by vocaviking <vocaviking@users.noreply.github.com> -083d04dd4a
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -bea85b5273
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -8f96be6ca6
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -92e07e5590
Merge pull request #152 from clnperez/fix-multi-defines-p... by Derek Mauro <761129+derekmauro@users.noreply.github.com> -2125e6444a
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -9acad869d2
Merge pull request #150 from OlafvdSpek/patch-2 by Jonathan Cohen <cohenjon@google.com> -c2e00d3419
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -9e060686d1
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -7aa411ceaf
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -2c5af55ed3
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -44aa275286
Merge pull request #143 from rongjiecomputer/kernel by Xiaoyi Zhang <zhangxy988@gmail.com> -42f22a2840
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -b973bc53ef
Merge pull request #139 from siepkes/smartos-support by ahedberg <ahedberg@google.com> -e0def7473e
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -f826f1d489
Merge pull request #138 from edbaunton/remove-deprecated-... by ahedberg <ahedberg@google.com> -7b50a4a94b
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -a5030ca512
Merge pull request #144 from rongjiecomputer/winsock2 by Xiaoyi Zhang <zhangxy988@gmail.com> -02687955b7
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -8f612ebb15
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -134496a31d
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -ba8d6cf077
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -be1e84b988
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -16ac2ec2e3
Merge pull request #134 from rongjiecomputer/cmake by Alex Strelnikov <strel@google.com> -7efd8dc0f1
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -87a4c07856
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> -4491d606df
Export of internal Abseil changes. by Abseil Team <absl-team@google.com> GitOrigin-RevId:44b0fafc62
Change-Id: I2c427b5b41b2d34101922048b00f3d9dafcb498d
parent
6c7de165d1
commit
fcb104594b
963 changed files with 35672 additions and 4925 deletions
@ -1,2 +1,12 @@ |
||||
# Ignore all bazel-* symlinks. |
||||
/bazel-* |
||||
# Ignore Bazel verbose explanations |
||||
--verbose_explanations |
||||
# Ignore CMake usual build directory |
||||
build |
||||
# Ignore Vim files |
||||
*.swp |
||||
# Ignore QtCreator Project file |
||||
CMakeLists.txt.user |
||||
# Ignore VS Code files |
||||
.vscode/* |
||||
|
@ -0,0 +1,145 @@ |
||||
# Abseil-specific compiler flags. See absl/copts.bzl for description. |
||||
# DO NOT CHANGE THIS FILE WITHOUT THE CORRESPONDING CHANGE TO absl/copts.bzl |
||||
|
||||
list(APPEND GCC_FLAGS |
||||
-Wall |
||||
-Wextra |
||||
-Wcast-qual |
||||
-Wconversion-null |
||||
-Wmissing-declarations |
||||
-Woverlength-strings |
||||
-Wpointer-arith |
||||
-Wunused-local-typedefs |
||||
-Wunused-result |
||||
-Wvarargs |
||||
-Wwrite-strings |
||||
-Wno-sign-compare |
||||
) |
||||
|
||||
list(APPEND GCC_TEST_FLAGS |
||||
-Wno-conversion-null |
||||
-Wno-missing-declarations |
||||
-Wno-sign-compare |
||||
-Wno-unused-function |
||||
-Wno-unused-parameter |
||||
-Wno-unused-private-field |
||||
) |
||||
|
||||
list(APPEND LLVM_FLAGS |
||||
-Wall |
||||
-Wextra |
||||
-Weverything |
||||
-Wno-c++98-compat-pedantic |
||||
-Wno-conversion |
||||
-Wno-covered-switch-default |
||||
-Wno-deprecated |
||||
-Wno-disabled-macro-expansion |
||||
-Wno-double-promotion |
||||
-Wno-comma |
||||
-Wno-extra-semi |
||||
-Wno-packed |
||||
-Wno-padded |
||||
-Wno-sign-compare |
||||
-Wno-float-conversion |
||||
-Wno-float-equal |
||||
-Wno-format-nonliteral |
||||
-Wno-gcc-compat |
||||
-Wno-global-constructors |
||||
-Wno-exit-time-destructors |
||||
-Wno-nested-anon-types |
||||
-Wno-non-modular-include-in-module |
||||
-Wno-old-style-cast |
||||
-Wno-range-loop-analysis |
||||
-Wno-reserved-id-macro |
||||
-Wno-shorten-64-to-32 |
||||
-Wno-switch-enum |
||||
-Wno-thread-safety-negative |
||||
-Wno-undef |
||||
-Wno-unknown-warning-option |
||||
-Wno-unreachable-code |
||||
-Wno-unused-macros |
||||
-Wno-weak-vtables |
||||
-Wbitfield-enum-conversion |
||||
-Wbool-conversion |
||||
-Wconstant-conversion |
||||
-Wenum-conversion |
||||
-Wint-conversion |
||||
-Wliteral-conversion |
||||
-Wnon-literal-null-conversion |
||||
-Wnull-conversion |
||||
-Wobjc-literal-conversion |
||||
-Wno-sign-conversion |
||||
-Wstring-conversion |
||||
) |
||||
|
||||
list(APPEND LLVM_TEST_FLAGS |
||||
-Wno-c99-extensions |
||||
-Wno-missing-noreturn |
||||
-Wno-missing-prototypes |
||||
-Wno-missing-variable-declarations |
||||
-Wno-null-conversion |
||||
-Wno-shadow |
||||
-Wno-shift-sign-overflow |
||||
-Wno-sign-compare |
||||
-Wno-unused-function |
||||
-Wno-unused-member-function |
||||
-Wno-unused-parameter |
||||
-Wno-unused-private-field |
||||
-Wno-unused-template |
||||
-Wno-used-but-marked-unused |
||||
-Wno-zero-as-null-pointer-constant |
||||
-Wno-gnu-zero-variadic-macro-arguments |
||||
) |
||||
|
||||
list(APPEND MSVC_FLAGS |
||||
/W3 |
||||
/wd4005 |
||||
/wd4018 |
||||
/wd4068 |
||||
/wd4180 |
||||
/wd4244 |
||||
/wd4267 |
||||
/wd4800 |
||||
/DNOMINMAX |
||||
/DWIN32_LEAN_AND_MEAN |
||||
/D_CRT_SECURE_NO_WARNINGS |
||||
/D_SCL_SECURE_NO_WARNINGS |
||||
/D_ENABLE_EXTENDED_ALIGNED_STORAGE |
||||
) |
||||
|
||||
list(APPEND MSVC_TEST_FLAGS |
||||
/wd4101 |
||||
/wd4503 |
||||
) |
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") |
||||
set(ABSL_DEFAULT_COPTS "${GCC_FLAGS}") |
||||
set(ABSL_TEST_COPTS "${GCC_FLAGS};${GCC_TEST_FLAGS}") |
||||
set(ABSL_EXCEPTIONS_FLAG "-fexceptions") |
||||
elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") |
||||
# MATCHES so we get both Clang and AppleClang |
||||
set(ABSL_DEFAULT_COPTS "${LLVM_FLAGS}") |
||||
set(ABSL_TEST_COPTS "${LLVM_FLAGS};${LLVM_TEST_FLAGS}") |
||||
set(ABSL_EXCEPTIONS_FLAG "-fexceptions") |
||||
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") |
||||
set(ABSL_DEFAULT_COPTS "${MSVC_FLAGS}") |
||||
set(ABSL_TEST_COPTS "${MSVC_FLAGS};${MSVC_TEST_FLAGS}") |
||||
set(ABSL_EXCEPTIONS_FLAG "/U_HAS_EXCEPTIONS;/D_HAS_EXCEPTIONS=1;/EHsc") |
||||
else() |
||||
message(WARNING "Unknown compiler: ${CMAKE_CXX_COMPILER}. Building with no default flags") |
||||
set(ABSL_DEFAULT_COPTS "") |
||||
set(ABSL_TEST_COPTS "") |
||||
set(ABSL_EXCEPTIONS_FLAG "") |
||||
endif() |
||||
|
||||
# This flag is used internally for Bazel builds and is kept here for consistency |
||||
set(ABSL_EXCEPTIONS_FLAG_LINKOPTS "") |
||||
|
||||
if("${CMAKE_CXX_STANDARD}" EQUAL 98) |
||||
message(FATAL_ERROR "Abseil requires at least C++11") |
||||
elseif(NOT "${CMAKE_CXX_STANDARD}") |
||||
message(STATUS "No CMAKE_CXX_STANDARD set, assuming 11") |
||||
set(ABSL_CXX_STANDARD 11) |
||||
else() |
||||
set(ABSL_CXX_STANDARD "${CMAKE_CXX_STANDARD}") |
||||
endif() |
@ -0,0 +1,195 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_BASE_INTERNAL_BITS_H_ |
||||
#define ABSL_BASE_INTERNAL_BITS_H_ |
||||
|
||||
// This file contains bitwise ops which are implementation details of various
|
||||
// absl libraries.
|
||||
|
||||
#include <cstdint> |
||||
|
||||
// Clang on Windows has __builtin_clzll; otherwise we need to use the
|
||||
// windows intrinsic functions.
|
||||
#if defined(_MSC_VER) |
||||
#include <intrin.h> |
||||
#if defined(_M_X64) |
||||
#pragma intrinsic(_BitScanReverse64) |
||||
#pragma intrinsic(_BitScanForward64) |
||||
#endif |
||||
#pragma intrinsic(_BitScanReverse) |
||||
#pragma intrinsic(_BitScanForward) |
||||
#endif |
||||
|
||||
#include "absl/base/attributes.h" |
||||
|
||||
#if defined(_MSC_VER) |
||||
// We can achieve something similar to attribute((always_inline)) with MSVC by
|
||||
// using the __forceinline keyword, however this is not perfect. MSVC is
|
||||
// much less aggressive about inlining, and even with the __forceinline keyword.
|
||||
#define ABSL_BASE_INTERNAL_FORCEINLINE __forceinline |
||||
#else |
||||
// Use default attribute inline.
|
||||
#define ABSL_BASE_INTERNAL_FORCEINLINE inline ABSL_ATTRIBUTE_ALWAYS_INLINE |
||||
#endif |
||||
|
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace base_internal { |
||||
|
||||
ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64Slow(uint64_t n) { |
||||
int zeroes = 60; |
||||
if (n >> 32) zeroes -= 32, n >>= 32; |
||||
if (n >> 16) zeroes -= 16, n >>= 16; |
||||
if (n >> 8) zeroes -= 8, n >>= 8; |
||||
if (n >> 4) zeroes -= 4, n >>= 4; |
||||
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; |
||||
} |
||||
|
||||
ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64(uint64_t n) { |
||||
#if defined(_MSC_VER) && defined(_M_X64) |
||||
// MSVC does not have __buitin_clzll. Use _BitScanReverse64.
|
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
if (_BitScanReverse64(&result, n)) { |
||||
return 63 - result; |
||||
} |
||||
return 64; |
||||
#elif defined(_MSC_VER) |
||||
// MSVC does not have __buitin_clzll. Compose two calls to _BitScanReverse
|
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
if ((n >> 32) && _BitScanReverse(&result, n >> 32)) { |
||||
return 31 - result; |
||||
} |
||||
if (_BitScanReverse(&result, n)) { |
||||
return 63 - result; |
||||
} |
||||
return 64; |
||||
#elif defined(__GNUC__) |
||||
// Use __builtin_clzll, which uses the following instructions:
|
||||
// x86: bsr
|
||||
// ARM64: clz
|
||||
// PPC: cntlzd
|
||||
static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int)
|
||||
"__builtin_clzll does not take 64-bit arg"); |
||||
|
||||
// Handle 0 as a special case because __builtin_clzll(0) is undefined.
|
||||
if (n == 0) { |
||||
return 64; |
||||
} |
||||
return __builtin_clzll(n); |
||||
#else |
||||
return CountLeadingZeros64Slow(n); |
||||
#endif |
||||
} |
||||
|
||||
ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32Slow(uint64_t n) { |
||||
int zeroes = 28; |
||||
if (n >> 16) zeroes -= 16, n >>= 16; |
||||
if (n >> 8) zeroes -= 8, n >>= 8; |
||||
if (n >> 4) zeroes -= 4, n >>= 4; |
||||
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; |
||||
} |
||||
|
||||
ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32(uint32_t n) { |
||||
#if defined(_MSC_VER) |
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
if (_BitScanReverse(&result, n)) { |
||||
return 31 - result; |
||||
} |
||||
return 32; |
||||
#elif defined(__GNUC__) |
||||
// Use __builtin_clz, which uses the following instructions:
|
||||
// x86: bsr
|
||||
// ARM64: clz
|
||||
// PPC: cntlzd
|
||||
static_assert(sizeof(int) == sizeof(n), |
||||
"__builtin_clz does not take 32-bit arg"); |
||||
|
||||
// Handle 0 as a special case because __builtin_clz(0) is undefined.
|
||||
if (n == 0) { |
||||
return 32; |
||||
} |
||||
return __builtin_clz(n); |
||||
#else |
||||
return CountLeadingZeros32Slow(n); |
||||
#endif |
||||
} |
||||
|
||||
ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64Slow(uint64_t n) { |
||||
int c = 63; |
||||
n &= ~n + 1; |
||||
if (n & 0x00000000FFFFFFFF) c -= 32; |
||||
if (n & 0x0000FFFF0000FFFF) c -= 16; |
||||
if (n & 0x00FF00FF00FF00FF) c -= 8; |
||||
if (n & 0x0F0F0F0F0F0F0F0F) c -= 4; |
||||
if (n & 0x3333333333333333) c -= 2; |
||||
if (n & 0x5555555555555555) c -= 1; |
||||
return c; |
||||
} |
||||
|
||||
ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64(uint64_t n) { |
||||
#if defined(_MSC_VER) && defined(_M_X64) |
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
_BitScanForward64(&result, n); |
||||
return result; |
||||
#elif defined(_MSC_VER) |
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
if (static_cast<uint32_t>(n) == 0) { |
||||
_BitScanForward(&result, n >> 32); |
||||
return result + 32; |
||||
} |
||||
_BitScanForward(&result, n); |
||||
return result; |
||||
#elif defined(__GNUC__) |
||||
static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int)
|
||||
"__builtin_ctzll does not take 64-bit arg"); |
||||
return __builtin_ctzll(n); |
||||
#else |
||||
return CountTrailingZerosNonZero64Slow(n); |
||||
#endif |
||||
} |
||||
|
||||
ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32Slow(uint32_t n) { |
||||
int c = 31; |
||||
n &= ~n + 1; |
||||
if (n & 0x0000FFFF) c -= 16; |
||||
if (n & 0x00FF00FF) c -= 8; |
||||
if (n & 0x0F0F0F0F) c -= 4; |
||||
if (n & 0x33333333) c -= 2; |
||||
if (n & 0x55555555) c -= 1; |
||||
return c; |
||||
} |
||||
|
||||
ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) { |
||||
#if defined(_MSC_VER) |
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
_BitScanForward(&result, n); |
||||
return result; |
||||
#elif defined(__GNUC__) |
||||
static_assert(sizeof(int) == sizeof(n), |
||||
"__builtin_ctz does not take 32-bit arg"); |
||||
return __builtin_ctz(n); |
||||
#else |
||||
return CountTrailingZerosNonZero32Slow(n); |
||||
#endif |
||||
} |
||||
|
||||
#undef ABSL_BASE_INTERNAL_FORCEINLINE |
||||
|
||||
} // namespace base_internal
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_BASE_INTERNAL_BITS_H_
|
@ -0,0 +1,97 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/base/internal/bits.h" |
||||
|
||||
#include "gtest/gtest.h" |
||||
|
||||
namespace { |
||||
|
||||
int CLZ64(uint64_t n) { |
||||
int fast = absl::base_internal::CountLeadingZeros64(n); |
||||
int slow = absl::base_internal::CountLeadingZeros64Slow(n); |
||||
EXPECT_EQ(fast, slow) << n; |
||||
return fast; |
||||
} |
||||
|
||||
TEST(BitsTest, CountLeadingZeros64) { |
||||
EXPECT_EQ(64, CLZ64(uint64_t{})); |
||||
EXPECT_EQ(0, CLZ64(~uint64_t{})); |
||||
|
||||
for (int index = 0; index < 64; index++) { |
||||
uint64_t x = static_cast<uint64_t>(1) << index; |
||||
const auto cnt = 63 - index; |
||||
ASSERT_EQ(cnt, CLZ64(x)) << index; |
||||
ASSERT_EQ(cnt, CLZ64(x + x - 1)) << index; |
||||
} |
||||
} |
||||
|
||||
int CLZ32(uint32_t n) { |
||||
int fast = absl::base_internal::CountLeadingZeros32(n); |
||||
int slow = absl::base_internal::CountLeadingZeros32Slow(n); |
||||
EXPECT_EQ(fast, slow) << n; |
||||
return fast; |
||||
} |
||||
|
||||
TEST(BitsTest, CountLeadingZeros32) { |
||||
EXPECT_EQ(32, CLZ32(uint32_t{})); |
||||
EXPECT_EQ(0, CLZ32(~uint32_t{})); |
||||
|
||||
for (int index = 0; index < 32; index++) { |
||||
uint32_t x = static_cast<uint32_t>(1) << index; |
||||
const auto cnt = 31 - index; |
||||
ASSERT_EQ(cnt, CLZ32(x)) << index; |
||||
ASSERT_EQ(cnt, CLZ32(x + x - 1)) << index; |
||||
ASSERT_EQ(CLZ64(x), CLZ32(x) + 32); |
||||
} |
||||
} |
||||
|
||||
int CTZ64(uint64_t n) { |
||||
int fast = absl::base_internal::CountTrailingZerosNonZero64(n); |
||||
int slow = absl::base_internal::CountTrailingZerosNonZero64Slow(n); |
||||
EXPECT_EQ(fast, slow) << n; |
||||
return fast; |
||||
} |
||||
|
||||
TEST(BitsTest, CountTrailingZerosNonZero64) { |
||||
EXPECT_EQ(0, CTZ64(~uint64_t{})); |
||||
|
||||
for (int index = 0; index < 64; index++) { |
||||
uint64_t x = static_cast<uint64_t>(1) << index; |
||||
const auto cnt = index; |
||||
ASSERT_EQ(cnt, CTZ64(x)) << index; |
||||
ASSERT_EQ(cnt, CTZ64(~(x - 1))) << index; |
||||
} |
||||
} |
||||
|
||||
int CTZ32(uint32_t n) { |
||||
int fast = absl::base_internal::CountTrailingZerosNonZero32(n); |
||||
int slow = absl::base_internal::CountTrailingZerosNonZero32Slow(n); |
||||
EXPECT_EQ(fast, slow) << n; |
||||
return fast; |
||||
} |
||||
|
||||
TEST(BitsTest, CountTrailingZerosNonZero32) { |
||||
EXPECT_EQ(0, CTZ32(~uint32_t{})); |
||||
|
||||
for (int index = 0; index < 32; index++) { |
||||
uint32_t x = static_cast<uint32_t>(1) << index; |
||||
const auto cnt = index; |
||||
ASSERT_EQ(cnt, CTZ32(x)) << index; |
||||
ASSERT_EQ(cnt, CTZ32(~(x - 1))) << index; |
||||
} |
||||
} |
||||
|
||||
|
||||
} // namespace
|
@ -0,0 +1,52 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// See also //absl/synchronization:mutex_benchmark for a comparison of SpinLock
|
||||
// and Mutex performance under varying levels of contention.
|
||||
|
||||
#include "absl/base/internal/raw_logging.h" |
||||
#include "absl/base/internal/scheduling_mode.h" |
||||
#include "absl/base/internal/spinlock.h" |
||||
#include "absl/synchronization/internal/create_thread_identity.h" |
||||
#include "benchmark/benchmark.h" |
||||
|
||||
namespace { |
||||
|
||||
template <absl::base_internal::SchedulingMode scheduling_mode> |
||||
static void BM_SpinLock(benchmark::State& state) { |
||||
// Ensure a ThreadIdentity is installed.
|
||||
ABSL_INTERNAL_CHECK( |
||||
absl::synchronization_internal::GetOrCreateCurrentThreadIdentity() != |
||||
nullptr, |
||||
"GetOrCreateCurrentThreadIdentity() failed"); |
||||
|
||||
static auto* spinlock = new absl::base_internal::SpinLock(scheduling_mode); |
||||
for (auto _ : state) { |
||||
absl::base_internal::SpinLockHolder holder(spinlock); |
||||
} |
||||
} |
||||
|
||||
BENCHMARK_TEMPLATE(BM_SpinLock, |
||||
absl::base_internal::SCHEDULE_KERNEL_ONLY) |
||||
->UseRealTime() |
||||
->Threads(1) |
||||
->ThreadPerCpu(); |
||||
|
||||
BENCHMARK_TEMPLATE(BM_SpinLock, |
||||
absl::base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL) |
||||
->UseRealTime() |
||||
->Threads(1) |
||||
->ThreadPerCpu(); |
||||
|
||||
} // namespace
|
@ -0,0 +1,72 @@ |
||||
// Copyright 2018 The Abseil Authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
// |
||||
// This file is a Linux-specific part of spinlock_wait.cc |
||||
|
||||
#include <linux/futex.h> |
||||
#include <sys/syscall.h> |
||||
#include <unistd.h> |
||||
|
||||
#include <atomic> |
||||
#include <cerrno> |
||||
#include <climits> |
||||
#include <cstdint> |
||||
#include <ctime> |
||||
|
||||
#include "absl/base/attributes.h" |
||||
|
||||
// The SpinLock lockword is `std::atomic<uint32_t>`. Here we assert that |
||||
// `std::atomic<uint32_t>` is bitwise equivalent of the `int` expected |
||||
// by SYS_futex. We also assume that reads/writes done to the lockword |
||||
// by SYS_futex have rational semantics with regard to the |
||||
// std::atomic<> API. C++ provides no guarantees of these assumptions, |
||||
// but they are believed to hold in practice. |
||||
static_assert(sizeof(std::atomic<uint32_t>) == sizeof(int), |
||||
"SpinLock lockword has the wrong size for a futex"); |
||||
|
||||
// Some Android headers are missing these definitions even though they |
||||
// support these futex operations. |
||||
#ifdef __BIONIC__ |
||||
#ifndef SYS_futex |
||||
#define SYS_futex __NR_futex |
||||
#endif |
||||
#ifndef FUTEX_PRIVATE_FLAG |
||||
#define FUTEX_PRIVATE_FLAG 128 |
||||
#endif |
||||
#endif |
||||
|
||||
extern "C" { |
||||
|
||||
ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( |
||||
std::atomic<uint32_t> *w, uint32_t value, int loop, |
||||
absl::base_internal::SchedulingMode) { |
||||
if (loop != 0) { |
||||
int save_errno = errno; |
||||
struct timespec tm; |
||||
tm.tv_sec = 0; |
||||
// Increase the delay; we expect (but do not rely on) explicit wakeups. |
||||
// We don't rely on explicit wakeups because we intentionally allow for |
||||
// a race on the kSpinLockSleeper bit. |
||||
tm.tv_nsec = 16 * absl::base_internal::SpinLockSuggestedDelayNS(loop); |
||||
syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, &tm); |
||||
errno = save_errno; |
||||
} |
||||
} |
||||
|
||||
ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(std::atomic<uint32_t> *w, |
||||
bool all) { |
||||
syscall(SYS_futex, w, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, all ? INT_MAX : 1, 0); |
||||
} |
||||
|
||||
} // extern "C" |
@ -0,0 +1,39 @@ |
||||
# |
||||
# Copyright 2018 The Abseil Authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
# |
||||
|
||||
"""Creates config_setting that allows selecting based on 'compiler' value.""" |
||||
|
||||
def create_llvm_config(name, visibility): |
||||
# The "do_not_use_tools_cpp_compiler_present" attribute exists to |
||||
# distinguish between older versions of Bazel that do not support |
||||
# "@bazel_tools//tools/cpp:compiler" flag_value, and newer ones that do. |
||||
# In the future, the only way to select on the compiler will be through |
||||
# flag_values{"@bazel_tools//tools/cpp:compiler"} and the else branch can |
||||
# be removed. |
||||
if hasattr(cc_common, "do_not_use_tools_cpp_compiler_present"): |
||||
native.config_setting( |
||||
name = name, |
||||
flag_values = { |
||||
"@bazel_tools//tools/cpp:compiler": "llvm", |
||||
}, |
||||
visibility = visibility, |
||||
) |
||||
else: |
||||
native.config_setting( |
||||
name = name, |
||||
values = {"compiler": "llvm"}, |
||||
visibility = visibility, |
||||
) |
@ -0,0 +1,119 @@ |
||||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <initializer_list> |
||||
|
||||
#include "absl/container/fixed_array.h" |
||||
|
||||
#include "gtest/gtest.h" |
||||
#include "absl/base/internal/exception_safety_testing.h" |
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
|
||||
namespace { |
||||
|
||||
constexpr size_t kInlined = 25; |
||||
constexpr size_t kSmallSize = kInlined / 2; |
||||
constexpr size_t kLargeSize = kInlined * 2; |
||||
|
||||
constexpr int kInitialValue = 5; |
||||
constexpr int kUpdatedValue = 10; |
||||
|
||||
using ::testing::TestThrowingCtor; |
||||
|
||||
using Thrower = testing::ThrowingValue<testing::TypeSpec::kEverythingThrows>; |
||||
using FixedArr = absl::FixedArray<Thrower, kInlined>; |
||||
|
||||
using MoveThrower = testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>; |
||||
using MoveFixedArr = absl::FixedArray<MoveThrower, kInlined>; |
||||
|
||||
TEST(FixedArrayExceptionSafety, CopyConstructor) { |
||||
auto small = FixedArr(kSmallSize); |
||||
TestThrowingCtor<FixedArr>(small); |
||||
|
||||
auto large = FixedArr(kLargeSize); |
||||
TestThrowingCtor<FixedArr>(large); |
||||
} |
||||
|
||||
TEST(FixedArrayExceptionSafety, MoveConstructor) { |
||||
TestThrowingCtor<FixedArr>(FixedArr(kSmallSize)); |
||||
TestThrowingCtor<FixedArr>(FixedArr(kLargeSize)); |
||||
|
||||
// TypeSpec::kNoThrowMove
|
||||
TestThrowingCtor<MoveFixedArr>(MoveFixedArr(kSmallSize)); |
||||
TestThrowingCtor<MoveFixedArr>(MoveFixedArr(kLargeSize)); |
||||
} |
||||
|
||||
TEST(FixedArrayExceptionSafety, SizeConstructor) { |
||||
TestThrowingCtor<FixedArr>(kSmallSize); |
||||
TestThrowingCtor<FixedArr>(kLargeSize); |
||||
} |
||||
|
||||
TEST(FixedArrayExceptionSafety, SizeValueConstructor) { |
||||
TestThrowingCtor<FixedArr>(kSmallSize, Thrower()); |
||||
TestThrowingCtor<FixedArr>(kLargeSize, Thrower()); |
||||
} |
||||
|
||||
TEST(FixedArrayExceptionSafety, IteratorConstructor) { |
||||
auto small = FixedArr(kSmallSize); |
||||
TestThrowingCtor<FixedArr>(small.begin(), small.end()); |
||||
|
||||
auto large = FixedArr(kLargeSize); |
||||
TestThrowingCtor<FixedArr>(large.begin(), large.end()); |
||||
} |
||||
|
||||
TEST(FixedArrayExceptionSafety, InitListConstructor) { |
||||
constexpr int small_inlined = 3; |
||||
using SmallFixedArr = absl::FixedArray<Thrower, small_inlined>; |
||||
|
||||
TestThrowingCtor<SmallFixedArr>(std::initializer_list<Thrower>{}); |
||||
// Test inlined allocation
|
||||
TestThrowingCtor<SmallFixedArr>( |
||||
std::initializer_list<Thrower>{Thrower{}, Thrower{}}); |
||||
// Test out of line allocation
|
||||
TestThrowingCtor<SmallFixedArr>(std::initializer_list<Thrower>{ |
||||
Thrower{}, Thrower{}, Thrower{}, Thrower{}, Thrower{}}); |
||||
} |
||||
|
||||
testing::AssertionResult ReadMemory(FixedArr* fixed_arr) { |
||||
// Marked volatile to prevent optimization. Used for running asan tests.
|
||||
volatile int sum = 0; |
||||
for (const auto& thrower : *fixed_arr) { |
||||
sum += thrower.Get(); |
||||
} |
||||
return testing::AssertionSuccess() << "Values sum to [" << sum << "]"; |
||||
} |
||||
|
||||
TEST(FixedArrayExceptionSafety, Fill) { |
||||
auto test_fill = testing::MakeExceptionSafetyTester() |
||||
.WithContracts(ReadMemory) |
||||
.WithOperation([&](FixedArr* fixed_arr_ptr) { |
||||
auto thrower = |
||||
Thrower(kUpdatedValue, testing::nothrow_ctor); |
||||
fixed_arr_ptr->fill(thrower); |
||||
}); |
||||
|
||||
EXPECT_TRUE( |
||||
test_fill.WithInitialValue(FixedArr(kSmallSize, Thrower(kInitialValue))) |
||||
.Test()); |
||||
EXPECT_TRUE( |
||||
test_fill.WithInitialValue(FixedArr(kLargeSize, Thrower(kInitialValue))) |
||||
.Test()); |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
@ -0,0 +1,582 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: flat_hash_map.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// An `absl::flat_hash_map<K, V>` is an unordered associative container of
|
||||
// unique keys and associated values designed to be a more efficient replacement
|
||||
// for `std::unordered_map`. Like `unordered_map`, search, insertion, and
|
||||
// deletion of map elements can be done as an `O(1)` operation. However,
|
||||
// `flat_hash_map` (and other unordered associative containers known as the
|
||||
// collection of Abseil "Swiss tables") contain other optimizations that result
|
||||
// in both memory and computation advantages.
|
||||
//
|
||||
// In most cases, your default choice for a hash map should be a map of type
|
||||
// `flat_hash_map`.
|
||||
|
||||
#ifndef ABSL_CONTAINER_FLAT_HASH_MAP_H_ |
||||
#define ABSL_CONTAINER_FLAT_HASH_MAP_H_ |
||||
|
||||
#include <cstddef> |
||||
#include <new> |
||||
#include <type_traits> |
||||
#include <utility> |
||||
|
||||
#include "absl/algorithm/container.h" |
||||
#include "absl/container/internal/container_memory.h" |
||||
#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export |
||||
#include "absl/container/internal/raw_hash_map.h" // IWYU pragma: export |
||||
#include "absl/memory/memory.h" |
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
template <class K, class V> |
||||
struct FlatHashMapPolicy; |
||||
} // namespace container_internal
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// absl::flat_hash_map
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// An `absl::flat_hash_map<K, V>` is an unordered associative container which
|
||||
// has been optimized for both speed and memory footprint in most common use
|
||||
// cases. Its interface is similar to that of `std::unordered_map<K, V>` with
|
||||
// the following notable differences:
|
||||
//
|
||||
// * Requires keys that are CopyConstructible
|
||||
// * Requires values that are MoveConstructible
|
||||
// * Supports heterogeneous lookup, through `find()`, `operator[]()` and
|
||||
// `insert()`, provided that the map is provided a compatible heterogeneous
|
||||
// hashing function and equality operator.
|
||||
// * Invalidates any references and pointers to elements within the table after
|
||||
// `rehash()`.
|
||||
// * Contains a `capacity()` member function indicating the number of element
|
||||
// slots (open, deleted, and empty) within the hash map.
|
||||
// * Returns `void` from the `erase(iterator)` overload.
|
||||
//
|
||||
// By default, `flat_hash_map` uses the `absl::Hash` hashing framework.
|
||||
// All fundamental and Abseil types that support the `absl::Hash` framework have
|
||||
// a compatible equality operator for comparing insertions into `flat_hash_map`.
|
||||
// If your type is not yet supported by the `absl::Hash` framework, see
|
||||
// absl/hash/hash.h for information on extending Abseil hashing to user-defined
|
||||
// types.
|
||||
//
|
||||
// NOTE: A `flat_hash_map` stores its value types directly inside its
|
||||
// implementation array to avoid memory indirection. Because a `flat_hash_map`
|
||||
// is designed to move data when rehashed, map values will not retain pointer
|
||||
// stability. If you require pointer stability, or your values are large,
|
||||
// consider using `absl::flat_hash_map<Key, std::unique_ptr<Value>>` instead.
|
||||
// If your types are not moveable or you require pointer stability for keys,
|
||||
// consider `absl::node_hash_map`.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// // Create a flat hash map of three strings (that map to strings)
|
||||
// absl::flat_hash_map<std::string, std::string> ducks =
|
||||
// {{"a", "huey"}, {"b", "dewey"}, {"c", "louie"}};
|
||||
//
|
||||
// // Insert a new element into the flat hash map
|
||||
// ducks.insert({"d", "donald"});
|
||||
//
|
||||
// // Force a rehash of the flat hash map
|
||||
// ducks.rehash(0);
|
||||
//
|
||||
// // Find the element with the key "b"
|
||||
// std::string search_key = "b";
|
||||
// auto result = ducks.find(search_key);
|
||||
// if (result != ducks.end()) {
|
||||
// std::cout << "Result: " << result->second << std::endl;
|
||||
// }
|
||||
template <class K, class V, |
||||
class Hash = absl::container_internal::hash_default_hash<K>, |
||||
class Eq = absl::container_internal::hash_default_eq<K>, |
||||
class Allocator = std::allocator<std::pair<const K, V>>> |
||||
class flat_hash_map : public absl::container_internal::raw_hash_map< |
||||
absl::container_internal::FlatHashMapPolicy<K, V>, |
||||
Hash, Eq, Allocator> { |
||||
using Base = typename flat_hash_map::raw_hash_map; |
||||
|
||||
public: |
||||
// Constructors and Assignment Operators
|
||||
//
|
||||
// A flat_hash_map supports the same overload set as `std::unordered_map`
|
||||
// for construction and assignment:
|
||||
//
|
||||
// * Default constructor
|
||||
//
|
||||
// // No allocation for the table's elements is made.
|
||||
// absl::flat_hash_map<int, std::string> map1;
|
||||
//
|
||||
// * Initializer List constructor
|
||||
//
|
||||
// absl::flat_hash_map<int, std::string> map2 =
|
||||
// {{1, "huey"}, {2, "dewey"}, {3, "louie"},};
|
||||
//
|
||||
// * Copy constructor
|
||||
//
|
||||
// absl::flat_hash_map<int, std::string> map3(map2);
|
||||
//
|
||||
// * Copy assignment operator
|
||||
//
|
||||
// // Hash functor and Comparator are copied as well
|
||||
// absl::flat_hash_map<int, std::string> map4;
|
||||
// map4 = map3;
|
||||
//
|
||||
// * Move constructor
|
||||
//
|
||||
// // Move is guaranteed efficient
|
||||
// absl::flat_hash_map<int, std::string> map5(std::move(map4));
|
||||
//
|
||||
// * Move assignment operator
|
||||
//
|
||||
// // May be efficient if allocators are compatible
|
||||
// absl::flat_hash_map<int, std::string> map6;
|
||||
// map6 = std::move(map5);
|
||||
//
|
||||
// * Range constructor
|
||||
//
|
||||
// std::vector<std::pair<int, std::string>> v = {{1, "a"}, {2, "b"}};
|
||||
// absl::flat_hash_map<int, std::string> map7(v.begin(), v.end());
|
||||
flat_hash_map() {} |
||||
using Base::Base; |
||||
|
||||
// flat_hash_map::begin()
|
||||
//
|
||||
// Returns an iterator to the beginning of the `flat_hash_map`.
|
||||
using Base::begin; |
||||
|
||||
// flat_hash_map::cbegin()
|
||||
//
|
||||
// Returns a const iterator to the beginning of the `flat_hash_map`.
|
||||
using Base::cbegin; |
||||
|
||||
// flat_hash_map::cend()
|
||||
//
|
||||
// Returns a const iterator to the end of the `flat_hash_map`.
|
||||
using Base::cend; |
||||
|
||||
// flat_hash_map::end()
|
||||
//
|
||||
// Returns an iterator to the end of the `flat_hash_map`.
|
||||
using Base::end; |
||||
|
||||
// flat_hash_map::capacity()
|
||||
//
|
||||
// Returns the number of element slots (assigned, deleted, and empty)
|
||||
// available within the `flat_hash_map`.
|
||||
//
|
||||
// NOTE: this member function is particular to `absl::flat_hash_map` and is
|
||||
// not provided in the `std::unordered_map` API.
|
||||
using Base::capacity; |
||||
|
||||
// flat_hash_map::empty()
|
||||
//
|
||||
// Returns whether or not the `flat_hash_map` is empty.
|
||||
using Base::empty; |
||||
|
||||
// flat_hash_map::max_size()
|
||||
//
|
||||
// Returns the largest theoretical possible number of elements within a
|
||||
// `flat_hash_map` under current memory constraints. This value can be thought
|
||||
// of the largest value of `std::distance(begin(), end())` for a
|
||||
// `flat_hash_map<K, V>`.
|
||||
using Base::max_size; |
||||
|
||||
// flat_hash_map::size()
|
||||
//
|
||||
// Returns the number of elements currently within the `flat_hash_map`.
|
||||
using Base::size; |
||||
|
||||
// flat_hash_map::clear()
|
||||
//
|
||||
// Removes all elements from the `flat_hash_map`. Invalidates any references,
|
||||
// pointers, or iterators referring to contained elements.
|
||||
//
|
||||
// NOTE: this operation may shrink the underlying buffer. To avoid shrinking
|
||||
// the underlying buffer call `erase(begin(), end())`.
|
||||
using Base::clear; |
||||
|
||||
// flat_hash_map::erase()
|
||||
//
|
||||
// Erases elements within the `flat_hash_map`. Erasing does not trigger a
|
||||
// rehash. Overloads are listed below.
|
||||
//
|
||||
// void erase(const_iterator pos):
|
||||
//
|
||||
// Erases the element at `position` of the `flat_hash_map`, returning
|
||||
// `void`.
|
||||
//
|
||||
// NOTE: this return behavior is different than that of STL containers in
|
||||
// general and `std::unordered_map` in particular.
|
||||
//
|
||||
// iterator erase(const_iterator first, const_iterator last):
|
||||
//
|
||||
// Erases the elements in the open interval [`first`, `last`), returning an
|
||||
// iterator pointing to `last`.
|
||||
//
|
||||
// size_type erase(const key_type& key):
|
||||
//
|
||||
// Erases the element with the matching key, if it exists.
|
||||
using Base::erase; |
||||
|
||||
// flat_hash_map::insert()
|
||||
//
|
||||
// Inserts an element of the specified value into the `flat_hash_map`,
|
||||
// returning an iterator pointing to the newly inserted element, provided that
|
||||
// an element with the given key does not already exist. If rehashing occurs
|
||||
// due to the insertion, all iterators are invalidated. Overloads are listed
|
||||
// below.
|
||||
//
|
||||
// std::pair<iterator,bool> insert(const init_type& value):
|
||||
//
|
||||
// Inserts a value into the `flat_hash_map`. Returns a pair consisting of an
|
||||
// iterator to the inserted element (or to the element that prevented the
|
||||
// insertion) and a bool denoting whether the insertion took place.
|
||||
//
|
||||
// std::pair<iterator,bool> insert(T&& value):
|
||||
// std::pair<iterator,bool> insert(init_type&& value):
|
||||
//
|
||||
// Inserts a moveable value into the `flat_hash_map`. Returns a pair
|
||||
// consisting of an iterator to the inserted element (or to the element that
|
||||
// prevented the insertion) and a bool denoting whether the insertion took
|
||||
// place.
|
||||
//
|
||||
// iterator insert(const_iterator hint, const init_type& value):
|
||||
// iterator insert(const_iterator hint, T&& value):
|
||||
// iterator insert(const_iterator hint, init_type&& value);
|
||||
//
|
||||
// Inserts a value, using the position of `hint` as a non-binding suggestion
|
||||
// for where to begin the insertion search. Returns an iterator to the
|
||||
// inserted element, or to the existing element that prevented the
|
||||
// insertion.
|
||||
//
|
||||
// void insert(InputIterator first, InputIterator last):
|
||||
//
|
||||
// Inserts a range of values [`first`, `last`).
|
||||
//
|
||||
// NOTE: Although the STL does not specify which element may be inserted if
|
||||
// multiple keys compare equivalently, for `flat_hash_map` we guarantee the
|
||||
// first match is inserted.
|
||||
//
|
||||
// void insert(std::initializer_list<init_type> ilist):
|
||||
//
|
||||
// Inserts the elements within the initializer list `ilist`.
|
||||
//
|
||||
// NOTE: Although the STL does not specify which element may be inserted if
|
||||
// multiple keys compare equivalently within the initializer list, for
|
||||
// `flat_hash_map` we guarantee the first match is inserted.
|
||||
using Base::insert; |
||||
|
||||
// flat_hash_map::insert_or_assign()
|
||||
//
|
||||
// Inserts an element of the specified value into the `flat_hash_map` provided
|
||||
// that a value with the given key does not already exist, or replaces it with
|
||||
// the element value if a key for that value already exists, returning an
|
||||
// iterator pointing to the newly inserted element. If rehashing occurs due
|
||||
// to the insertion, all existing iterators are invalidated. Overloads are
|
||||
// listed below.
|
||||
//
|
||||
// pair<iterator, bool> insert_or_assign(const init_type& k, T&& obj):
|
||||
// pair<iterator, bool> insert_or_assign(init_type&& k, T&& obj):
|
||||
//
|
||||
// Inserts/Assigns (or moves) the element of the specified key into the
|
||||
// `flat_hash_map`.
|
||||
//
|
||||
// iterator insert_or_assign(const_iterator hint,
|
||||
// const init_type& k, T&& obj):
|
||||
// iterator insert_or_assign(const_iterator hint, init_type&& k, T&& obj):
|
||||
//
|
||||
// Inserts/Assigns (or moves) the element of the specified key into the
|
||||
// `flat_hash_map` using the position of `hint` as a non-binding suggestion
|
||||
// for where to begin the insertion search.
|
||||
using Base::insert_or_assign; |
||||
|
||||
// flat_hash_map::emplace()
|
||||
//
|
||||
// Inserts an element of the specified value by constructing it in-place
|
||||
// within the `flat_hash_map`, provided that no element with the given key
|
||||
// already exists.
|
||||
//
|
||||
// The element may be constructed even if there already is an element with the
|
||||
// key in the container, in which case the newly constructed element will be
|
||||
// destroyed immediately. Prefer `try_emplace()` unless your key is not
|
||||
// copyable or moveable.
|
||||
//
|
||||
// If rehashing occurs due to the insertion, all iterators are invalidated.
|
||||
using Base::emplace; |
||||
|
||||
// flat_hash_map::emplace_hint()
|
||||
//
|
||||
// Inserts an element of the specified value by constructing it in-place
|
||||
// within the `flat_hash_map`, using the position of `hint` as a non-binding
|
||||
// suggestion for where to begin the insertion search, and only inserts
|
||||
// provided that no element with the given key already exists.
|
||||
//
|
||||
// The element may be constructed even if there already is an element with the
|
||||
// key in the container, in which case the newly constructed element will be
|
||||
// destroyed immediately. Prefer `try_emplace()` unless your key is not
|
||||
// copyable or moveable.
|
||||
//
|
||||
// If rehashing occurs due to the insertion, all iterators are invalidated.
|
||||
using Base::emplace_hint; |
||||
|
||||
// flat_hash_map::try_emplace()
|
||||
//
|
||||
// Inserts an element of the specified value by constructing it in-place
|
||||
// within the `flat_hash_map`, provided that no element with the given key
|
||||
// already exists. Unlike `emplace()`, if an element with the given key
|
||||
// already exists, we guarantee that no element is constructed.
|
||||
//
|
||||
// If rehashing occurs due to the insertion, all iterators are invalidated.
|
||||
// Overloads are listed below.
|
||||
//
|
||||
// pair<iterator, bool> try_emplace(const key_type& k, Args&&... args):
|
||||
// pair<iterator, bool> try_emplace(key_type&& k, Args&&... args):
|
||||
//
|
||||
// Inserts (via copy or move) the element of the specified key into the
|
||||
// `flat_hash_map`.
|
||||
//
|
||||
// iterator try_emplace(const_iterator hint,
|
||||
// const init_type& k, Args&&... args):
|
||||
// iterator try_emplace(const_iterator hint, init_type&& k, Args&&... args):
|
||||
//
|
||||
// Inserts (via copy or move) the element of the specified key into the
|
||||
// `flat_hash_map` using the position of `hint` as a non-binding suggestion
|
||||
// for where to begin the insertion search.
|
||||
using Base::try_emplace; |
||||
|
||||
// flat_hash_map::extract()
|
||||
//
|
||||
// Extracts the indicated element, erasing it in the process, and returns it
|
||||
// as a C++17-compatible node handle. Overloads are listed below.
|
||||
//
|
||||
// node_type extract(const_iterator position):
|
||||
//
|
||||
// Extracts the key,value pair of the element at the indicated position and
|
||||
// returns a node handle owning that extracted data.
|
||||
//
|
||||
// node_type extract(const key_type& x):
|
||||
//
|
||||
// Extracts the key,value pair of the element with a key matching the passed
|
||||
// key value and returns a node handle owning that extracted data. If the
|
||||
// `flat_hash_map` does not contain an element with a matching key, this
|
||||
// function returns an empty node handle.
|
||||
using Base::extract; |
||||
|
||||
// flat_hash_map::merge()
|
||||
//
|
||||
// Extracts elements from a given `source` flat hash map into this
|
||||
// `flat_hash_map`. If the destination `flat_hash_map` already contains an
|
||||
// element with an equivalent key, that element is not extracted.
|
||||
using Base::merge; |
||||
|
||||
// flat_hash_map::swap(flat_hash_map& other)
|
||||
//
|
||||
// Exchanges the contents of this `flat_hash_map` with those of the `other`
|
||||
// flat hash map, avoiding invocation of any move, copy, or swap operations on
|
||||
// individual elements.
|
||||
//
|
||||
// All iterators and references on the `flat_hash_map` remain valid, excepting
|
||||
// for the past-the-end iterator, which is invalidated.
|
||||
//
|
||||
// `swap()` requires that the flat hash map's hashing and key equivalence
|
||||
// functions be Swappable, and are exchaged using unqualified calls to
|
||||
// non-member `swap()`. If the map's allocator has
|
||||
// `std::allocator_traits<allocator_type>::propagate_on_container_swap::value`
|
||||
// set to `true`, the allocators are also exchanged using an unqualified call
|
||||
// to non-member `swap()`; otherwise, the allocators are not swapped.
|
||||
using Base::swap; |
||||
|
||||
// flat_hash_map::rehash(count)
|
||||
//
|
||||
// Rehashes the `flat_hash_map`, setting the number of slots to be at least
|
||||
// the passed value. If the new number of slots increases the load factor more
|
||||
// than the current maximum load factor
|
||||
// (`count` < `size()` / `max_load_factor()`), then the new number of slots
|
||||
// will be at least `size()` / `max_load_factor()`.
|
||||
//
|
||||
// To force a rehash, pass rehash(0).
|
||||
//
|
||||
// NOTE: unlike behavior in `std::unordered_map`, references are also
|
||||
// invalidated upon a `rehash()`.
|
||||
using Base::rehash; |
||||
|
||||
// flat_hash_map::reserve(count)
|
||||
//
|
||||
// Sets the number of slots in the `flat_hash_map` to the number needed to
|
||||
// accommodate at least `count` total elements without exceeding the current
|
||||
// maximum load factor, and may rehash the container if needed.
|
||||
using Base::reserve; |
||||
|
||||
// flat_hash_map::at()
|
||||
//
|
||||
// Returns a reference to the mapped value of the element with key equivalent
|
||||
// to the passed key.
|
||||
using Base::at; |
||||
|
||||
// flat_hash_map::contains()
|
||||
//
|
||||
// Determines whether an element with a key comparing equal to the given `key`
|
||||
// exists within the `flat_hash_map`, returning `true` if so or `false`
|
||||
// otherwise.
|
||||
using Base::contains; |
||||
|
||||
// flat_hash_map::count(const Key& key) const
|
||||
//
|
||||
// Returns the number of elements with a key comparing equal to the given
|
||||
// `key` within the `flat_hash_map`. note that this function will return
|
||||
// either `1` or `0` since duplicate keys are not allowed within a
|
||||
// `flat_hash_map`.
|
||||
using Base::count; |
||||
|
||||
// flat_hash_map::equal_range()
|
||||
//
|
||||
// Returns a closed range [first, last], defined by a `std::pair` of two
|
||||
// iterators, containing all elements with the passed key in the
|
||||
// `flat_hash_map`.
|
||||
using Base::equal_range; |
||||
|
||||
// flat_hash_map::find()
|
||||
//
|
||||
// Finds an element with the passed `key` within the `flat_hash_map`.
|
||||
using Base::find; |
||||
|
||||
// flat_hash_map::operator[]()
|
||||
//
|
||||
// Returns a reference to the value mapped to the passed key within the
|
||||
// `flat_hash_map`, performing an `insert()` if the key does not already
|
||||
// exist.
|
||||
//
|
||||
// If an insertion occurs and results in a rehashing of the container, all
|
||||
// iterators are invalidated. Otherwise iterators are not affected and
|
||||
// references are not invalidated. Overloads are listed below.
|
||||
//
|
||||
// T& operator[](const Key& key):
|
||||
//
|
||||
// Inserts an init_type object constructed in-place if the element with the
|
||||
// given key does not exist.
|
||||
//
|
||||
// T& operator[](Key&& key):
|
||||
//
|
||||
// Inserts an init_type object constructed in-place provided that an element
|
||||
// with the given key does not exist.
|
||||
using Base::operator[]; |
||||
|
||||
// flat_hash_map::bucket_count()
|
||||
//
|
||||
// Returns the number of "buckets" within the `flat_hash_map`. Note that
|
||||
// because a flat hash map contains all elements within its internal storage,
|
||||
// this value simply equals the current capacity of the `flat_hash_map`.
|
||||
using Base::bucket_count; |
||||
|
||||
// flat_hash_map::load_factor()
|
||||
//
|
||||
// Returns the current load factor of the `flat_hash_map` (the average number
|
||||
// of slots occupied with a value within the hash map).
|
||||
using Base::load_factor; |
||||
|
||||
// flat_hash_map::max_load_factor()
|
||||
//
|
||||
// Manages the maximum load factor of the `flat_hash_map`. Overloads are
|
||||
// listed below.
|
||||
//
|
||||
// float flat_hash_map::max_load_factor()
|
||||
//
|
||||
// Returns the current maximum load factor of the `flat_hash_map`.
|
||||
//
|
||||
// void flat_hash_map::max_load_factor(float ml)
|
||||
//
|
||||
// Sets the maximum load factor of the `flat_hash_map` to the passed value.
|
||||
//
|
||||
// NOTE: This overload is provided only for API compatibility with the STL;
|
||||
// `flat_hash_map` will ignore any set load factor and manage its rehashing
|
||||
// internally as an implementation detail.
|
||||
using Base::max_load_factor; |
||||
|
||||
// flat_hash_map::get_allocator()
|
||||
//
|
||||
// Returns the allocator function associated with this `flat_hash_map`.
|
||||
using Base::get_allocator; |
||||
|
||||
// flat_hash_map::hash_function()
|
||||
//
|
||||
// Returns the hashing function used to hash the keys within this
|
||||
// `flat_hash_map`.
|
||||
using Base::hash_function; |
||||
|
||||
// flat_hash_map::key_eq()
|
||||
//
|
||||
// Returns the function used for comparing keys equality.
|
||||
using Base::key_eq; |
||||
}; |
||||
|
||||
namespace container_internal { |
||||
|
||||
template <class K, class V> |
||||
struct FlatHashMapPolicy { |
||||
using slot_type = container_internal::slot_type<K, V>; |
||||
using key_type = K; |
||||
using mapped_type = V; |
||||
using init_type = std::pair</*non const*/ key_type, mapped_type>; |
||||
|
||||
template <class Allocator, class... Args> |
||||
static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { |
||||
slot_type::construct(alloc, slot, std::forward<Args>(args)...); |
||||
} |
||||
|
||||
template <class Allocator> |
||||
static void destroy(Allocator* alloc, slot_type* slot) { |
||||
slot_type::destroy(alloc, slot); |
||||
} |
||||
|
||||
template <class Allocator> |
||||
static void transfer(Allocator* alloc, slot_type* new_slot, |
||||
slot_type* old_slot) { |
||||
slot_type::transfer(alloc, new_slot, old_slot); |
||||
} |
||||
|
||||
template <class F, class... Args> |
||||
static decltype(absl::container_internal::DecomposePair( |
||||
std::declval<F>(), std::declval<Args>()...)) |
||||
apply(F&& f, Args&&... args) { |
||||
return absl::container_internal::DecomposePair(std::forward<F>(f), |
||||
std::forward<Args>(args)...); |
||||
} |
||||
|
||||
static size_t space_used(const slot_type*) { return 0; } |
||||
|
||||
static std::pair<const K, V>& element(slot_type* slot) { return slot->value; } |
||||
|
||||
static V& value(std::pair<const K, V>* kv) { return kv->second; } |
||||
static const V& value(const std::pair<const K, V>* kv) { return kv->second; } |
||||
}; |
||||
|
||||
} // namespace container_internal
|
||||
|
||||
namespace container_algorithm_internal { |
||||
|
||||
// Specialization of trait in absl/algorithm/container.h
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Allocator> |
||||
struct IsUnorderedContainer< |
||||
absl::flat_hash_map<Key, T, Hash, KeyEqual, Allocator>> : std::true_type {}; |
||||
|
||||
} // namespace container_algorithm_internal
|
||||
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_CONTAINER_FLAT_HASH_MAP_H_
|
@ -0,0 +1,243 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/container/flat_hash_map.h" |
||||
|
||||
#include "absl/container/internal/hash_generator_testing.h" |
||||
#include "absl/container/internal/unordered_map_constructor_test.h" |
||||
#include "absl/container/internal/unordered_map_lookup_test.h" |
||||
#include "absl/container/internal/unordered_map_modifiers_test.h" |
||||
#include "absl/types/any.h" |
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
namespace { |
||||
using ::absl::container_internal::hash_internal::Enum; |
||||
using ::absl::container_internal::hash_internal::EnumClass; |
||||
using ::testing::_; |
||||
using ::testing::Pair; |
||||
using ::testing::UnorderedElementsAre; |
||||
|
||||
template <class K, class V> |
||||
using Map = |
||||
flat_hash_map<K, V, StatefulTestingHash, StatefulTestingEqual, Alloc<>>; |
||||
|
||||
static_assert(!std::is_standard_layout<NonStandardLayout>(), ""); |
||||
|
||||
using MapTypes = |
||||
::testing::Types<Map<int, int>, Map<std::string, int>, Map<Enum, std::string>, |
||||
Map<EnumClass, int>, Map<int, NonStandardLayout>, |
||||
Map<NonStandardLayout, int>>; |
||||
|
||||
INSTANTIATE_TYPED_TEST_CASE_P(FlatHashMap, ConstructorTest, MapTypes); |
||||
INSTANTIATE_TYPED_TEST_CASE_P(FlatHashMap, LookupTest, MapTypes); |
||||
INSTANTIATE_TYPED_TEST_CASE_P(FlatHashMap, ModifiersTest, MapTypes); |
||||
|
||||
TEST(FlatHashMap, StandardLayout) { |
||||
struct Int { |
||||
explicit Int(size_t value) : value(value) {} |
||||
Int() : value(0) { ADD_FAILURE(); } |
||||
Int(const Int& other) : value(other.value) { ADD_FAILURE(); } |
||||
Int(Int&&) = default; |
||||
bool operator==(const Int& other) const { return value == other.value; } |
||||
size_t value; |
||||
}; |
||||
static_assert(std::is_standard_layout<Int>(), ""); |
||||
|
||||
struct Hash { |
||||
size_t operator()(const Int& obj) const { return obj.value; } |
||||
}; |
||||
|
||||
// Verify that neither the key nor the value get default-constructed or
|
||||
// copy-constructed.
|
||||
{ |
||||
flat_hash_map<Int, Int, Hash> m; |
||||
m.try_emplace(Int(1), Int(2)); |
||||
m.try_emplace(Int(3), Int(4)); |
||||
m.erase(Int(1)); |
||||
m.rehash(2 * m.bucket_count()); |
||||
} |
||||
{ |
||||
flat_hash_map<Int, Int, Hash> m; |
||||
m.try_emplace(Int(1), Int(2)); |
||||
m.try_emplace(Int(3), Int(4)); |
||||
m.erase(Int(1)); |
||||
m.clear(); |
||||
} |
||||
} |
||||
|
||||
// gcc becomes unhappy if this is inside the method, so pull it out here.
|
||||
struct balast {}; |
||||
|
||||
TEST(FlatHashMap, IteratesMsan) { |
||||
// Because SwissTable randomizes on pointer addresses, we keep old tables
|
||||
// around to ensure we don't reuse old memory.
|
||||
std::vector<absl::flat_hash_map<int, balast>> garbage; |
||||
for (int i = 0; i < 100; ++i) { |
||||
absl::flat_hash_map<int, balast> t; |
||||
for (int j = 0; j < 100; ++j) { |
||||
t[j]; |
||||
for (const auto& p : t) EXPECT_THAT(p, Pair(_, _)); |
||||
} |
||||
garbage.push_back(std::move(t)); |
||||
} |
||||
} |
||||
|
||||
// Demonstration of the "Lazy Key" pattern. This uses heterogeneous insert to
|
||||
// avoid creating expensive key elements when the item is already present in the
|
||||
// map.
|
||||
struct LazyInt { |
||||
explicit LazyInt(size_t value, int* tracker) |
||||
: value(value), tracker(tracker) {} |
||||
|
||||
explicit operator size_t() const { |
||||
++*tracker; |
||||
return value; |
||||
} |
||||
|
||||
size_t value; |
||||
int* tracker; |
||||
}; |
||||
|
||||
struct Hash { |
||||
using is_transparent = void; |
||||
int* tracker; |
||||
size_t operator()(size_t obj) const { |
||||
++*tracker; |
||||
return obj; |
||||
} |
||||
size_t operator()(const LazyInt& obj) const { |
||||
++*tracker; |
||||
return obj.value; |
||||
} |
||||
}; |
||||
|
||||
struct Eq { |
||||
using is_transparent = void; |
||||
bool operator()(size_t lhs, size_t rhs) const { |
||||
return lhs == rhs; |
||||
} |
||||
bool operator()(size_t lhs, const LazyInt& rhs) const { |
||||
return lhs == rhs.value; |
||||
} |
||||
}; |
||||
|
||||
TEST(FlatHashMap, LazyKeyPattern) { |
||||
// hashes are only guaranteed in opt mode, we use assertions to track internal
|
||||
// state that can cause extra calls to hash.
|
||||
int conversions = 0; |
||||
int hashes = 0; |
||||
flat_hash_map<size_t, size_t, Hash, Eq> m(0, Hash{&hashes}); |
||||
|
||||
m[LazyInt(1, &conversions)] = 1; |
||||
EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 1))); |
||||
EXPECT_EQ(conversions, 1); |
||||
#ifdef NDEBUG |
||||
EXPECT_EQ(hashes, 1); |
||||
#endif |
||||
|
||||
m[LazyInt(1, &conversions)] = 2; |
||||
EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2))); |
||||
EXPECT_EQ(conversions, 1); |
||||
#ifdef NDEBUG |
||||
EXPECT_EQ(hashes, 2); |
||||
#endif |
||||
|
||||
m.try_emplace(LazyInt(2, &conversions), 3); |
||||
EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2), Pair(2, 3))); |
||||
EXPECT_EQ(conversions, 2); |
||||
#ifdef NDEBUG |
||||
EXPECT_EQ(hashes, 3); |
||||
#endif |
||||
|
||||
m.try_emplace(LazyInt(2, &conversions), 4); |
||||
EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2), Pair(2, 3))); |
||||
EXPECT_EQ(conversions, 2); |
||||
#ifdef NDEBUG |
||||
EXPECT_EQ(hashes, 4); |
||||
#endif |
||||
} |
||||
|
||||
TEST(FlatHashMap, BitfieldArgument) { |
||||
union { |
||||
int n : 1; |
||||
}; |
||||
n = 0; |
||||
flat_hash_map<int, int> m; |
||||
m.erase(n); |
||||
m.count(n); |
||||
m.prefetch(n); |
||||
m.find(n); |
||||
m.contains(n); |
||||
m.equal_range(n); |
||||
m.insert_or_assign(n, n); |
||||
m.insert_or_assign(m.end(), n, n); |
||||
m.try_emplace(n); |
||||
m.try_emplace(m.end(), n); |
||||
m.at(n); |
||||
m[n]; |
||||
} |
||||
|
||||
TEST(FlatHashMap, MergeExtractInsert) { |
||||
// We can't test mutable keys, or non-copyable keys with flat_hash_map.
|
||||
// Test that the nodes have the proper API.
|
||||
absl::flat_hash_map<int, int> m = {{1, 7}, {2, 9}}; |
||||
auto node = m.extract(1); |
||||
EXPECT_TRUE(node); |
||||
EXPECT_EQ(node.key(), 1); |
||||
EXPECT_EQ(node.mapped(), 7); |
||||
EXPECT_THAT(m, UnorderedElementsAre(Pair(2, 9))); |
||||
|
||||
node.mapped() = 17; |
||||
m.insert(std::move(node)); |
||||
EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 17), Pair(2, 9))); |
||||
} |
||||
#if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__EMSCRIPTEN__) |
||||
TEST(FlatHashMap, Any) { |
||||
absl::flat_hash_map<int, absl::any> m; |
||||
m.emplace(1, 7); |
||||
auto it = m.find(1); |
||||
ASSERT_NE(it, m.end()); |
||||
EXPECT_EQ(7, absl::any_cast<int>(it->second)); |
||||
|
||||
m.emplace(std::piecewise_construct, std::make_tuple(2), std::make_tuple(8)); |
||||
it = m.find(2); |
||||
ASSERT_NE(it, m.end()); |
||||
EXPECT_EQ(8, absl::any_cast<int>(it->second)); |
||||
|
||||
m.emplace(std::piecewise_construct, std::make_tuple(3), |
||||
std::make_tuple(absl::any(9))); |
||||
it = m.find(3); |
||||
ASSERT_NE(it, m.end()); |
||||
EXPECT_EQ(9, absl::any_cast<int>(it->second)); |
||||
|
||||
struct H { |
||||
size_t operator()(const absl::any&) const { return 0; } |
||||
}; |
||||
struct E { |
||||
bool operator()(const absl::any&, const absl::any&) const { return true; } |
||||
}; |
||||
absl::flat_hash_map<absl::any, int, H, E> m2; |
||||
m2.emplace(1, 7); |
||||
auto it2 = m2.find(1); |
||||
ASSERT_NE(it2, m2.end()); |
||||
EXPECT_EQ(7, it2->second); |
||||
} |
||||
#endif // __ANDROID__
|
||||
|
||||
} // namespace
|
||||
} // namespace container_internal
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
@ -0,0 +1,491 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: flat_hash_set.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// An `absl::flat_hash_set<T>` is an unordered associative container designed to
|
||||
// be a more efficient replacement for `std::unordered_set`. Like
|
||||
// `unordered_set`, search, insertion, and deletion of set elements can be done
|
||||
// as an `O(1)` operation. However, `flat_hash_set` (and other unordered
|
||||
// associative containers known as the collection of Abseil "Swiss tables")
|
||||
// contain other optimizations that result in both memory and computation
|
||||
// advantages.
|
||||
//
|
||||
// In most cases, your default choice for a hash set should be a set of type
|
||||
// `flat_hash_set`.
|
||||
#ifndef ABSL_CONTAINER_FLAT_HASH_SET_H_ |
||||
#define ABSL_CONTAINER_FLAT_HASH_SET_H_ |
||||
|
||||
#include <type_traits> |
||||
#include <utility> |
||||
|
||||
#include "absl/algorithm/container.h" |
||||
#include "absl/base/macros.h" |
||||
#include "absl/container/internal/container_memory.h" |
||||
#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export |
||||
#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export |
||||
#include "absl/memory/memory.h" |
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
template <typename T> |
||||
struct FlatHashSetPolicy; |
||||
} // namespace container_internal
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// absl::flat_hash_set
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// An `absl::flat_hash_set<T>` is an unordered associative container which has
|
||||
// been optimized for both speed and memory footprint in most common use cases.
|
||||
// Its interface is similar to that of `std::unordered_set<T>` with the
|
||||
// following notable differences:
|
||||
//
|
||||
// * Requires keys that are CopyConstructible
|
||||
// * Supports heterogeneous lookup, through `find()`, `operator[]()` and
|
||||
// `insert()`, provided that the set is provided a compatible heterogeneous
|
||||
// hashing function and equality operator.
|
||||
// * Invalidates any references and pointers to elements within the table after
|
||||
// `rehash()`.
|
||||
// * Contains a `capacity()` member function indicating the number of element
|
||||
// slots (open, deleted, and empty) within the hash set.
|
||||
// * Returns `void` from the `erase(iterator)` overload.
|
||||
//
|
||||
// By default, `flat_hash_set` uses the `absl::Hash` hashing framework. All
|
||||
// fundamental and Abseil types that support the `absl::Hash` framework have a
|
||||
// compatible equality operator for comparing insertions into `flat_hash_map`.
|
||||
// If your type is not yet supported by the `absl::Hash` framework, see
|
||||
// absl/hash/hash.h for information on extending Abseil hashing to user-defined
|
||||
// types.
|
||||
//
|
||||
// NOTE: A `flat_hash_set` stores its keys directly inside its implementation
|
||||
// array to avoid memory indirection. Because a `flat_hash_set` is designed to
|
||||
// move data when rehashed, set keys will not retain pointer stability. If you
|
||||
// require pointer stability, consider using
|
||||
// `absl::flat_hash_set<std::unique_ptr<T>>`. If your type is not moveable and
|
||||
// you require pointer stability, consider `absl::node_hash_set` instead.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// // Create a flat hash set of three strings
|
||||
// absl::flat_hash_set<std::string> ducks =
|
||||
// {"huey", "dewey", "louie"};
|
||||
//
|
||||
// // Insert a new element into the flat hash set
|
||||
// ducks.insert("donald");
|
||||
//
|
||||
// // Force a rehash of the flat hash set
|
||||
// ducks.rehash(0);
|
||||
//
|
||||
// // See if "dewey" is present
|
||||
// if (ducks.contains("dewey")) {
|
||||
// std::cout << "We found dewey!" << std::endl;
|
||||
// }
|
||||
template <class T, class Hash = absl::container_internal::hash_default_hash<T>, |
||||
class Eq = absl::container_internal::hash_default_eq<T>, |
||||
class Allocator = std::allocator<T>> |
||||
class flat_hash_set |
||||
: public absl::container_internal::raw_hash_set< |
||||
absl::container_internal::FlatHashSetPolicy<T>, Hash, Eq, Allocator> { |
||||
using Base = typename flat_hash_set::raw_hash_set; |
||||
|
||||
public: |
||||
// Constructors and Assignment Operators
|
||||
//
|
||||
// A flat_hash_set supports the same overload set as `std::unordered_map`
|
||||
// for construction and assignment:
|
||||
//
|
||||
// * Default constructor
|
||||
//
|
||||
// // No allocation for the table's elements is made.
|
||||
// absl::flat_hash_set<std::string> set1;
|
||||
//
|
||||
// * Initializer List constructor
|
||||
//
|
||||
// absl::flat_hash_set<std::string> set2 =
|
||||
// {{"huey"}, {"dewey"}, {"louie"},};
|
||||
//
|
||||
// * Copy constructor
|
||||
//
|
||||
// absl::flat_hash_set<std::string> set3(set2);
|
||||
//
|
||||
// * Copy assignment operator
|
||||
//
|
||||
// // Hash functor and Comparator are copied as well
|
||||
// absl::flat_hash_set<std::string> set4;
|
||||
// set4 = set3;
|
||||
//
|
||||
// * Move constructor
|
||||
//
|
||||
// // Move is guaranteed efficient
|
||||
// absl::flat_hash_set<std::string> set5(std::move(set4));
|
||||
//
|
||||
// * Move assignment operator
|
||||
//
|
||||
// // May be efficient if allocators are compatible
|
||||
// absl::flat_hash_set<std::string> set6;
|
||||
// set6 = std::move(set5);
|
||||
//
|
||||
// * Range constructor
|
||||
//
|
||||
// std::vector<std::string> v = {"a", "b"};
|
||||
// absl::flat_hash_set<std::string> set7(v.begin(), v.end());
|
||||
flat_hash_set() {} |
||||
using Base::Base; |
||||
|
||||
// flat_hash_set::begin()
|
||||
//
|
||||
// Returns an iterator to the beginning of the `flat_hash_set`.
|
||||
using Base::begin; |
||||
|
||||
// flat_hash_set::cbegin()
|
||||
//
|
||||
// Returns a const iterator to the beginning of the `flat_hash_set`.
|
||||
using Base::cbegin; |
||||
|
||||
// flat_hash_set::cend()
|
||||
//
|
||||
// Returns a const iterator to the end of the `flat_hash_set`.
|
||||
using Base::cend; |
||||
|
||||
// flat_hash_set::end()
|
||||
//
|
||||
// Returns an iterator to the end of the `flat_hash_set`.
|
||||
using Base::end; |
||||
|
||||
// flat_hash_set::capacity()
|
||||
//
|
||||
// Returns the number of element slots (assigned, deleted, and empty)
|
||||
// available within the `flat_hash_set`.
|
||||
//
|
||||
// NOTE: this member function is particular to `absl::flat_hash_set` and is
|
||||
// not provided in the `std::unordered_map` API.
|
||||
using Base::capacity; |
||||
|
||||
// flat_hash_set::empty()
|
||||
//
|
||||
// Returns whether or not the `flat_hash_set` is empty.
|
||||
using Base::empty; |
||||
|
||||
// flat_hash_set::max_size()
|
||||
//
|
||||
// Returns the largest theoretical possible number of elements within a
|
||||
// `flat_hash_set` under current memory constraints. This value can be thought
|
||||
// of the largest value of `std::distance(begin(), end())` for a
|
||||
// `flat_hash_set<T>`.
|
||||
using Base::max_size; |
||||
|
||||
// flat_hash_set::size()
|
||||
//
|
||||
// Returns the number of elements currently within the `flat_hash_set`.
|
||||
using Base::size; |
||||
|
||||
// flat_hash_set::clear()
|
||||
//
|
||||
// Removes all elements from the `flat_hash_set`. Invalidates any references,
|
||||
// pointers, or iterators referring to contained elements.
|
||||
//
|
||||
// NOTE: this operation may shrink the underlying buffer. To avoid shrinking
|
||||
// the underlying buffer call `erase(begin(), end())`.
|
||||
using Base::clear; |
||||
|
||||
// flat_hash_set::erase()
|
||||
//
|
||||
// Erases elements within the `flat_hash_set`. Erasing does not trigger a
|
||||
// rehash. Overloads are listed below.
|
||||
//
|
||||
// void erase(const_iterator pos):
|
||||
//
|
||||
// Erases the element at `position` of the `flat_hash_set`, returning
|
||||
// `void`.
|
||||
//
|
||||
// NOTE: this return behavior is different than that of STL containers in
|
||||
// general and `std::unordered_map` in particular.
|
||||
//
|
||||
// iterator erase(const_iterator first, const_iterator last):
|
||||
//
|
||||
// Erases the elements in the open interval [`first`, `last`), returning an
|
||||
// iterator pointing to `last`.
|
||||
//
|
||||
// size_type erase(const key_type& key):
|
||||
//
|
||||
// Erases the element with the matching key, if it exists.
|
||||
using Base::erase; |
||||
|
||||
// flat_hash_set::insert()
|
||||
//
|
||||
// Inserts an element of the specified value into the `flat_hash_set`,
|
||||
// returning an iterator pointing to the newly inserted element, provided that
|
||||
// an element with the given key does not already exist. If rehashing occurs
|
||||
// due to the insertion, all iterators are invalidated. Overloads are listed
|
||||
// below.
|
||||
//
|
||||
// std::pair<iterator,bool> insert(const T& value):
|
||||
//
|
||||
// Inserts a value into the `flat_hash_set`. Returns a pair consisting of an
|
||||
// iterator to the inserted element (or to the element that prevented the
|
||||
// insertion) and a bool denoting whether the insertion took place.
|
||||
//
|
||||
// std::pair<iterator,bool> insert(T&& value):
|
||||
//
|
||||
// Inserts a moveable value into the `flat_hash_set`. Returns a pair
|
||||
// consisting of an iterator to the inserted element (or to the element that
|
||||
// prevented the insertion) and a bool denoting whether the insertion took
|
||||
// place.
|
||||
//
|
||||
// iterator insert(const_iterator hint, const T& value):
|
||||
// iterator insert(const_iterator hint, T&& value):
|
||||
//
|
||||
// Inserts a value, using the position of `hint` as a non-binding suggestion
|
||||
// for where to begin the insertion search. Returns an iterator to the
|
||||
// inserted element, or to the existing element that prevented the
|
||||
// insertion.
|
||||
//
|
||||
// void insert(InputIterator first, InputIterator last):
|
||||
//
|
||||
// Inserts a range of values [`first`, `last`).
|
||||
//
|
||||
// NOTE: Although the STL does not specify which element may be inserted if
|
||||
// multiple keys compare equivalently, for `flat_hash_set` we guarantee the
|
||||
// first match is inserted.
|
||||
//
|
||||
// void insert(std::initializer_list<T> ilist):
|
||||
//
|
||||
// Inserts the elements within the initializer list `ilist`.
|
||||
//
|
||||
// NOTE: Although the STL does not specify which element may be inserted if
|
||||
// multiple keys compare equivalently within the initializer list, for
|
||||
// `flat_hash_set` we guarantee the first match is inserted.
|
||||
using Base::insert; |
||||
|
||||
// flat_hash_set::emplace()
|
||||
//
|
||||
// Inserts an element of the specified value by constructing it in-place
|
||||
// within the `flat_hash_set`, provided that no element with the given key
|
||||
// already exists.
|
||||
//
|
||||
// The element may be constructed even if there already is an element with the
|
||||
// key in the container, in which case the newly constructed element will be
|
||||
// destroyed immediately.
|
||||
//
|
||||
// If rehashing occurs due to the insertion, all iterators are invalidated.
|
||||
using Base::emplace; |
||||
|
||||
// flat_hash_set::emplace_hint()
|
||||
//
|
||||
// Inserts an element of the specified value by constructing it in-place
|
||||
// within the `flat_hash_set`, using the position of `hint` as a non-binding
|
||||
// suggestion for where to begin the insertion search, and only inserts
|
||||
// provided that no element with the given key already exists.
|
||||
//
|
||||
// The element may be constructed even if there already is an element with the
|
||||
// key in the container, in which case the newly constructed element will be
|
||||
// destroyed immediately.
|
||||
//
|
||||
// If rehashing occurs due to the insertion, all iterators are invalidated.
|
||||
using Base::emplace_hint; |
||||
|
||||
// flat_hash_set::extract()
|
||||
//
|
||||
// Extracts the indicated element, erasing it in the process, and returns it
|
||||
// as a C++17-compatible node handle. Overloads are listed below.
|
||||
//
|
||||
// node_type extract(const_iterator position):
|
||||
//
|
||||
// Extracts the element at the indicated position and returns a node handle
|
||||
// owning that extracted data.
|
||||
//
|
||||
// node_type extract(const key_type& x):
|
||||
//
|
||||
// Extracts the element with the key matching the passed key value and
|
||||
// returns a node handle owning that extracted data. If the `flat_hash_set`
|
||||
// does not contain an element with a matching key, this function returns an
|
||||
// empty node handle.
|
||||
using Base::extract; |
||||
|
||||
// flat_hash_set::merge()
|
||||
//
|
||||
// Extracts elements from a given `source` flat hash map into this
|
||||
// `flat_hash_set`. If the destination `flat_hash_set` already contains an
|
||||
// element with an equivalent key, that element is not extracted.
|
||||
using Base::merge; |
||||
|
||||
// flat_hash_set::swap(flat_hash_set& other)
|
||||
//
|
||||
// Exchanges the contents of this `flat_hash_set` with those of the `other`
|
||||
// flat hash map, avoiding invocation of any move, copy, or swap operations on
|
||||
// individual elements.
|
||||
//
|
||||
// All iterators and references on the `flat_hash_set` remain valid, excepting
|
||||
// for the past-the-end iterator, which is invalidated.
|
||||
//
|
||||
// `swap()` requires that the flat hash set's hashing and key equivalence
|
||||
// functions be Swappable, and are exchaged using unqualified calls to
|
||||
// non-member `swap()`. If the map's allocator has
|
||||
// `std::allocator_traits<allocator_type>::propagate_on_container_swap::value`
|
||||
// set to `true`, the allocators are also exchanged using an unqualified call
|
||||
// to non-member `swap()`; otherwise, the allocators are not swapped.
|
||||
using Base::swap; |
||||
|
||||
// flat_hash_set::rehash(count)
|
||||
//
|
||||
// Rehashes the `flat_hash_set`, setting the number of slots to be at least
|
||||
// the passed value. If the new number of slots increases the load factor more
|
||||
// than the current maximum load factor
|
||||
// (`count` < `size()` / `max_load_factor()`), then the new number of slots
|
||||
// will be at least `size()` / `max_load_factor()`.
|
||||
//
|
||||
// To force a rehash, pass rehash(0).
|
||||
//
|
||||
// NOTE: unlike behavior in `std::unordered_set`, references are also
|
||||
// invalidated upon a `rehash()`.
|
||||
using Base::rehash; |
||||
|
||||
// flat_hash_set::reserve(count)
|
||||
//
|
||||
// Sets the number of slots in the `flat_hash_set` to the number needed to
|
||||
// accommodate at least `count` total elements without exceeding the current
|
||||
// maximum load factor, and may rehash the container if needed.
|
||||
using Base::reserve; |
||||
|
||||
// flat_hash_set::contains()
|
||||
//
|
||||
// Determines whether an element comparing equal to the given `key` exists
|
||||
// within the `flat_hash_set`, returning `true` if so or `false` otherwise.
|
||||
using Base::contains; |
||||
|
||||
// flat_hash_set::count(const Key& key) const
|
||||
//
|
||||
// Returns the number of elements comparing equal to the given `key` within
|
||||
// the `flat_hash_set`. note that this function will return either `1` or `0`
|
||||
// since duplicate elements are not allowed within a `flat_hash_set`.
|
||||
using Base::count; |
||||
|
||||
// flat_hash_set::equal_range()
|
||||
//
|
||||
// Returns a closed range [first, last], defined by a `std::pair` of two
|
||||
// iterators, containing all elements with the passed key in the
|
||||
// `flat_hash_set`.
|
||||
using Base::equal_range; |
||||
|
||||
// flat_hash_set::find()
|
||||
//
|
||||
// Finds an element with the passed `key` within the `flat_hash_set`.
|
||||
using Base::find; |
||||
|
||||
// flat_hash_set::bucket_count()
|
||||
//
|
||||
// Returns the number of "buckets" within the `flat_hash_set`. Note that
|
||||
// because a flat hash map contains all elements within its internal storage,
|
||||
// this value simply equals the current capacity of the `flat_hash_set`.
|
||||
using Base::bucket_count; |
||||
|
||||
// flat_hash_set::load_factor()
|
||||
//
|
||||
// Returns the current load factor of the `flat_hash_set` (the average number
|
||||
// of slots occupied with a value within the hash map).
|
||||
using Base::load_factor; |
||||
|
||||
// flat_hash_set::max_load_factor()
|
||||
//
|
||||
// Manages the maximum load factor of the `flat_hash_set`. Overloads are
|
||||
// listed below.
|
||||
//
|
||||
// float flat_hash_set::max_load_factor()
|
||||
//
|
||||
// Returns the current maximum load factor of the `flat_hash_set`.
|
||||
//
|
||||
// void flat_hash_set::max_load_factor(float ml)
|
||||
//
|
||||
// Sets the maximum load factor of the `flat_hash_set` to the passed value.
|
||||
//
|
||||
// NOTE: This overload is provided only for API compatibility with the STL;
|
||||
// `flat_hash_set` will ignore any set load factor and manage its rehashing
|
||||
// internally as an implementation detail.
|
||||
using Base::max_load_factor; |
||||
|
||||
// flat_hash_set::get_allocator()
|
||||
//
|
||||
// Returns the allocator function associated with this `flat_hash_set`.
|
||||
using Base::get_allocator; |
||||
|
||||
// flat_hash_set::hash_function()
|
||||
//
|
||||
// Returns the hashing function used to hash the keys within this
|
||||
// `flat_hash_set`.
|
||||
using Base::hash_function; |
||||
|
||||
// flat_hash_set::key_eq()
|
||||
//
|
||||
// Returns the function used for comparing keys equality.
|
||||
using Base::key_eq; |
||||
}; |
||||
|
||||
namespace container_internal { |
||||
|
||||
template <class T> |
||||
struct FlatHashSetPolicy { |
||||
using slot_type = T; |
||||
using key_type = T; |
||||
using init_type = T; |
||||
using constant_iterators = std::true_type; |
||||
|
||||
template <class Allocator, class... Args> |
||||
static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { |
||||
absl::allocator_traits<Allocator>::construct(*alloc, slot, |
||||
std::forward<Args>(args)...); |
||||
} |
||||
|
||||
template <class Allocator> |
||||
static void destroy(Allocator* alloc, slot_type* slot) { |
||||
absl::allocator_traits<Allocator>::destroy(*alloc, slot); |
||||
} |
||||
|
||||
template <class Allocator> |
||||
static void transfer(Allocator* alloc, slot_type* new_slot, |
||||
slot_type* old_slot) { |
||||
construct(alloc, new_slot, std::move(*old_slot)); |
||||
destroy(alloc, old_slot); |
||||
} |
||||
|
||||
static T& element(slot_type* slot) { return *slot; } |
||||
|
||||
template <class F, class... Args> |
||||
static decltype(absl::container_internal::DecomposeValue( |
||||
std::declval<F>(), std::declval<Args>()...)) |
||||
apply(F&& f, Args&&... args) { |
||||
return absl::container_internal::DecomposeValue( |
||||
std::forward<F>(f), std::forward<Args>(args)...); |
||||
} |
||||
|
||||
static size_t space_used(const T*) { return 0; } |
||||
}; |
||||
} // namespace container_internal
|
||||
|
||||
namespace container_algorithm_internal { |
||||
|
||||
// Specialization of trait in absl/algorithm/container.h
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator> |
||||
struct IsUnorderedContainer<absl::flat_hash_set<Key, Hash, KeyEqual, Allocator>> |
||||
: std::true_type {}; |
||||
|
||||
} // namespace container_algorithm_internal
|
||||
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_CONTAINER_FLAT_HASH_SET_H_
|
@ -0,0 +1,128 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/container/flat_hash_set.h" |
||||
|
||||
#include <vector> |
||||
|
||||
#include "absl/container/internal/hash_generator_testing.h" |
||||
#include "absl/container/internal/unordered_set_constructor_test.h" |
||||
#include "absl/container/internal/unordered_set_lookup_test.h" |
||||
#include "absl/container/internal/unordered_set_modifiers_test.h" |
||||
#include "absl/memory/memory.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
namespace { |
||||
|
||||
using ::absl::container_internal::hash_internal::Enum; |
||||
using ::absl::container_internal::hash_internal::EnumClass; |
||||
using ::testing::Pointee; |
||||
using ::testing::UnorderedElementsAre; |
||||
using ::testing::UnorderedElementsAreArray; |
||||
|
||||
template <class T> |
||||
using Set = |
||||
absl::flat_hash_set<T, StatefulTestingHash, StatefulTestingEqual, Alloc<T>>; |
||||
|
||||
using SetTypes = |
||||
::testing::Types<Set<int>, Set<std::string>, Set<Enum>, Set<EnumClass>>; |
||||
|
||||
INSTANTIATE_TYPED_TEST_CASE_P(FlatHashSet, ConstructorTest, SetTypes); |
||||
INSTANTIATE_TYPED_TEST_CASE_P(FlatHashSet, LookupTest, SetTypes); |
||||
INSTANTIATE_TYPED_TEST_CASE_P(FlatHashSet, ModifiersTest, SetTypes); |
||||
|
||||
TEST(FlatHashSet, EmplaceString) { |
||||
std::vector<std::string> v = {"a", "b"}; |
||||
absl::flat_hash_set<absl::string_view> hs(v.begin(), v.end()); |
||||
EXPECT_THAT(hs, UnorderedElementsAreArray(v)); |
||||
} |
||||
|
||||
TEST(FlatHashSet, BitfieldArgument) { |
||||
union { |
||||
int n : 1; |
||||
}; |
||||
n = 0; |
||||
absl::flat_hash_set<int> s = {n}; |
||||
s.insert(n); |
||||
s.insert(s.end(), n); |
||||
s.insert({n}); |
||||
s.erase(n); |
||||
s.count(n); |
||||
s.prefetch(n); |
||||
s.find(n); |
||||
s.contains(n); |
||||
s.equal_range(n); |
||||
} |
||||
|
||||
TEST(FlatHashSet, MergeExtractInsert) { |
||||
struct Hash { |
||||
size_t operator()(const std::unique_ptr<int>& p) const { return *p; } |
||||
}; |
||||
struct Eq { |
||||
bool operator()(const std::unique_ptr<int>& a, |
||||
const std::unique_ptr<int>& b) const { |
||||
return *a == *b; |
||||
} |
||||
}; |
||||
absl::flat_hash_set<std::unique_ptr<int>, Hash, Eq> set1, set2; |
||||
set1.insert(absl::make_unique<int>(7)); |
||||
set1.insert(absl::make_unique<int>(17)); |
||||
|
||||
set2.insert(absl::make_unique<int>(7)); |
||||
set2.insert(absl::make_unique<int>(19)); |
||||
|
||||
EXPECT_THAT(set1, UnorderedElementsAre(Pointee(7), Pointee(17))); |
||||
EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(19))); |
||||
|
||||
set1.merge(set2); |
||||
|
||||
EXPECT_THAT(set1, UnorderedElementsAre(Pointee(7), Pointee(17), Pointee(19))); |
||||
EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7))); |
||||
|
||||
auto node = set1.extract(absl::make_unique<int>(7)); |
||||
EXPECT_TRUE(node); |
||||
EXPECT_THAT(node.value(), Pointee(7)); |
||||
EXPECT_THAT(set1, UnorderedElementsAre(Pointee(17), Pointee(19))); |
||||
|
||||
auto insert_result = set2.insert(std::move(node)); |
||||
EXPECT_FALSE(node); |
||||
EXPECT_FALSE(insert_result.inserted); |
||||
EXPECT_TRUE(insert_result.node); |
||||
EXPECT_THAT(insert_result.node.value(), Pointee(7)); |
||||
EXPECT_EQ(**insert_result.position, 7); |
||||
EXPECT_NE(insert_result.position->get(), insert_result.node.value().get()); |
||||
EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7))); |
||||
|
||||
node = set1.extract(absl::make_unique<int>(17)); |
||||
EXPECT_TRUE(node); |
||||
EXPECT_THAT(node.value(), Pointee(17)); |
||||
EXPECT_THAT(set1, UnorderedElementsAre(Pointee(19))); |
||||
|
||||
node.value() = absl::make_unique<int>(23); |
||||
|
||||
insert_result = set2.insert(std::move(node)); |
||||
EXPECT_FALSE(node); |
||||
EXPECT_TRUE(insert_result.inserted); |
||||
EXPECT_FALSE(insert_result.node); |
||||
EXPECT_EQ(**insert_result.position, 23); |
||||
EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(23))); |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace container_internal
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,177 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Helper class to perform the Empty Base Optimization.
|
||||
// Ts can contain classes and non-classes, empty or not. For the ones that
|
||||
// are empty classes, we perform the optimization. If all types in Ts are empty
|
||||
// classes, then CompressedTuple<Ts...> is itself an empty class.
|
||||
//
|
||||
// To access the members, use member get<N>() function.
|
||||
//
|
||||
// Eg:
|
||||
// absl::container_internal::CompressedTuple<int, T1, T2, T3> value(7, t1, t2,
|
||||
// t3);
|
||||
// assert(value.get<0>() == 7);
|
||||
// T1& t1 = value.get<1>();
|
||||
// const T2& t2 = value.get<2>();
|
||||
// ...
|
||||
//
|
||||
// http://en.cppreference.com/w/cpp/language/ebo
|
||||
|
||||
#ifndef ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ |
||||
#define ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ |
||||
|
||||
#include <tuple> |
||||
#include <type_traits> |
||||
#include <utility> |
||||
|
||||
#include "absl/utility/utility.h" |
||||
|
||||
#ifdef _MSC_VER |
||||
// We need to mark these classes with this declspec to ensure that
|
||||
// CompressedTuple happens.
|
||||
#define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC __declspec(empty_bases) |
||||
#else // _MSC_VER
|
||||
#define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC |
||||
#endif // _MSC_VER
|
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
|
||||
template <typename... Ts> |
||||
class CompressedTuple; |
||||
|
||||
namespace internal_compressed_tuple { |
||||
|
||||
template <typename D, size_t I> |
||||
struct Elem; |
||||
template <typename... B, size_t I> |
||||
struct Elem<CompressedTuple<B...>, I> |
||||
: std::tuple_element<I, std::tuple<B...>> {}; |
||||
template <typename D, size_t I> |
||||
using ElemT = typename Elem<D, I>::type; |
||||
|
||||
// Use the __is_final intrinsic if available. Where it's not available, classes
|
||||
// declared with the 'final' specifier cannot be used as CompressedTuple
|
||||
// elements.
|
||||
// TODO(sbenza): Replace this with std::is_final in C++14.
|
||||
template <typename T> |
||||
constexpr bool IsFinal() { |
||||
#if defined(__clang__) || defined(__GNUC__) |
||||
return __is_final(T); |
||||
#else |
||||
return false; |
||||
#endif |
||||
} |
||||
|
||||
template <typename T> |
||||
constexpr bool ShouldUseBase() { |
||||
return std::is_class<T>::value && std::is_empty<T>::value && !IsFinal<T>(); |
||||
} |
||||
|
||||
// The storage class provides two specializations:
|
||||
// - For empty classes, it stores T as a base class.
|
||||
// - For everything else, it stores T as a member.
|
||||
template <typename D, size_t I, bool = ShouldUseBase<ElemT<D, I>>()> |
||||
struct Storage { |
||||
using T = ElemT<D, I>; |
||||
T value; |
||||
constexpr Storage() = default; |
||||
explicit constexpr Storage(T&& v) : value(absl::forward<T>(v)) {} |
||||
constexpr const T& get() const { return value; } |
||||
T& get() { return value; } |
||||
}; |
||||
|
||||
template <typename D, size_t I> |
||||
struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage<D, I, true> |
||||
: ElemT<D, I> { |
||||
using T = internal_compressed_tuple::ElemT<D, I>; |
||||
constexpr Storage() = default; |
||||
explicit constexpr Storage(T&& v) : T(absl::forward<T>(v)) {} |
||||
constexpr const T& get() const { return *this; } |
||||
T& get() { return *this; } |
||||
}; |
||||
|
||||
template <typename D, typename I> |
||||
struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl; |
||||
|
||||
template <typename... Ts, size_t... I> |
||||
struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC |
||||
CompressedTupleImpl<CompressedTuple<Ts...>, absl::index_sequence<I...>> |
||||
// We use the dummy identity function through std::integral_constant to
|
||||
// convince MSVC of accepting and expanding I in that context. Without it
|
||||
// you would get:
|
||||
// error C3548: 'I': parameter pack cannot be used in this context
|
||||
: Storage<CompressedTuple<Ts...>, |
||||
std::integral_constant<size_t, I>::value>... { |
||||
constexpr CompressedTupleImpl() = default; |
||||
explicit constexpr CompressedTupleImpl(Ts&&... args) |
||||
: Storage<CompressedTuple<Ts...>, I>(absl::forward<Ts>(args))... {} |
||||
}; |
||||
|
||||
} // namespace internal_compressed_tuple
|
||||
|
||||
// Helper class to perform the Empty Base Class Optimization.
|
||||
// Ts can contain classes and non-classes, empty or not. For the ones that
|
||||
// are empty classes, we perform the CompressedTuple. If all types in Ts are
|
||||
// empty classes, then CompressedTuple<Ts...> is itself an empty class.
|
||||
//
|
||||
// To access the members, use member .get<N>() function.
|
||||
//
|
||||
// Eg:
|
||||
// absl::container_internal::CompressedTuple<int, T1, T2, T3> value(7, t1, t2,
|
||||
// t3);
|
||||
// assert(value.get<0>() == 7);
|
||||
// T1& t1 = value.get<1>();
|
||||
// const T2& t2 = value.get<2>();
|
||||
// ...
|
||||
//
|
||||
// http://en.cppreference.com/w/cpp/language/ebo
|
||||
template <typename... Ts> |
||||
class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple |
||||
: private internal_compressed_tuple::CompressedTupleImpl< |
||||
CompressedTuple<Ts...>, absl::index_sequence_for<Ts...>> { |
||||
private: |
||||
template <int I> |
||||
using ElemT = internal_compressed_tuple::ElemT<CompressedTuple, I>; |
||||
|
||||
public: |
||||
constexpr CompressedTuple() = default; |
||||
explicit constexpr CompressedTuple(Ts... base) |
||||
: CompressedTuple::CompressedTupleImpl(absl::forward<Ts>(base)...) {} |
||||
|
||||
template <int I> |
||||
ElemT<I>& get() { |
||||
return internal_compressed_tuple::Storage<CompressedTuple, I>::get(); |
||||
} |
||||
|
||||
template <int I> |
||||
constexpr const ElemT<I>& get() const { |
||||
return internal_compressed_tuple::Storage<CompressedTuple, I>::get(); |
||||
} |
||||
}; |
||||
|
||||
// Explicit specialization for a zero-element tuple
|
||||
// (needed to avoid ambiguous overloads for the default constructor).
|
||||
template <> |
||||
class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple<> {}; |
||||
|
||||
} // namespace container_internal
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
||||
|
||||
#undef ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC |
||||
|
||||
#endif // ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_
|
@ -0,0 +1,168 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/container/internal/compressed_tuple.h" |
||||
|
||||
#include <string> |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
namespace { |
||||
|
||||
template <int> |
||||
struct Empty {}; |
||||
|
||||
template <typename T> |
||||
struct NotEmpty { |
||||
T value; |
||||
}; |
||||
|
||||
template <typename T, typename U> |
||||
struct TwoValues { |
||||
T value1; |
||||
U value2; |
||||
}; |
||||
|
||||
TEST(CompressedTupleTest, Sizeof) { |
||||
EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int>)); |
||||
EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int, Empty<0>>)); |
||||
EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int, Empty<0>, Empty<1>>)); |
||||
EXPECT_EQ(sizeof(int), |
||||
sizeof(CompressedTuple<int, Empty<0>, Empty<1>, Empty<2>>)); |
||||
|
||||
EXPECT_EQ(sizeof(TwoValues<int, double>), |
||||
sizeof(CompressedTuple<int, NotEmpty<double>>)); |
||||
EXPECT_EQ(sizeof(TwoValues<int, double>), |
||||
sizeof(CompressedTuple<int, Empty<0>, NotEmpty<double>>)); |
||||
EXPECT_EQ(sizeof(TwoValues<int, double>), |
||||
sizeof(CompressedTuple<int, Empty<0>, NotEmpty<double>, Empty<1>>)); |
||||
} |
||||
|
||||
TEST(CompressedTupleTest, Access) { |
||||
struct S { |
||||
std::string x; |
||||
}; |
||||
CompressedTuple<int, Empty<0>, S> x(7, {}, S{"ABC"}); |
||||
EXPECT_EQ(sizeof(x), sizeof(TwoValues<int, S>)); |
||||
EXPECT_EQ(7, x.get<0>()); |
||||
EXPECT_EQ("ABC", x.get<2>().x); |
||||
} |
||||
|
||||
TEST(CompressedTupleTest, NonClasses) { |
||||
CompressedTuple<int, const char*> x(7, "ABC"); |
||||
EXPECT_EQ(7, x.get<0>()); |
||||
EXPECT_STREQ("ABC", x.get<1>()); |
||||
} |
||||
|
||||
TEST(CompressedTupleTest, MixClassAndNonClass) { |
||||
CompressedTuple<int, const char*, Empty<0>, NotEmpty<double>> x(7, "ABC", {}, |
||||
{1.25}); |
||||
struct Mock { |
||||
int v; |
||||
const char* p; |
||||
double d; |
||||
}; |
||||
EXPECT_EQ(sizeof(x), sizeof(Mock)); |
||||
EXPECT_EQ(7, x.get<0>()); |
||||
EXPECT_STREQ("ABC", x.get<1>()); |
||||
EXPECT_EQ(1.25, x.get<3>().value); |
||||
} |
||||
|
||||
TEST(CompressedTupleTest, Nested) { |
||||
CompressedTuple<int, CompressedTuple<int>, |
||||
CompressedTuple<int, CompressedTuple<int>>> |
||||
x(1, CompressedTuple<int>(2), |
||||
CompressedTuple<int, CompressedTuple<int>>(3, CompressedTuple<int>(4))); |
||||
EXPECT_EQ(1, x.get<0>()); |
||||
EXPECT_EQ(2, x.get<1>().get<0>()); |
||||
EXPECT_EQ(3, x.get<2>().get<0>()); |
||||
EXPECT_EQ(4, x.get<2>().get<1>().get<0>()); |
||||
|
||||
CompressedTuple<Empty<0>, Empty<0>, |
||||
CompressedTuple<Empty<0>, CompressedTuple<Empty<0>>>> |
||||
y; |
||||
std::set<Empty<0>*> empties{&y.get<0>(), &y.get<1>(), &y.get<2>().get<0>(), |
||||
&y.get<2>().get<1>().get<0>()}; |
||||
#ifdef _MSC_VER |
||||
// MSVC has a bug where many instances of the same base class are layed out in
|
||||
// the same address when using __declspec(empty_bases).
|
||||
// This will be fixed in a future version of MSVC.
|
||||
int expected = 1; |
||||
#else |
||||
int expected = 4; |
||||
#endif |
||||
EXPECT_EQ(expected, sizeof(y)); |
||||
EXPECT_EQ(expected, empties.size()); |
||||
EXPECT_EQ(sizeof(y), sizeof(Empty<0>) * empties.size()); |
||||
|
||||
EXPECT_EQ(4 * sizeof(char), |
||||
sizeof(CompressedTuple<CompressedTuple<char, char>, |
||||
CompressedTuple<char, char>>)); |
||||
EXPECT_TRUE( |
||||
(std::is_empty<CompressedTuple<CompressedTuple<Empty<0>>, |
||||
CompressedTuple<Empty<1>>>>::value)); |
||||
} |
||||
|
||||
TEST(CompressedTupleTest, Reference) { |
||||
int i = 7; |
||||
std::string s = "Very long std::string that goes in the heap"; |
||||
CompressedTuple<int, int&, std::string, std::string&> x(i, i, s, s); |
||||
|
||||
// Sanity check. We should have not moved from `s`
|
||||
EXPECT_EQ(s, "Very long std::string that goes in the heap"); |
||||
|
||||
EXPECT_EQ(x.get<0>(), x.get<1>()); |
||||
EXPECT_NE(&x.get<0>(), &x.get<1>()); |
||||
EXPECT_EQ(&x.get<1>(), &i); |
||||
|
||||
EXPECT_EQ(x.get<2>(), x.get<3>()); |
||||
EXPECT_NE(&x.get<2>(), &x.get<3>()); |
||||
EXPECT_EQ(&x.get<3>(), &s); |
||||
} |
||||
|
||||
TEST(CompressedTupleTest, NoElements) { |
||||
CompressedTuple<> x; |
||||
static_cast<void>(x); // Silence -Wunused-variable.
|
||||
EXPECT_TRUE(std::is_empty<CompressedTuple<>>::value); |
||||
} |
||||
|
||||
TEST(CompressedTupleTest, Constexpr) { |
||||
constexpr CompressedTuple<int, double, CompressedTuple<int>> x( |
||||
7, 1.25, CompressedTuple<int>(5)); |
||||
constexpr int x0 = x.get<0>(); |
||||
constexpr double x1 = x.get<1>(); |
||||
constexpr int x2 = x.get<2>().get<0>(); |
||||
EXPECT_EQ(x0, 7); |
||||
EXPECT_EQ(x1, 1.25); |
||||
EXPECT_EQ(x2, 5); |
||||
} |
||||
|
||||
#if defined(__clang__) || defined(__GNUC__) |
||||
TEST(CompressedTupleTest, EmptyFinalClass) { |
||||
struct S final { |
||||
int f() const { return 5; } |
||||
}; |
||||
CompressedTuple<S> x; |
||||
EXPECT_EQ(x.get<0>().f(), 5); |
||||
} |
||||
#endif |
||||
|
||||
} // namespace
|
||||
} // namespace container_internal
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
@ -0,0 +1,407 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ |
||||
#define ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ |
||||
|
||||
#ifdef ADDRESS_SANITIZER |
||||
#include <sanitizer/asan_interface.h> |
||||
#endif |
||||
|
||||
#ifdef MEMORY_SANITIZER |
||||
#include <sanitizer/msan_interface.h> |
||||
#endif |
||||
|
||||
#include <cassert> |
||||
#include <cstddef> |
||||
#include <memory> |
||||
#include <tuple> |
||||
#include <type_traits> |
||||
#include <utility> |
||||
|
||||
#include "absl/memory/memory.h" |
||||
#include "absl/utility/utility.h" |
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
|
||||
// Allocates at least n bytes aligned to the specified alignment.
|
||||
// Alignment must be a power of 2. It must be positive.
|
||||
//
|
||||
// Note that many allocators don't honor alignment requirements above certain
|
||||
// threshold (usually either alignof(std::max_align_t) or alignof(void*)).
|
||||
// Allocate() doesn't apply alignment corrections. If the underlying allocator
|
||||
// returns insufficiently alignment pointer, that's what you are going to get.
|
||||
template <size_t Alignment, class Alloc> |
||||
void* Allocate(Alloc* alloc, size_t n) { |
||||
static_assert(Alignment > 0, ""); |
||||
assert(n && "n must be positive"); |
||||
struct alignas(Alignment) M {}; |
||||
using A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>; |
||||
using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>; |
||||
A mem_alloc(*alloc); |
||||
void* p = AT::allocate(mem_alloc, (n + sizeof(M) - 1) / sizeof(M)); |
||||
assert(reinterpret_cast<uintptr_t>(p) % Alignment == 0 && |
||||
"allocator does not respect alignment"); |
||||
return p; |
||||
} |
||||
|
||||
// The pointer must have been previously obtained by calling
|
||||
// Allocate<Alignment>(alloc, n).
|
||||
template <size_t Alignment, class Alloc> |
||||
void Deallocate(Alloc* alloc, void* p, size_t n) { |
||||
static_assert(Alignment > 0, ""); |
||||
assert(n && "n must be positive"); |
||||
struct alignas(Alignment) M {}; |
||||
using A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>; |
||||
using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>; |
||||
A mem_alloc(*alloc); |
||||
AT::deallocate(mem_alloc, static_cast<M*>(p), |
||||
(n + sizeof(M) - 1) / sizeof(M)); |
||||
} |
||||
|
||||
namespace memory_internal { |
||||
|
||||
// Constructs T into uninitialized storage pointed by `ptr` using the args
|
||||
// specified in the tuple.
|
||||
template <class Alloc, class T, class Tuple, size_t... I> |
||||
void ConstructFromTupleImpl(Alloc* alloc, T* ptr, Tuple&& t, |
||||
absl::index_sequence<I...>) { |
||||
absl::allocator_traits<Alloc>::construct( |
||||
*alloc, ptr, std::get<I>(std::forward<Tuple>(t))...); |
||||
} |
||||
|
||||
template <class T, class F> |
||||
struct WithConstructedImplF { |
||||
template <class... Args> |
||||
decltype(std::declval<F>()(std::declval<T>())) operator()( |
||||
Args&&... args) const { |
||||
return std::forward<F>(f)(T(std::forward<Args>(args)...)); |
||||
} |
||||
F&& f; |
||||
}; |
||||
|
||||
template <class T, class Tuple, size_t... Is, class F> |
||||
decltype(std::declval<F>()(std::declval<T>())) WithConstructedImpl( |
||||
Tuple&& t, absl::index_sequence<Is...>, F&& f) { |
||||
return WithConstructedImplF<T, F>{std::forward<F>(f)}( |
||||
std::get<Is>(std::forward<Tuple>(t))...); |
||||
} |
||||
|
||||
template <class T, size_t... Is> |
||||
auto TupleRefImpl(T&& t, absl::index_sequence<Is...>) |
||||
-> decltype(std::forward_as_tuple(std::get<Is>(std::forward<T>(t))...)) { |
||||
return std::forward_as_tuple(std::get<Is>(std::forward<T>(t))...); |
||||
} |
||||
|
||||
// Returns a tuple of references to the elements of the input tuple. T must be a
|
||||
// tuple.
|
||||
template <class T> |
||||
auto TupleRef(T&& t) -> decltype( |
||||
TupleRefImpl(std::forward<T>(t), |
||||
absl::make_index_sequence< |
||||
std::tuple_size<typename std::decay<T>::type>::value>())) { |
||||
return TupleRefImpl( |
||||
std::forward<T>(t), |
||||
absl::make_index_sequence< |
||||
std::tuple_size<typename std::decay<T>::type>::value>()); |
||||
} |
||||
|
||||
template <class F, class K, class V> |
||||
decltype(std::declval<F>()(std::declval<const K&>(), std::piecewise_construct, |
||||
std::declval<std::tuple<K>>(), std::declval<V>())) |
||||
DecomposePairImpl(F&& f, std::pair<std::tuple<K>, V> p) { |
||||
const auto& key = std::get<0>(p.first); |
||||
return std::forward<F>(f)(key, std::piecewise_construct, std::move(p.first), |
||||
std::move(p.second)); |
||||
} |
||||
|
||||
} // namespace memory_internal
|
||||
|
||||
// Constructs T into uninitialized storage pointed by `ptr` using the args
|
||||
// specified in the tuple.
|
||||
template <class Alloc, class T, class Tuple> |
||||
void ConstructFromTuple(Alloc* alloc, T* ptr, Tuple&& t) { |
||||
memory_internal::ConstructFromTupleImpl( |
||||
alloc, ptr, std::forward<Tuple>(t), |
||||
absl::make_index_sequence< |
||||
std::tuple_size<typename std::decay<Tuple>::type>::value>()); |
||||
} |
||||
|
||||
// Constructs T using the args specified in the tuple and calls F with the
|
||||
// constructed value.
|
||||
template <class T, class Tuple, class F> |
||||
decltype(std::declval<F>()(std::declval<T>())) WithConstructed( |
||||
Tuple&& t, F&& f) { |
||||
return memory_internal::WithConstructedImpl<T>( |
||||
std::forward<Tuple>(t), |
||||
absl::make_index_sequence< |
||||
std::tuple_size<typename std::decay<Tuple>::type>::value>(), |
||||
std::forward<F>(f)); |
||||
} |
||||
|
||||
// Given arguments of an std::pair's consructor, PairArgs() returns a pair of
|
||||
// tuples with references to the passed arguments. The tuples contain
|
||||
// constructor arguments for the first and the second elements of the pair.
|
||||
//
|
||||
// The following two snippets are equivalent.
|
||||
//
|
||||
// 1. std::pair<F, S> p(args...);
|
||||
//
|
||||
// 2. auto a = PairArgs(args...);
|
||||
// std::pair<F, S> p(std::piecewise_construct,
|
||||
// std::move(p.first), std::move(p.second));
|
||||
inline std::pair<std::tuple<>, std::tuple<>> PairArgs() { return {}; } |
||||
template <class F, class S> |
||||
std::pair<std::tuple<F&&>, std::tuple<S&&>> PairArgs(F&& f, S&& s) { |
||||
return {std::piecewise_construct, std::forward_as_tuple(std::forward<F>(f)), |
||||
std::forward_as_tuple(std::forward<S>(s))}; |
||||
} |
||||
template <class F, class S> |
||||
std::pair<std::tuple<const F&>, std::tuple<const S&>> PairArgs( |
||||
const std::pair<F, S>& p) { |
||||
return PairArgs(p.first, p.second); |
||||
} |
||||
template <class F, class S> |
||||
std::pair<std::tuple<F&&>, std::tuple<S&&>> PairArgs(std::pair<F, S>&& p) { |
||||
return PairArgs(std::forward<F>(p.first), std::forward<S>(p.second)); |
||||
} |
||||
template <class F, class S> |
||||
auto PairArgs(std::piecewise_construct_t, F&& f, S&& s) |
||||
-> decltype(std::make_pair(memory_internal::TupleRef(std::forward<F>(f)), |
||||
memory_internal::TupleRef(std::forward<S>(s)))) { |
||||
return std::make_pair(memory_internal::TupleRef(std::forward<F>(f)), |
||||
memory_internal::TupleRef(std::forward<S>(s))); |
||||
} |
||||
|
||||
// A helper function for implementing apply() in map policies.
|
||||
template <class F, class... Args> |
||||
auto DecomposePair(F&& f, Args&&... args) |
||||
-> decltype(memory_internal::DecomposePairImpl( |
||||
std::forward<F>(f), PairArgs(std::forward<Args>(args)...))) { |
||||
return memory_internal::DecomposePairImpl( |
||||
std::forward<F>(f), PairArgs(std::forward<Args>(args)...)); |
||||
} |
||||
|
||||
// A helper function for implementing apply() in set policies.
|
||||
template <class F, class Arg> |
||||
decltype(std::declval<F>()(std::declval<const Arg&>(), std::declval<Arg>())) |
||||
DecomposeValue(F&& f, Arg&& arg) { |
||||
const auto& key = arg; |
||||
return std::forward<F>(f)(key, std::forward<Arg>(arg)); |
||||
} |
||||
|
||||
// Helper functions for asan and msan.
|
||||
inline void SanitizerPoisonMemoryRegion(const void* m, size_t s) { |
||||
#ifdef ADDRESS_SANITIZER |
||||
ASAN_POISON_MEMORY_REGION(m, s); |
||||
#endif |
||||
#ifdef MEMORY_SANITIZER |
||||
__msan_poison(m, s); |
||||
#endif |
||||
(void)m; |
||||
(void)s; |
||||
} |
||||
|
||||
inline void SanitizerUnpoisonMemoryRegion(const void* m, size_t s) { |
||||
#ifdef ADDRESS_SANITIZER |
||||
ASAN_UNPOISON_MEMORY_REGION(m, s); |
||||
#endif |
||||
#ifdef MEMORY_SANITIZER |
||||
__msan_unpoison(m, s); |
||||
#endif |
||||
(void)m; |
||||
(void)s; |
||||
} |
||||
|
||||
template <typename T> |
||||
inline void SanitizerPoisonObject(const T* object) { |
||||
SanitizerPoisonMemoryRegion(object, sizeof(T)); |
||||
} |
||||
|
||||
template <typename T> |
||||
inline void SanitizerUnpoisonObject(const T* object) { |
||||
SanitizerUnpoisonMemoryRegion(object, sizeof(T)); |
||||
} |
||||
|
||||
namespace memory_internal { |
||||
|
||||
// If Pair is a standard-layout type, OffsetOf<Pair>::kFirst and
|
||||
// OffsetOf<Pair>::kSecond are equivalent to offsetof(Pair, first) and
|
||||
// offsetof(Pair, second) respectively. Otherwise they are -1.
|
||||
//
|
||||
// The purpose of OffsetOf is to avoid calling offsetof() on non-standard-layout
|
||||
// type, which is non-portable.
|
||||
template <class Pair, class = std::true_type> |
||||
struct OffsetOf { |
||||
static constexpr size_t kFirst = -1; |
||||
static constexpr size_t kSecond = -1; |
||||
}; |
||||
|
||||
template <class Pair> |
||||
struct OffsetOf<Pair, typename std::is_standard_layout<Pair>::type> { |
||||
static constexpr size_t kFirst = offsetof(Pair, first); |
||||
static constexpr size_t kSecond = offsetof(Pair, second); |
||||
}; |
||||
|
||||
template <class K, class V> |
||||
struct IsLayoutCompatible { |
||||
private: |
||||
struct Pair { |
||||
K first; |
||||
V second; |
||||
}; |
||||
|
||||
// Is P layout-compatible with Pair?
|
||||
template <class P> |
||||
static constexpr bool LayoutCompatible() { |
||||
return std::is_standard_layout<P>() && sizeof(P) == sizeof(Pair) && |
||||
alignof(P) == alignof(Pair) && |
||||
memory_internal::OffsetOf<P>::kFirst == |
||||
memory_internal::OffsetOf<Pair>::kFirst && |
||||
memory_internal::OffsetOf<P>::kSecond == |
||||
memory_internal::OffsetOf<Pair>::kSecond; |
||||
} |
||||
|
||||
public: |
||||
// Whether pair<const K, V> and pair<K, V> are layout-compatible. If they are,
|
||||
// then it is safe to store them in a union and read from either.
|
||||
static constexpr bool value = std::is_standard_layout<K>() && |
||||
std::is_standard_layout<Pair>() && |
||||
memory_internal::OffsetOf<Pair>::kFirst == 0 && |
||||
LayoutCompatible<std::pair<K, V>>() && |
||||
LayoutCompatible<std::pair<const K, V>>(); |
||||
}; |
||||
|
||||
} // namespace memory_internal
|
||||
|
||||
// If kMutableKeys is false, only the value member is accessed.
|
||||
//
|
||||
// If kMutableKeys is true, key is accessed through all slots while value and
|
||||
// mutable_value are accessed only via INITIALIZED slots. Slots are created and
|
||||
// destroyed via mutable_value so that the key can be moved later.
|
||||
template <class K, class V> |
||||
union slot_type { |
||||
private: |
||||
static void emplace(slot_type* slot) { |
||||
// The construction of union doesn't do anything at runtime but it allows us
|
||||
// to access its members without violating aliasing rules.
|
||||
new (slot) slot_type; |
||||
} |
||||
// If pair<const K, V> and pair<K, V> are layout-compatible, we can accept one
|
||||
// or the other via slot_type. We are also free to access the key via
|
||||
// slot_type::key in this case.
|
||||
using kMutableKeys = |
||||
std::integral_constant<bool, |
||||
memory_internal::IsLayoutCompatible<K, V>::value>; |
||||
|
||||
public: |
||||
slot_type() {} |
||||
~slot_type() = delete; |
||||
using value_type = std::pair<const K, V>; |
||||
using mutable_value_type = std::pair<K, V>; |
||||
|
||||
value_type value; |
||||
mutable_value_type mutable_value; |
||||
K key; |
||||
|
||||
template <class Allocator, class... Args> |
||||
static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { |
||||
emplace(slot); |
||||
if (kMutableKeys::value) { |
||||
absl::allocator_traits<Allocator>::construct(*alloc, &slot->mutable_value, |
||||
std::forward<Args>(args)...); |
||||
} else { |
||||
absl::allocator_traits<Allocator>::construct(*alloc, &slot->value, |
||||
std::forward<Args>(args)...); |
||||
} |
||||
} |
||||
|
||||
// Construct this slot by moving from another slot.
|
||||
template <class Allocator> |
||||
static void construct(Allocator* alloc, slot_type* slot, slot_type* other) { |
||||
emplace(slot); |
||||
if (kMutableKeys::value) { |
||||
absl::allocator_traits<Allocator>::construct( |
||||
*alloc, &slot->mutable_value, std::move(other->mutable_value)); |
||||
} else { |
||||
absl::allocator_traits<Allocator>::construct(*alloc, &slot->value, |
||||
std::move(other->value)); |
||||
} |
||||
} |
||||
|
||||
template <class Allocator> |
||||
static void destroy(Allocator* alloc, slot_type* slot) { |
||||
if (kMutableKeys::value) { |
||||
absl::allocator_traits<Allocator>::destroy(*alloc, &slot->mutable_value); |
||||
} else { |
||||
absl::allocator_traits<Allocator>::destroy(*alloc, &slot->value); |
||||
} |
||||
} |
||||
|
||||
template <class Allocator> |
||||
static void transfer(Allocator* alloc, slot_type* new_slot, |
||||
slot_type* old_slot) { |
||||
emplace(new_slot); |
||||
if (kMutableKeys::value) { |
||||
absl::allocator_traits<Allocator>::construct( |
||||
*alloc, &new_slot->mutable_value, std::move(old_slot->mutable_value)); |
||||
} else { |
||||
absl::allocator_traits<Allocator>::construct(*alloc, &new_slot->value, |
||||
std::move(old_slot->value)); |
||||
} |
||||
destroy(alloc, old_slot); |
||||
} |
||||
|
||||
template <class Allocator> |
||||
static void swap(Allocator* alloc, slot_type* a, slot_type* b) { |
||||
if (kMutableKeys::value) { |
||||
using std::swap; |
||||
swap(a->mutable_value, b->mutable_value); |
||||
} else { |
||||
value_type tmp = std::move(a->value); |
||||
absl::allocator_traits<Allocator>::destroy(*alloc, &a->value); |
||||
absl::allocator_traits<Allocator>::construct(*alloc, &a->value, |
||||
std::move(b->value)); |
||||
absl::allocator_traits<Allocator>::destroy(*alloc, &b->value); |
||||
absl::allocator_traits<Allocator>::construct(*alloc, &b->value, |
||||
std::move(tmp)); |
||||
} |
||||
} |
||||
|
||||
template <class Allocator> |
||||
static void move(Allocator* alloc, slot_type* src, slot_type* dest) { |
||||
if (kMutableKeys::value) { |
||||
dest->mutable_value = std::move(src->mutable_value); |
||||
} else { |
||||
absl::allocator_traits<Allocator>::destroy(*alloc, &dest->value); |
||||
absl::allocator_traits<Allocator>::construct(*alloc, &dest->value, |
||||
std::move(src->value)); |
||||
} |
||||
} |
||||
|
||||
template <class Allocator> |
||||
static void move(Allocator* alloc, slot_type* first, slot_type* last, |
||||
slot_type* result) { |
||||
for (slot_type *src = first, *dest = result; src != last; ++src, ++dest) |
||||
move(alloc, src, dest); |
||||
} |
||||
}; |
||||
|
||||
} // namespace container_internal
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_
|
@ -0,0 +1,190 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/container/internal/container_memory.h" |
||||
|
||||
#include <cstdint> |
||||
#include <tuple> |
||||
#include <utility> |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
namespace { |
||||
|
||||
using ::testing::Pair; |
||||
|
||||
TEST(Memory, AlignmentLargerThanBase) { |
||||
std::allocator<int8_t> alloc; |
||||
void* mem = Allocate<2>(&alloc, 3); |
||||
EXPECT_EQ(0, reinterpret_cast<uintptr_t>(mem) % 2); |
||||
memcpy(mem, "abc", 3); |
||||
Deallocate<2>(&alloc, mem, 3); |
||||
} |
||||
|
||||
TEST(Memory, AlignmentSmallerThanBase) { |
||||
std::allocator<int64_t> alloc; |
||||
void* mem = Allocate<2>(&alloc, 3); |
||||
EXPECT_EQ(0, reinterpret_cast<uintptr_t>(mem) % 2); |
||||
memcpy(mem, "abc", 3); |
||||
Deallocate<2>(&alloc, mem, 3); |
||||
} |
||||
|
||||
class Fixture : public ::testing::Test { |
||||
using Alloc = std::allocator<std::string>; |
||||
|
||||
public: |
||||
Fixture() { ptr_ = std::allocator_traits<Alloc>::allocate(*alloc(), 1); } |
||||
~Fixture() override { |
||||
std::allocator_traits<Alloc>::destroy(*alloc(), ptr_); |
||||
std::allocator_traits<Alloc>::deallocate(*alloc(), ptr_, 1); |
||||
} |
||||
std::string* ptr() { return ptr_; } |
||||
Alloc* alloc() { return &alloc_; } |
||||
|
||||
private: |
||||
Alloc alloc_; |
||||
std::string* ptr_; |
||||
}; |
||||
|
||||
TEST_F(Fixture, ConstructNoArgs) { |
||||
ConstructFromTuple(alloc(), ptr(), std::forward_as_tuple()); |
||||
EXPECT_EQ(*ptr(), ""); |
||||
} |
||||
|
||||
TEST_F(Fixture, ConstructOneArg) { |
||||
ConstructFromTuple(alloc(), ptr(), std::forward_as_tuple("abcde")); |
||||
EXPECT_EQ(*ptr(), "abcde"); |
||||
} |
||||
|
||||
TEST_F(Fixture, ConstructTwoArg) { |
||||
ConstructFromTuple(alloc(), ptr(), std::forward_as_tuple(5, 'a')); |
||||
EXPECT_EQ(*ptr(), "aaaaa"); |
||||
} |
||||
|
||||
TEST(PairArgs, NoArgs) { |
||||
EXPECT_THAT(PairArgs(), |
||||
Pair(std::forward_as_tuple(), std::forward_as_tuple())); |
||||
} |
||||
|
||||
TEST(PairArgs, TwoArgs) { |
||||
EXPECT_EQ( |
||||
std::make_pair(std::forward_as_tuple(1), std::forward_as_tuple('A')), |
||||
PairArgs(1, 'A')); |
||||
} |
||||
|
||||
TEST(PairArgs, Pair) { |
||||
EXPECT_EQ( |
||||
std::make_pair(std::forward_as_tuple(1), std::forward_as_tuple('A')), |
||||
PairArgs(std::make_pair(1, 'A'))); |
||||
} |
||||
|
||||
TEST(PairArgs, Piecewise) { |
||||
EXPECT_EQ( |
||||
std::make_pair(std::forward_as_tuple(1), std::forward_as_tuple('A')), |
||||
PairArgs(std::piecewise_construct, std::forward_as_tuple(1), |
||||
std::forward_as_tuple('A'))); |
||||
} |
||||
|
||||
TEST(WithConstructed, Simple) { |
||||
EXPECT_EQ(1, WithConstructed<absl::string_view>( |
||||
std::make_tuple(std::string("a")), |
||||
[](absl::string_view str) { return str.size(); })); |
||||
} |
||||
|
||||
template <class F, class Arg> |
||||
decltype(DecomposeValue(std::declval<F>(), std::declval<Arg>())) |
||||
DecomposeValueImpl(int, F&& f, Arg&& arg) { |
||||
return DecomposeValue(std::forward<F>(f), std::forward<Arg>(arg)); |
||||
} |
||||
|
||||
template <class F, class Arg> |
||||
const char* DecomposeValueImpl(char, F&& f, Arg&& arg) { |
||||
return "not decomposable"; |
||||
} |
||||
|
||||
template <class F, class Arg> |
||||
decltype(DecomposeValueImpl(0, std::declval<F>(), std::declval<Arg>())) |
||||
TryDecomposeValue(F&& f, Arg&& arg) { |
||||
return DecomposeValueImpl(0, std::forward<F>(f), std::forward<Arg>(arg)); |
||||
} |
||||
|
||||
TEST(DecomposeValue, Decomposable) { |
||||
auto f = [](const int& x, int&& y) { |
||||
EXPECT_EQ(&x, &y); |
||||
EXPECT_EQ(42, x); |
||||
return 'A'; |
||||
}; |
||||
EXPECT_EQ('A', TryDecomposeValue(f, 42)); |
||||
} |
||||
|
||||
TEST(DecomposeValue, NotDecomposable) { |
||||
auto f = [](void*) { |
||||
ADD_FAILURE() << "Must not be called"; |
||||
return 'A'; |
||||
}; |
||||
EXPECT_STREQ("not decomposable", TryDecomposeValue(f, 42)); |
||||
} |
||||
|
||||
template <class F, class... Args> |
||||
decltype(DecomposePair(std::declval<F>(), std::declval<Args>()...)) |
||||
DecomposePairImpl(int, F&& f, Args&&... args) { |
||||
return DecomposePair(std::forward<F>(f), std::forward<Args>(args)...); |
||||
} |
||||
|
||||
template <class F, class... Args> |
||||
const char* DecomposePairImpl(char, F&& f, Args&&... args) { |
||||
return "not decomposable"; |
||||
} |
||||
|
||||
template <class F, class... Args> |
||||
decltype(DecomposePairImpl(0, std::declval<F>(), std::declval<Args>()...)) |
||||
TryDecomposePair(F&& f, Args&&... args) { |
||||
return DecomposePairImpl(0, std::forward<F>(f), std::forward<Args>(args)...); |
||||
} |
||||
|
||||
TEST(DecomposePair, Decomposable) { |
||||
auto f = [](const int& x, std::piecewise_construct_t, std::tuple<int&&> k, |
||||
std::tuple<double>&& v) { |
||||
EXPECT_EQ(&x, &std::get<0>(k)); |
||||
EXPECT_EQ(42, x); |
||||
EXPECT_EQ(0.5, std::get<0>(v)); |
||||
return 'A'; |
||||
}; |
||||
EXPECT_EQ('A', TryDecomposePair(f, 42, 0.5)); |
||||
EXPECT_EQ('A', TryDecomposePair(f, std::make_pair(42, 0.5))); |
||||
EXPECT_EQ('A', TryDecomposePair(f, std::piecewise_construct, |
||||
std::make_tuple(42), std::make_tuple(0.5))); |
||||
} |
||||
|
||||
TEST(DecomposePair, NotDecomposable) { |
||||
auto f = [](...) { |
||||
ADD_FAILURE() << "Must not be called"; |
||||
return 'A'; |
||||
}; |
||||
EXPECT_STREQ("not decomposable", |
||||
TryDecomposePair(f)); |
||||
EXPECT_STREQ("not decomposable", |
||||
TryDecomposePair(f, std::piecewise_construct, std::make_tuple(), |
||||
std::make_tuple(0.5))); |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace container_internal
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
@ -0,0 +1,145 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Define the default Hash and Eq functions for SwissTable containers.
|
||||
//
|
||||
// std::hash<T> and std::equal_to<T> are not appropriate hash and equal
|
||||
// functions for SwissTable containers. There are two reasons for this.
|
||||
//
|
||||
// SwissTable containers are power of 2 sized containers:
|
||||
//
|
||||
// This means they use the lower bits of the hash value to find the slot for
|
||||
// each entry. The typical hash function for integral types is the identity.
|
||||
// This is a very weak hash function for SwissTable and any power of 2 sized
|
||||
// hashtable implementation which will lead to excessive collisions. For
|
||||
// SwissTable we use murmur3 style mixing to reduce collisions to a minimum.
|
||||
//
|
||||
// SwissTable containers support heterogeneous lookup:
|
||||
//
|
||||
// In order to make heterogeneous lookup work, hash and equal functions must be
|
||||
// polymorphic. At the same time they have to satisfy the same requirements the
|
||||
// C++ standard imposes on hash functions and equality operators. That is:
|
||||
//
|
||||
// if hash_default_eq<T>(a, b) returns true for any a and b of type T, then
|
||||
// hash_default_hash<T>(a) must equal hash_default_hash<T>(b)
|
||||
//
|
||||
// For SwissTable containers this requirement is relaxed to allow a and b of
|
||||
// any and possibly different types. Note that like the standard the hash and
|
||||
// equal functions are still bound to T. This is important because some type U
|
||||
// can be hashed by/tested for equality differently depending on T. A notable
|
||||
// example is `const char*`. `const char*` is treated as a c-style string when
|
||||
// the hash function is hash<string> but as a pointer when the hash function is
|
||||
// hash<void*>.
|
||||
//
|
||||
#ifndef ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ |
||||
#define ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ |
||||
|
||||
#include <stdint.h> |
||||
#include <cstddef> |
||||
#include <memory> |
||||
#include <string> |
||||
#include <type_traits> |
||||
|
||||
#include "absl/base/config.h" |
||||
#include "absl/hash/hash.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
|
||||
// The hash of an object of type T is computed by using absl::Hash.
|
||||
template <class T, class E = void> |
||||
struct HashEq { |
||||
using Hash = absl::Hash<T>; |
||||
using Eq = std::equal_to<T>; |
||||
}; |
||||
|
||||
struct StringHash { |
||||
using is_transparent = void; |
||||
|
||||
size_t operator()(absl::string_view v) const { |
||||
return absl::Hash<absl::string_view>{}(v); |
||||
} |
||||
}; |
||||
|
||||
// Supports heterogeneous lookup for string-like elements.
|
||||
struct StringHashEq { |
||||
using Hash = StringHash; |
||||
struct Eq { |
||||
using is_transparent = void; |
||||
bool operator()(absl::string_view lhs, absl::string_view rhs) const { |
||||
return lhs == rhs; |
||||
} |
||||
}; |
||||
}; |
||||
template <> |
||||
struct HashEq<std::string> : StringHashEq {}; |
||||
template <> |
||||
struct HashEq<absl::string_view> : StringHashEq {}; |
||||
|
||||
// Supports heterogeneous lookup for pointers and smart pointers.
|
||||
template <class T> |
||||
struct HashEq<T*> { |
||||
struct Hash { |
||||
using is_transparent = void; |
||||
template <class U> |
||||
size_t operator()(const U& ptr) const { |
||||
return absl::Hash<const T*>{}(HashEq::ToPtr(ptr)); |
||||
} |
||||
}; |
||||
struct Eq { |
||||
using is_transparent = void; |
||||
template <class A, class B> |
||||
bool operator()(const A& a, const B& b) const { |
||||
return HashEq::ToPtr(a) == HashEq::ToPtr(b); |
||||
} |
||||
}; |
||||
|
||||
private: |
||||
static const T* ToPtr(const T* ptr) { return ptr; } |
||||
template <class U, class D> |
||||
static const T* ToPtr(const std::unique_ptr<U, D>& ptr) { |
||||
return ptr.get(); |
||||
} |
||||
template <class U> |
||||
static const T* ToPtr(const std::shared_ptr<U>& ptr) { |
||||
return ptr.get(); |
||||
} |
||||
}; |
||||
|
||||
template <class T, class D> |
||||
struct HashEq<std::unique_ptr<T, D>> : HashEq<T*> {}; |
||||
template <class T> |
||||
struct HashEq<std::shared_ptr<T>> : HashEq<T*> {}; |
||||
|
||||
// This header's visibility is restricted. If you need to access the default
|
||||
// hasher please use the container's ::hasher alias instead.
|
||||
//
|
||||
// Example: typename Hash = typename absl::flat_hash_map<K, V>::hasher
|
||||
template <class T> |
||||
using hash_default_hash = typename container_internal::HashEq<T>::Hash; |
||||
|
||||
// This header's visibility is restricted. If you need to access the default
|
||||
// key equal please use the container's ::key_equal alias instead.
|
||||
//
|
||||
// Example: typename Eq = typename absl::flat_hash_map<K, V, Hash>::key_equal
|
||||
template <class T> |
||||
using hash_default_eq = typename container_internal::HashEq<T>::Eq; |
||||
|
||||
} // namespace container_internal
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_
|
@ -0,0 +1,303 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/container/internal/hash_function_defaults.h" |
||||
|
||||
#include <functional> |
||||
#include <type_traits> |
||||
#include <utility> |
||||
|
||||
#include "gtest/gtest.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
namespace { |
||||
|
||||
using ::testing::Types; |
||||
|
||||
TEST(Eq, Int32) { |
||||
hash_default_eq<int32_t> eq; |
||||
EXPECT_TRUE(eq(1, 1u)); |
||||
EXPECT_TRUE(eq(1, char{1})); |
||||
EXPECT_TRUE(eq(1, true)); |
||||
EXPECT_TRUE(eq(1, double{1.1})); |
||||
EXPECT_FALSE(eq(1, char{2})); |
||||
EXPECT_FALSE(eq(1, 2u)); |
||||
EXPECT_FALSE(eq(1, false)); |
||||
EXPECT_FALSE(eq(1, 2.)); |
||||
} |
||||
|
||||
TEST(Hash, Int32) { |
||||
hash_default_hash<int32_t> hash; |
||||
auto h = hash(1); |
||||
EXPECT_EQ(h, hash(1u)); |
||||
EXPECT_EQ(h, hash(char{1})); |
||||
EXPECT_EQ(h, hash(true)); |
||||
EXPECT_EQ(h, hash(double{1.1})); |
||||
EXPECT_NE(h, hash(2u)); |
||||
EXPECT_NE(h, hash(char{2})); |
||||
EXPECT_NE(h, hash(false)); |
||||
EXPECT_NE(h, hash(2.)); |
||||
} |
||||
|
||||
enum class MyEnum { A, B, C, D }; |
||||
|
||||
TEST(Eq, Enum) { |
||||
hash_default_eq<MyEnum> eq; |
||||
EXPECT_TRUE(eq(MyEnum::A, MyEnum::A)); |
||||
EXPECT_FALSE(eq(MyEnum::A, MyEnum::B)); |
||||
} |
||||
|
||||
TEST(Hash, Enum) { |
||||
hash_default_hash<MyEnum> hash; |
||||
|
||||
for (MyEnum e : {MyEnum::A, MyEnum::B, MyEnum::C}) { |
||||
auto h = hash(e); |
||||
EXPECT_EQ(h, hash_default_hash<int>{}(static_cast<int>(e))); |
||||
EXPECT_NE(h, hash(MyEnum::D)); |
||||
} |
||||
} |
||||
|
||||
using StringTypes = ::testing::Types<std::string, absl::string_view>; |
||||
|
||||
template <class T> |
||||
struct EqString : ::testing::Test { |
||||
hash_default_eq<T> key_eq; |
||||
}; |
||||
|
||||
TYPED_TEST_CASE(EqString, StringTypes); |
||||
|
||||
template <class T> |
||||
struct HashString : ::testing::Test { |
||||
hash_default_hash<T> hasher; |
||||
}; |
||||
|
||||
TYPED_TEST_CASE(HashString, StringTypes); |
||||
|
||||
TYPED_TEST(EqString, Works) { |
||||
auto eq = this->key_eq; |
||||
EXPECT_TRUE(eq("a", "a")); |
||||
EXPECT_TRUE(eq("a", absl::string_view("a"))); |
||||
EXPECT_TRUE(eq("a", std::string("a"))); |
||||
EXPECT_FALSE(eq("a", "b")); |
||||
EXPECT_FALSE(eq("a", absl::string_view("b"))); |
||||
EXPECT_FALSE(eq("a", std::string("b"))); |
||||
} |
||||
|
||||
TYPED_TEST(HashString, Works) { |
||||
auto hash = this->hasher; |
||||
auto h = hash("a"); |
||||
EXPECT_EQ(h, hash(absl::string_view("a"))); |
||||
EXPECT_EQ(h, hash(std::string("a"))); |
||||
EXPECT_NE(h, hash(absl::string_view("b"))); |
||||
EXPECT_NE(h, hash(std::string("b"))); |
||||
} |
||||
|
||||
struct NoDeleter { |
||||
template <class T> |
||||
void operator()(const T* ptr) const {} |
||||
}; |
||||
|
||||
using PointerTypes = |
||||
::testing::Types<const int*, int*, std::unique_ptr<const int>, |
||||
std::unique_ptr<const int, NoDeleter>, |
||||
std::unique_ptr<int>, std::unique_ptr<int, NoDeleter>, |
||||
std::shared_ptr<const int>, std::shared_ptr<int>>; |
||||
|
||||
template <class T> |
||||
struct EqPointer : ::testing::Test { |
||||
hash_default_eq<T> key_eq; |
||||
}; |
||||
|
||||
TYPED_TEST_CASE(EqPointer, PointerTypes); |
||||
|
||||
template <class T> |
||||
struct HashPointer : ::testing::Test { |
||||
hash_default_hash<T> hasher; |
||||
}; |
||||
|
||||
TYPED_TEST_CASE(HashPointer, PointerTypes); |
||||
|
||||
TYPED_TEST(EqPointer, Works) { |
||||
int dummy; |
||||
auto eq = this->key_eq; |
||||
auto sptr = std::make_shared<int>(); |
||||
std::shared_ptr<const int> csptr = sptr; |
||||
int* ptr = sptr.get(); |
||||
const int* cptr = ptr; |
||||
std::unique_ptr<int, NoDeleter> uptr(ptr); |
||||
std::unique_ptr<const int, NoDeleter> cuptr(ptr); |
||||
|
||||
EXPECT_TRUE(eq(ptr, cptr)); |
||||
EXPECT_TRUE(eq(ptr, sptr)); |
||||
EXPECT_TRUE(eq(ptr, uptr)); |
||||
EXPECT_TRUE(eq(ptr, csptr)); |
||||
EXPECT_TRUE(eq(ptr, cuptr)); |
||||
EXPECT_FALSE(eq(&dummy, cptr)); |
||||
EXPECT_FALSE(eq(&dummy, sptr)); |
||||
EXPECT_FALSE(eq(&dummy, uptr)); |
||||
EXPECT_FALSE(eq(&dummy, csptr)); |
||||
EXPECT_FALSE(eq(&dummy, cuptr)); |
||||
} |
||||
|
||||
TEST(Hash, DerivedAndBase) { |
||||
struct Base {}; |
||||
struct Derived : Base {}; |
||||
|
||||
hash_default_hash<Base*> hasher; |
||||
|
||||
Base base; |
||||
Derived derived; |
||||
EXPECT_NE(hasher(&base), hasher(&derived)); |
||||
EXPECT_EQ(hasher(static_cast<Base*>(&derived)), hasher(&derived)); |
||||
|
||||
auto dp = std::make_shared<Derived>(); |
||||
EXPECT_EQ(hasher(static_cast<Base*>(dp.get())), hasher(dp)); |
||||
} |
||||
|
||||
TEST(Hash, FunctionPointer) { |
||||
using Func = int (*)(); |
||||
hash_default_hash<Func> hasher; |
||||
hash_default_eq<Func> eq; |
||||
|
||||
Func p1 = [] { return 1; }, p2 = [] { return 2; }; |
||||
EXPECT_EQ(hasher(p1), hasher(p1)); |
||||
EXPECT_TRUE(eq(p1, p1)); |
||||
|
||||
EXPECT_NE(hasher(p1), hasher(p2)); |
||||
EXPECT_FALSE(eq(p1, p2)); |
||||
} |
||||
|
||||
TYPED_TEST(HashPointer, Works) { |
||||
int dummy; |
||||
auto hash = this->hasher; |
||||
auto sptr = std::make_shared<int>(); |
||||
std::shared_ptr<const int> csptr = sptr; |
||||
int* ptr = sptr.get(); |
||||
const int* cptr = ptr; |
||||
std::unique_ptr<int, NoDeleter> uptr(ptr); |
||||
std::unique_ptr<const int, NoDeleter> cuptr(ptr); |
||||
|
||||
EXPECT_EQ(hash(ptr), hash(cptr)); |
||||
EXPECT_EQ(hash(ptr), hash(sptr)); |
||||
EXPECT_EQ(hash(ptr), hash(uptr)); |
||||
EXPECT_EQ(hash(ptr), hash(csptr)); |
||||
EXPECT_EQ(hash(ptr), hash(cuptr)); |
||||
EXPECT_NE(hash(&dummy), hash(cptr)); |
||||
EXPECT_NE(hash(&dummy), hash(sptr)); |
||||
EXPECT_NE(hash(&dummy), hash(uptr)); |
||||
EXPECT_NE(hash(&dummy), hash(csptr)); |
||||
EXPECT_NE(hash(&dummy), hash(cuptr)); |
||||
} |
||||
|
||||
// Cartesian product of (string, std::string, absl::string_view)
|
||||
// with (string, std::string, absl::string_view, const char*).
|
||||
using StringTypesCartesianProduct = Types< |
||||
// clang-format off
|
||||
|
||||
std::pair<std::string, std::string>, |
||||
std::pair<std::string, absl::string_view>, |
||||
std::pair<std::string, const char*>, |
||||
|
||||
std::pair<absl::string_view, std::string>, |
||||
std::pair<absl::string_view, absl::string_view>, |
||||
std::pair<absl::string_view, const char*>>; |
||||
// clang-format on
|
||||
|
||||
constexpr char kFirstString[] = "abc123"; |
||||
constexpr char kSecondString[] = "ijk456"; |
||||
|
||||
template <typename T> |
||||
struct StringLikeTest : public ::testing::Test { |
||||
typename T::first_type a1{kFirstString}; |
||||
typename T::second_type b1{kFirstString}; |
||||
typename T::first_type a2{kSecondString}; |
||||
typename T::second_type b2{kSecondString}; |
||||
hash_default_eq<typename T::first_type> eq; |
||||
hash_default_hash<typename T::first_type> hash; |
||||
}; |
||||
|
||||
TYPED_TEST_CASE_P(StringLikeTest); |
||||
|
||||
TYPED_TEST_P(StringLikeTest, Eq) { |
||||
EXPECT_TRUE(this->eq(this->a1, this->b1)); |
||||
EXPECT_TRUE(this->eq(this->b1, this->a1)); |
||||
} |
||||
|
||||
TYPED_TEST_P(StringLikeTest, NotEq) { |
||||
EXPECT_FALSE(this->eq(this->a1, this->b2)); |
||||
EXPECT_FALSE(this->eq(this->b2, this->a1)); |
||||
} |
||||
|
||||
TYPED_TEST_P(StringLikeTest, HashEq) { |
||||
EXPECT_EQ(this->hash(this->a1), this->hash(this->b1)); |
||||
EXPECT_EQ(this->hash(this->a2), this->hash(this->b2)); |
||||
// It would be a poor hash function which collides on these strings.
|
||||
EXPECT_NE(this->hash(this->a1), this->hash(this->b2)); |
||||
} |
||||
|
||||
TYPED_TEST_CASE(StringLikeTest, StringTypesCartesianProduct); |
||||
|
||||
} // namespace
|
||||
} // namespace container_internal
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
||||
|
||||
enum Hash : size_t { |
||||
kStd = 0x2, // std::hash
|
||||
#ifdef _MSC_VER |
||||
kExtension = kStd, // In MSVC, std::hash == ::hash
|
||||
#else // _MSC_VER
|
||||
kExtension = 0x4, // ::hash (GCC extension)
|
||||
#endif // _MSC_VER
|
||||
}; |
||||
|
||||
// H is a bitmask of Hash enumerations.
|
||||
// Hashable<H> is hashable via all means specified in H.
|
||||
template <int H> |
||||
struct Hashable { |
||||
static constexpr bool HashableBy(Hash h) { return h & H; } |
||||
}; |
||||
|
||||
namespace std { |
||||
template <int H> |
||||
struct hash<Hashable<H>> { |
||||
template <class E = Hashable<H>, |
||||
class = typename std::enable_if<E::HashableBy(kStd)>::type> |
||||
size_t operator()(E) const { |
||||
return kStd; |
||||
} |
||||
}; |
||||
} // namespace std
|
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
namespace { |
||||
|
||||
template <class T> |
||||
size_t Hash(const T& v) { |
||||
return hash_default_hash<T>()(v); |
||||
} |
||||
|
||||
TEST(Delegate, HashDispatch) { |
||||
EXPECT_EQ(Hash(kStd), Hash(Hashable<kStd>())); |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace container_internal
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
@ -0,0 +1,74 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/container/internal/hash_generator_testing.h" |
||||
|
||||
#include <deque> |
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
namespace hash_internal { |
||||
namespace { |
||||
|
||||
class RandomDeviceSeedSeq { |
||||
public: |
||||
using result_type = typename std::random_device::result_type; |
||||
|
||||
template <class Iterator> |
||||
void generate(Iterator start, Iterator end) { |
||||
while (start != end) { |
||||
*start = gen_(); |
||||
++start; |
||||
} |
||||
} |
||||
|
||||
private: |
||||
std::random_device gen_; |
||||
}; |
||||
|
||||
} // namespace
|
||||
|
||||
std::mt19937_64* GetSharedRng() { |
||||
RandomDeviceSeedSeq seed_seq; |
||||
static auto* rng = new std::mt19937_64(seed_seq); |
||||
return rng; |
||||
} |
||||
|
||||
std::string Generator<std::string>::operator()() const { |
||||
// NOLINTNEXTLINE(runtime/int)
|
||||
std::uniform_int_distribution<short> chars(0x20, 0x7E); |
||||
std::string res; |
||||
res.resize(32); |
||||
std::generate(res.begin(), res.end(), |
||||
[&]() { return chars(*GetSharedRng()); }); |
||||
return res; |
||||
} |
||||
|
||||
absl::string_view Generator<absl::string_view>::operator()() const { |
||||
static auto* arena = new std::deque<std::string>(); |
||||
// NOLINTNEXTLINE(runtime/int)
|
||||
std::uniform_int_distribution<short> chars(0x20, 0x7E); |
||||
arena->emplace_back(); |
||||
auto& res = arena->back(); |
||||
res.resize(32); |
||||
std::generate(res.begin(), res.end(), |
||||
[&]() { return chars(*GetSharedRng()); }); |
||||
return res; |
||||
} |
||||
|
||||
} // namespace hash_internal
|
||||
} // namespace container_internal
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
@ -0,0 +1,152 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Generates random values for testing. Specialized only for the few types we
|
||||
// care about.
|
||||
|
||||
#ifndef ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_ |
||||
#define ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_ |
||||
|
||||
#include <stdint.h> |
||||
#include <algorithm> |
||||
#include <iosfwd> |
||||
#include <random> |
||||
#include <tuple> |
||||
#include <type_traits> |
||||
#include <utility> |
||||
|
||||
#include "absl/container/internal/hash_policy_testing.h" |
||||
#include "absl/meta/type_traits.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
namespace hash_internal { |
||||
namespace generator_internal { |
||||
|
||||
template <class Container, class = void> |
||||
struct IsMap : std::false_type {}; |
||||
|
||||
template <class Map> |
||||
struct IsMap<Map, absl::void_t<typename Map::mapped_type>> : std::true_type {}; |
||||
|
||||
} // namespace generator_internal
|
||||
|
||||
std::mt19937_64* GetSharedRng(); |
||||
|
||||
enum Enum { |
||||
kEnumEmpty, |
||||
kEnumDeleted, |
||||
}; |
||||
|
||||
enum class EnumClass : uint64_t { |
||||
kEmpty, |
||||
kDeleted, |
||||
}; |
||||
|
||||
inline std::ostream& operator<<(std::ostream& o, const EnumClass& ec) { |
||||
return o << static_cast<uint64_t>(ec); |
||||
} |
||||
|
||||
template <class T, class E = void> |
||||
struct Generator; |
||||
|
||||
template <class T> |
||||
struct Generator<T, typename std::enable_if<std::is_integral<T>::value>::type> { |
||||
T operator()() const { |
||||
std::uniform_int_distribution<T> dist; |
||||
return dist(*GetSharedRng()); |
||||
} |
||||
}; |
||||
|
||||
template <> |
||||
struct Generator<Enum> { |
||||
Enum operator()() const { |
||||
std::uniform_int_distribution<typename std::underlying_type<Enum>::type> |
||||
dist; |
||||
while (true) { |
||||
auto variate = dist(*GetSharedRng()); |
||||
if (variate != kEnumEmpty && variate != kEnumDeleted) |
||||
return static_cast<Enum>(variate); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
template <> |
||||
struct Generator<EnumClass> { |
||||
EnumClass operator()() const { |
||||
std::uniform_int_distribution< |
||||
typename std::underlying_type<EnumClass>::type> |
||||
dist; |
||||
while (true) { |
||||
EnumClass variate = static_cast<EnumClass>(dist(*GetSharedRng())); |
||||
if (variate != EnumClass::kEmpty && variate != EnumClass::kDeleted) |
||||
return static_cast<EnumClass>(variate); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
template <> |
||||
struct Generator<std::string> { |
||||
std::string operator()() const; |
||||
}; |
||||
|
||||
template <> |
||||
struct Generator<absl::string_view> { |
||||
absl::string_view operator()() const; |
||||
}; |
||||
|
||||
template <> |
||||
struct Generator<NonStandardLayout> { |
||||
NonStandardLayout operator()() const { |
||||
return NonStandardLayout(Generator<std::string>()()); |
||||
} |
||||
}; |
||||
|
||||
template <class K, class V> |
||||
struct Generator<std::pair<K, V>> { |
||||
std::pair<K, V> operator()() const { |
||||
return std::pair<K, V>(Generator<typename std::decay<K>::type>()(), |
||||
Generator<typename std::decay<V>::type>()()); |
||||
} |
||||
}; |
||||
|
||||
template <class... Ts> |
||||
struct Generator<std::tuple<Ts...>> { |
||||
std::tuple<Ts...> operator()() const { |
||||
return std::tuple<Ts...>(Generator<typename std::decay<Ts>::type>()()...); |
||||
} |
||||
}; |
||||
|
||||
template <class U> |
||||
struct Generator<U, absl::void_t<decltype(std::declval<U&>().key()), |
||||
decltype(std::declval<U&>().value())>> |
||||
: Generator<std::pair< |
||||
typename std::decay<decltype(std::declval<U&>().key())>::type, |
||||
typename std::decay<decltype(std::declval<U&>().value())>::type>> {}; |
||||
|
||||
template <class Container> |
||||
using GeneratedType = decltype( |
||||
std::declval<const Generator< |
||||
typename std::conditional<generator_internal::IsMap<Container>::value, |
||||
typename Container::value_type, |
||||
typename Container::key_type>::type>&>()()); |
||||
|
||||
} // namespace hash_internal
|
||||
} // namespace container_internal
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_
|
@ -0,0 +1,184 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Utilities to help tests verify that hash tables properly handle stateful
|
||||
// allocators and hash functions.
|
||||
|
||||
#ifndef ABSL_CONTAINER_INTERNAL_HASH_POLICY_TESTING_H_ |
||||
#define ABSL_CONTAINER_INTERNAL_HASH_POLICY_TESTING_H_ |
||||
|
||||
#include <cstdlib> |
||||
#include <limits> |
||||
#include <memory> |
||||
#include <ostream> |
||||
#include <type_traits> |
||||
#include <utility> |
||||
#include <vector> |
||||
|
||||
#include "absl/hash/hash.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
namespace hash_testing_internal { |
||||
|
||||
template <class Derived> |
||||
struct WithId { |
||||
WithId() : id_(next_id<Derived>()) {} |
||||
WithId(const WithId& that) : id_(that.id_) {} |
||||
WithId(WithId&& that) : id_(that.id_) { that.id_ = 0; } |
||||
WithId& operator=(const WithId& that) { |
||||
id_ = that.id_; |
||||
return *this; |
||||
} |
||||
WithId& operator=(WithId&& that) { |
||||
id_ = that.id_; |
||||
that.id_ = 0; |
||||
return *this; |
||||
} |
||||
|
||||
size_t id() const { return id_; } |
||||
|
||||
friend bool operator==(const WithId& a, const WithId& b) { |
||||
return a.id_ == b.id_; |
||||
} |
||||
friend bool operator!=(const WithId& a, const WithId& b) { return !(a == b); } |
||||
|
||||
protected: |
||||
explicit WithId(size_t id) : id_(id) {} |
||||
|
||||
private: |
||||
size_t id_; |
||||
|
||||
template <class T> |
||||
static size_t next_id() { |
||||
// 0 is reserved for moved from state.
|
||||
static size_t gId = 1; |
||||
return gId++; |
||||
} |
||||
}; |
||||
|
||||
} // namespace hash_testing_internal
|
||||
|
||||
struct NonStandardLayout { |
||||
NonStandardLayout() {} |
||||
explicit NonStandardLayout(std::string s) : value(std::move(s)) {} |
||||
virtual ~NonStandardLayout() {} |
||||
|
||||
friend bool operator==(const NonStandardLayout& a, |
||||
const NonStandardLayout& b) { |
||||
return a.value == b.value; |
||||
} |
||||
friend bool operator!=(const NonStandardLayout& a, |
||||
const NonStandardLayout& b) { |
||||
return a.value != b.value; |
||||
} |
||||
|
||||
template <typename H> |
||||
friend H AbslHashValue(H h, const NonStandardLayout& v) { |
||||
return H::combine(std::move(h), v.value); |
||||
} |
||||
|
||||
std::string value; |
||||
}; |
||||
|
||||
struct StatefulTestingHash |
||||
: absl::container_internal::hash_testing_internal::WithId< |
||||
StatefulTestingHash> { |
||||
template <class T> |
||||
size_t operator()(const T& t) const { |
||||
return absl::Hash<T>{}(t); |
||||
} |
||||
}; |
||||
|
||||
struct StatefulTestingEqual |
||||
: absl::container_internal::hash_testing_internal::WithId< |
||||
StatefulTestingEqual> { |
||||
template <class T, class U> |
||||
bool operator()(const T& t, const U& u) const { |
||||
return t == u; |
||||
} |
||||
}; |
||||
|
||||
// It is expected that Alloc() == Alloc() for all allocators so we cannot use
|
||||
// WithId base. We need to explicitly assign ids.
|
||||
template <class T = int> |
||||
struct Alloc : std::allocator<T> { |
||||
using propagate_on_container_swap = std::true_type; |
||||
|
||||
// Using old paradigm for this to ensure compatibility.
|
||||
explicit Alloc(size_t id = 0) : id_(id) {} |
||||
|
||||
Alloc(const Alloc&) = default; |
||||
Alloc& operator=(const Alloc&) = default; |
||||
|
||||
template <class U> |
||||
Alloc(const Alloc<U>& that) : std::allocator<T>(that), id_(that.id()) {} |
||||
|
||||
template <class U> |
||||
struct rebind { |
||||
using other = Alloc<U>; |
||||
}; |
||||
|
||||
size_t id() const { return id_; } |
||||
|
||||
friend bool operator==(const Alloc& a, const Alloc& b) { |
||||
return a.id_ == b.id_; |
||||
} |
||||
friend bool operator!=(const Alloc& a, const Alloc& b) { return !(a == b); } |
||||
|
||||
private: |
||||
size_t id_ = (std::numeric_limits<size_t>::max)(); |
||||
}; |
||||
|
||||
template <class Map> |
||||
auto items(const Map& m) -> std::vector< |
||||
std::pair<typename Map::key_type, typename Map::mapped_type>> { |
||||
using std::get; |
||||
std::vector<std::pair<typename Map::key_type, typename Map::mapped_type>> res; |
||||
res.reserve(m.size()); |
||||
for (const auto& v : m) res.emplace_back(get<0>(v), get<1>(v)); |
||||
return res; |
||||
} |
||||
|
||||
template <class Set> |
||||
auto keys(const Set& s) |
||||
-> std::vector<typename std::decay<typename Set::key_type>::type> { |
||||
std::vector<typename std::decay<typename Set::key_type>::type> res; |
||||
res.reserve(s.size()); |
||||
for (const auto& v : s) res.emplace_back(v); |
||||
return res; |
||||
} |
||||
|
||||
} // namespace container_internal
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
||||
|
||||
// ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS is false for glibcxx versions
|
||||
// where the unordered containers are missing certain constructors that
|
||||
// take allocator arguments. This test is defined ad-hoc for the platforms
|
||||
// we care about (notably Crosstool 17) because libstdcxx's useless
|
||||
// versioning scheme precludes a more principled solution.
|
||||
// From GCC-4.9 Changelog: (src: https://gcc.gnu.org/gcc-4.9/changes.html)
|
||||
// "the unordered associative containers in <unordered_map> and <unordered_set>
|
||||
// meet the allocator-aware container requirements;"
|
||||
#if (defined(__GLIBCXX__) && __GLIBCXX__ <= 20140425 ) || \ |
||||
( __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 9 )) |
||||
#define ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS 0 |
||||
#else |
||||
#define ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS 1 |
||||
#endif |
||||
|
||||
#endif // ABSL_CONTAINER_INTERNAL_HASH_POLICY_TESTING_H_
|
@ -0,0 +1,45 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/container/internal/hash_policy_testing.h" |
||||
|
||||
#include "gtest/gtest.h" |
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
namespace { |
||||
|
||||
TEST(_, Hash) { |
||||
StatefulTestingHash h1; |
||||
EXPECT_EQ(1, h1.id()); |
||||
StatefulTestingHash h2; |
||||
EXPECT_EQ(2, h2.id()); |
||||
StatefulTestingHash h1c(h1); |
||||
EXPECT_EQ(1, h1c.id()); |
||||
StatefulTestingHash h2m(std::move(h2)); |
||||
EXPECT_EQ(2, h2m.id()); |
||||
EXPECT_EQ(0, h2.id()); |
||||
StatefulTestingHash h3; |
||||
EXPECT_EQ(3, h3.id()); |
||||
h3 = StatefulTestingHash(); |
||||
EXPECT_EQ(4, h3.id()); |
||||
h3 = std::move(h1); |
||||
EXPECT_EQ(1, h3.id()); |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace container_internal
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
@ -0,0 +1,191 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ |
||||
#define ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ |
||||
|
||||
#include <cstddef> |
||||
#include <memory> |
||||
#include <type_traits> |
||||
#include <utility> |
||||
|
||||
#include "absl/meta/type_traits.h" |
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
|
||||
// Defines how slots are initialized/destroyed/moved.
|
||||
template <class Policy, class = void> |
||||
struct hash_policy_traits { |
||||
private: |
||||
struct ReturnKey { |
||||
// We return `Key` here.
|
||||
// When Key=T&, we forward the lvalue reference.
|
||||
// When Key=T, we return by value to avoid a dangling reference.
|
||||
// eg, for string_hash_map.
|
||||
template <class Key, class... Args> |
||||
Key operator()(Key&& k, const Args&...) const { |
||||
return std::forward<Key>(k); |
||||
} |
||||
}; |
||||
|
||||
template <class P = Policy, class = void> |
||||
struct ConstantIteratorsImpl : std::false_type {}; |
||||
|
||||
template <class P> |
||||
struct ConstantIteratorsImpl<P, absl::void_t<typename P::constant_iterators>> |
||||
: P::constant_iterators {}; |
||||
|
||||
public: |
||||
// The actual object stored in the hash table.
|
||||
using slot_type = typename Policy::slot_type; |
||||
|
||||
// The type of the keys stored in the hashtable.
|
||||
using key_type = typename Policy::key_type; |
||||
|
||||
// The argument type for insertions into the hashtable. This is different
|
||||
// from value_type for increased performance. See initializer_list constructor
|
||||
// and insert() member functions for more details.
|
||||
using init_type = typename Policy::init_type; |
||||
|
||||
using reference = decltype(Policy::element(std::declval<slot_type*>())); |
||||
using pointer = typename std::remove_reference<reference>::type*; |
||||
using value_type = typename std::remove_reference<reference>::type; |
||||
|
||||
// Policies can set this variable to tell raw_hash_set that all iterators
|
||||
// should be constant, even `iterator`. This is useful for set-like
|
||||
// containers.
|
||||
// Defaults to false if not provided by the policy.
|
||||
using constant_iterators = ConstantIteratorsImpl<>; |
||||
|
||||
// PRECONDITION: `slot` is UNINITIALIZED
|
||||
// POSTCONDITION: `slot` is INITIALIZED
|
||||
template <class Alloc, class... Args> |
||||
static void construct(Alloc* alloc, slot_type* slot, Args&&... args) { |
||||
Policy::construct(alloc, slot, std::forward<Args>(args)...); |
||||
} |
||||
|
||||
// PRECONDITION: `slot` is INITIALIZED
|
||||
// POSTCONDITION: `slot` is UNINITIALIZED
|
||||
template <class Alloc> |
||||
static void destroy(Alloc* alloc, slot_type* slot) { |
||||
Policy::destroy(alloc, slot); |
||||
} |
||||
|
||||
// Transfers the `old_slot` to `new_slot`. Any memory allocated by the
|
||||
// allocator inside `old_slot` to `new_slot` can be transferred.
|
||||
//
|
||||
// OPTIONAL: defaults to:
|
||||
//
|
||||
// clone(new_slot, std::move(*old_slot));
|
||||
// destroy(old_slot);
|
||||
//
|
||||
// PRECONDITION: `new_slot` is UNINITIALIZED and `old_slot` is INITIALIZED
|
||||
// POSTCONDITION: `new_slot` is INITIALIZED and `old_slot` is
|
||||
// UNINITIALIZED
|
||||
template <class Alloc> |
||||
static void transfer(Alloc* alloc, slot_type* new_slot, slot_type* old_slot) { |
||||
transfer_impl(alloc, new_slot, old_slot, 0); |
||||
} |
||||
|
||||
// PRECONDITION: `slot` is INITIALIZED
|
||||
// POSTCONDITION: `slot` is INITIALIZED
|
||||
template <class P = Policy> |
||||
static auto element(slot_type* slot) -> decltype(P::element(slot)) { |
||||
return P::element(slot); |
||||
} |
||||
|
||||
// Returns the amount of memory owned by `slot`, exclusive of `sizeof(*slot)`.
|
||||
//
|
||||
// If `slot` is nullptr, returns the constant amount of memory owned by any
|
||||
// full slot or -1 if slots own variable amounts of memory.
|
||||
//
|
||||
// PRECONDITION: `slot` is INITIALIZED or nullptr
|
||||
template <class P = Policy> |
||||
static size_t space_used(const slot_type* slot) { |
||||
return P::space_used(slot); |
||||
} |
||||
|
||||
// Provides generalized access to the key for elements, both for elements in
|
||||
// the table and for elements that have not yet been inserted (or even
|
||||
// constructed). We would like an API that allows us to say: `key(args...)`
|
||||
// but we cannot do that for all cases, so we use this more general API that
|
||||
// can be used for many things, including the following:
|
||||
//
|
||||
// - Given an element in a table, get its key.
|
||||
// - Given an element initializer, get its key.
|
||||
// - Given `emplace()` arguments, get the element key.
|
||||
//
|
||||
// Implementations of this must adhere to a very strict technical
|
||||
// specification around aliasing and consuming arguments:
|
||||
//
|
||||
// Let `value_type` be the result type of `element()` without ref- and
|
||||
// cv-qualifiers. The first argument is a functor, the rest are constructor
|
||||
// arguments for `value_type`. Returns `std::forward<F>(f)(k, xs...)`, where
|
||||
// `k` is the element key, and `xs...` are the new constructor arguments for
|
||||
// `value_type`. It's allowed for `k` to alias `xs...`, and for both to alias
|
||||
// `ts...`. The key won't be touched once `xs...` are used to construct an
|
||||
// element; `ts...` won't be touched at all, which allows `apply()` to consume
|
||||
// any rvalues among them.
|
||||
//
|
||||
// If `value_type` is constructible from `Ts&&...`, `Policy::apply()` must not
|
||||
// trigger a hard compile error unless it originates from `f`. In other words,
|
||||
// `Policy::apply()` must be SFINAE-friendly. If `value_type` is not
|
||||
// constructible from `Ts&&...`, either SFINAE or a hard compile error is OK.
|
||||
//
|
||||
// If `Ts...` is `[cv] value_type[&]` or `[cv] init_type[&]`,
|
||||
// `Policy::apply()` must work. A compile error is not allowed, SFINAE or not.
|
||||
template <class F, class... Ts, class P = Policy> |
||||
static auto apply(F&& f, Ts&&... ts) |
||||
-> decltype(P::apply(std::forward<F>(f), std::forward<Ts>(ts)...)) { |
||||
return P::apply(std::forward<F>(f), std::forward<Ts>(ts)...); |
||||
} |
||||
|
||||
// Returns the "key" portion of the slot.
|
||||
// Used for node handle manipulation.
|
||||
template <class P = Policy> |
||||
static auto key(slot_type* slot) |
||||
-> decltype(P::apply(ReturnKey(), element(slot))) { |
||||
return P::apply(ReturnKey(), element(slot)); |
||||
} |
||||
|
||||
// Returns the "value" (as opposed to the "key") portion of the element. Used
|
||||
// by maps to implement `operator[]`, `at()` and `insert_or_assign()`.
|
||||
template <class T, class P = Policy> |
||||
static auto value(T* elem) -> decltype(P::value(elem)) { |
||||
return P::value(elem); |
||||
} |
||||
|
||||
private: |
||||
// Use auto -> decltype as an enabler.
|
||||
template <class Alloc, class P = Policy> |
||||
static auto transfer_impl(Alloc* alloc, slot_type* new_slot, |
||||
slot_type* old_slot, int) |
||||
-> decltype((void)P::transfer(alloc, new_slot, old_slot)) { |
||||
P::transfer(alloc, new_slot, old_slot); |
||||
} |
||||
template <class Alloc> |
||||
static void transfer_impl(Alloc* alloc, slot_type* new_slot, |
||||
slot_type* old_slot, char) { |
||||
construct(alloc, new_slot, std::move(element(old_slot))); |
||||
destroy(alloc, old_slot); |
||||
} |
||||
}; |
||||
|
||||
} // namespace container_internal
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_
|
@ -0,0 +1,144 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/container/internal/hash_policy_traits.h" |
||||
|
||||
#include <functional> |
||||
#include <memory> |
||||
#include <new> |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
namespace { |
||||
|
||||
using ::testing::MockFunction; |
||||
using ::testing::Return; |
||||
using ::testing::ReturnRef; |
||||
|
||||
using Alloc = std::allocator<int>; |
||||
using Slot = int; |
||||
|
||||
struct PolicyWithoutOptionalOps { |
||||
using slot_type = Slot; |
||||
using key_type = Slot; |
||||
using init_type = Slot; |
||||
|
||||
static std::function<void(void*, Slot*, Slot)> construct; |
||||
static std::function<void(void*, Slot*)> destroy; |
||||
|
||||
static std::function<Slot&(Slot*)> element; |
||||
static int apply(int v) { return apply_impl(v); } |
||||
static std::function<int(int)> apply_impl; |
||||
static std::function<Slot&(Slot*)> value; |
||||
}; |
||||
|
||||
std::function<void(void*, Slot*, Slot)> PolicyWithoutOptionalOps::construct; |
||||
std::function<void(void*, Slot*)> PolicyWithoutOptionalOps::destroy; |
||||
|
||||
std::function<Slot&(Slot*)> PolicyWithoutOptionalOps::element; |
||||
std::function<int(int)> PolicyWithoutOptionalOps::apply_impl; |
||||
std::function<Slot&(Slot*)> PolicyWithoutOptionalOps::value; |
||||
|
||||
struct PolicyWithOptionalOps : PolicyWithoutOptionalOps { |
||||
static std::function<void(void*, Slot*, Slot*)> transfer; |
||||
}; |
||||
|
||||
std::function<void(void*, Slot*, Slot*)> PolicyWithOptionalOps::transfer; |
||||
|
||||
struct Test : ::testing::Test { |
||||
Test() { |
||||
PolicyWithoutOptionalOps::construct = [&](void* a1, Slot* a2, Slot a3) { |
||||
construct.Call(a1, a2, std::move(a3)); |
||||
}; |
||||
PolicyWithoutOptionalOps::destroy = [&](void* a1, Slot* a2) { |
||||
destroy.Call(a1, a2); |
||||
}; |
||||
|
||||
PolicyWithoutOptionalOps::element = [&](Slot* a1) -> Slot& { |
||||
return element.Call(a1); |
||||
}; |
||||
PolicyWithoutOptionalOps::apply_impl = [&](int a1) -> int { |
||||
return apply.Call(a1); |
||||
}; |
||||
PolicyWithoutOptionalOps::value = [&](Slot* a1) -> Slot& { |
||||
return value.Call(a1); |
||||
}; |
||||
|
||||
PolicyWithOptionalOps::transfer = [&](void* a1, Slot* a2, Slot* a3) { |
||||
return transfer.Call(a1, a2, a3); |
||||
}; |
||||
} |
||||
|
||||
std::allocator<int> alloc; |
||||
int a = 53; |
||||
|
||||
MockFunction<void(void*, Slot*, Slot)> construct; |
||||
MockFunction<void(void*, Slot*)> destroy; |
||||
|
||||
MockFunction<Slot&(Slot*)> element; |
||||
MockFunction<int(int)> apply; |
||||
MockFunction<Slot&(Slot*)> value; |
||||
|
||||
MockFunction<void(void*, Slot*, Slot*)> transfer; |
||||
}; |
||||
|
||||
TEST_F(Test, construct) { |
||||
EXPECT_CALL(construct, Call(&alloc, &a, 53)); |
||||
hash_policy_traits<PolicyWithoutOptionalOps>::construct(&alloc, &a, 53); |
||||
} |
||||
|
||||
TEST_F(Test, destroy) { |
||||
EXPECT_CALL(destroy, Call(&alloc, &a)); |
||||
hash_policy_traits<PolicyWithoutOptionalOps>::destroy(&alloc, &a); |
||||
} |
||||
|
||||
TEST_F(Test, element) { |
||||
int b = 0; |
||||
EXPECT_CALL(element, Call(&a)).WillOnce(ReturnRef(b)); |
||||
EXPECT_EQ(&b, &hash_policy_traits<PolicyWithoutOptionalOps>::element(&a)); |
||||
} |
||||
|
||||
TEST_F(Test, apply) { |
||||
EXPECT_CALL(apply, Call(42)).WillOnce(Return(1337)); |
||||
EXPECT_EQ(1337, (hash_policy_traits<PolicyWithoutOptionalOps>::apply(42))); |
||||
} |
||||
|
||||
TEST_F(Test, value) { |
||||
int b = 0; |
||||
EXPECT_CALL(value, Call(&a)).WillOnce(ReturnRef(b)); |
||||
EXPECT_EQ(&b, &hash_policy_traits<PolicyWithoutOptionalOps>::value(&a)); |
||||
} |
||||
|
||||
TEST_F(Test, without_transfer) { |
||||
int b = 42; |
||||
EXPECT_CALL(element, Call(&b)).WillOnce(::testing::ReturnRef(b)); |
||||
EXPECT_CALL(construct, Call(&alloc, &a, b)); |
||||
EXPECT_CALL(destroy, Call(&alloc, &b)); |
||||
hash_policy_traits<PolicyWithoutOptionalOps>::transfer(&alloc, &a, &b); |
||||
} |
||||
|
||||
TEST_F(Test, with_transfer) { |
||||
int b = 42; |
||||
EXPECT_CALL(transfer, Call(&alloc, &a, &b)); |
||||
hash_policy_traits<PolicyWithOptionalOps>::transfer(&alloc, &a, &b); |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace container_internal
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
@ -0,0 +1,110 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// This library provides APIs to debug the probing behavior of hash tables.
|
||||
//
|
||||
// In general, the probing behavior is a black box for users and only the
|
||||
// side effects can be measured in the form of performance differences.
|
||||
// These APIs give a glimpse on the actual behavior of the probing algorithms in
|
||||
// these hashtables given a specified hash function and a set of elements.
|
||||
//
|
||||
// The probe count distribution can be used to assess the quality of the hash
|
||||
// function for that particular hash table. Note that a hash function that
|
||||
// performs well in one hash table implementation does not necessarily performs
|
||||
// well in a different one.
|
||||
//
|
||||
// This library supports std::unordered_{set,map}, dense_hash_{set,map} and
|
||||
// absl::{flat,node,string}_hash_{set,map}.
|
||||
|
||||
#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_ |
||||
#define ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_ |
||||
|
||||
#include <cstddef> |
||||
#include <algorithm> |
||||
#include <type_traits> |
||||
#include <vector> |
||||
|
||||
#include "absl/container/internal/hashtable_debug_hooks.h" |
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
|
||||
// Returns the number of probes required to lookup `key`. Returns 0 for a
|
||||
// search with no collisions. Higher values mean more hash collisions occurred;
|
||||
// however, the exact meaning of this number varies according to the container
|
||||
// type.
|
||||
template <typename C> |
||||
size_t GetHashtableDebugNumProbes( |
||||
const C& c, const typename C::key_type& key) { |
||||
return absl::container_internal::hashtable_debug_internal:: |
||||
HashtableDebugAccess<C>::GetNumProbes(c, key); |
||||
} |
||||
|
||||
// Gets a histogram of the number of probes for each elements in the container.
|
||||
// The sum of all the values in the vector is equal to container.size().
|
||||
template <typename C> |
||||
std::vector<size_t> GetHashtableDebugNumProbesHistogram(const C& container) { |
||||
std::vector<size_t> v; |
||||
for (auto it = container.begin(); it != container.end(); ++it) { |
||||
size_t num_probes = GetHashtableDebugNumProbes( |
||||
container, |
||||
absl::container_internal::hashtable_debug_internal::GetKey<C>(*it, 0)); |
||||
v.resize(std::max(v.size(), num_probes + 1)); |
||||
v[num_probes]++; |
||||
} |
||||
return v; |
||||
} |
||||
|
||||
struct HashtableDebugProbeSummary { |
||||
size_t total_elements; |
||||
size_t total_num_probes; |
||||
double mean; |
||||
}; |
||||
|
||||
// Gets a summary of the probe count distribution for the elements in the
|
||||
// container.
|
||||
template <typename C> |
||||
HashtableDebugProbeSummary GetHashtableDebugProbeSummary(const C& container) { |
||||
auto probes = GetHashtableDebugNumProbesHistogram(container); |
||||
HashtableDebugProbeSummary summary = {}; |
||||
for (size_t i = 0; i < probes.size(); ++i) { |
||||
summary.total_elements += probes[i]; |
||||
summary.total_num_probes += probes[i] * i; |
||||
} |
||||
summary.mean = 1.0 * summary.total_num_probes / summary.total_elements; |
||||
return summary; |
||||
} |
||||
|
||||
// Returns the number of bytes requested from the allocator by the container
|
||||
// and not freed.
|
||||
template <typename C> |
||||
size_t AllocatedByteSize(const C& c) { |
||||
return absl::container_internal::hashtable_debug_internal:: |
||||
HashtableDebugAccess<C>::AllocatedByteSize(c); |
||||
} |
||||
|
||||
// Returns a tight lower bound for AllocatedByteSize(c) where `c` is of type `C`
|
||||
// and `c.size()` is equal to `num_elements`.
|
||||
template <typename C> |
||||
size_t LowerBoundAllocatedByteSize(size_t num_elements) { |
||||
return absl::container_internal::hashtable_debug_internal:: |
||||
HashtableDebugAccess<C>::LowerBoundAllocatedByteSize(num_elements); |
||||
} |
||||
|
||||
} // namespace container_internal
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_
|
@ -0,0 +1,83 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Provides the internal API for hashtable_debug.h.
|
||||
|
||||
#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_ |
||||
#define ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_ |
||||
|
||||
#include <cstddef> |
||||
|
||||
#include <algorithm> |
||||
#include <type_traits> |
||||
#include <vector> |
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
namespace hashtable_debug_internal { |
||||
|
||||
// If it is a map, call get<0>().
|
||||
using std::get; |
||||
template <typename T, typename = typename T::mapped_type> |
||||
auto GetKey(const typename T::value_type& pair, int) -> decltype(get<0>(pair)) { |
||||
return get<0>(pair); |
||||
} |
||||
|
||||
// If it is not a map, return the value directly.
|
||||
template <typename T> |
||||
const typename T::key_type& GetKey(const typename T::key_type& key, char) { |
||||
return key; |
||||
} |
||||
|
||||
// Containers should specialize this to provide debug information for that
|
||||
// container.
|
||||
template <class Container, typename Enabler = void> |
||||
struct HashtableDebugAccess { |
||||
// Returns the number of probes required to find `key` in `c`. The "number of
|
||||
// probes" is a concept that can vary by container. Implementations should
|
||||
// return 0 when `key` was found in the minimum number of operations and
|
||||
// should increment the result for each non-trivial operation required to find
|
||||
// `key`.
|
||||
//
|
||||
// The default implementation uses the bucket api from the standard and thus
|
||||
// works for `std::unordered_*` containers.
|
||||
static size_t GetNumProbes(const Container& c, |
||||
const typename Container::key_type& key) { |
||||
if (!c.bucket_count()) return {}; |
||||
size_t num_probes = 0; |
||||
size_t bucket = c.bucket(key); |
||||
for (auto it = c.begin(bucket), e = c.end(bucket);; ++it, ++num_probes) { |
||||
if (it == e) return num_probes; |
||||
if (c.key_eq()(key, GetKey<Container>(*it, 0))) return num_probes; |
||||
} |
||||
} |
||||
|
||||
// Returns the number of bytes requested from the allocator by the container
|
||||
// and not freed.
|
||||
//
|
||||
// static size_t AllocatedByteSize(const Container& c);
|
||||
|
||||
// Returns a tight lower bound for AllocatedByteSize(c) where `c` is of type
|
||||
// `Container` and `c.size()` is equal to `num_elements`.
|
||||
//
|
||||
// static size_t LowerBoundAllocatedByteSize(size_t num_elements);
|
||||
}; |
||||
|
||||
} // namespace hashtable_debug_internal
|
||||
} // namespace container_internal
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_
|
@ -0,0 +1,740 @@ |
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// MOTIVATION AND TUTORIAL
|
||||
//
|
||||
// If you want to put in a single heap allocation N doubles followed by M ints,
|
||||
// it's easy if N and M are known at compile time.
|
||||
//
|
||||
// struct S {
|
||||
// double a[N];
|
||||
// int b[M];
|
||||
// };
|
||||
//
|
||||
// S* p = new S;
|
||||
//
|
||||
// But what if N and M are known only in run time? Class template Layout to the
|
||||
// rescue! It's a portable generalization of the technique known as struct hack.
|
||||
//
|
||||
// // This object will tell us everything we need to know about the memory
|
||||
// // layout of double[N] followed by int[M]. It's structurally identical to
|
||||
// // size_t[2] that stores N and M. It's very cheap to create.
|
||||
// const Layout<double, int> layout(N, M);
|
||||
//
|
||||
// // Allocate enough memory for both arrays. `AllocSize()` tells us how much
|
||||
// // memory is needed. We are free to use any allocation function we want as
|
||||
// // long as it returns aligned memory.
|
||||
// std::unique_ptr<unsigned char[]> p(new unsigned char[layout.AllocSize()]);
|
||||
//
|
||||
// // Obtain the pointer to the array of doubles.
|
||||
// // Equivalent to `reinterpret_cast<double*>(p.get())`.
|
||||
// //
|
||||
// // We could have written layout.Pointer<0>(p) instead. If all the types are
|
||||
// // unique you can use either form, but if some types are repeated you must
|
||||
// // use the index form.
|
||||
// double* a = layout.Pointer<double>(p.get());
|
||||
//
|
||||
// // Obtain the pointer to the array of ints.
|
||||
// // Equivalent to `reinterpret_cast<int*>(p.get() + N * 8)`.
|
||||
// int* b = layout.Pointer<int>(p);
|
||||
//
|
||||
// If we are unable to specify sizes of all fields, we can pass as many sizes as
|
||||
// we can to `Partial()`. In return, it'll allow us to access the fields whose
|
||||
// locations and sizes can be computed from the provided information.
|
||||
// `Partial()` comes in handy when the array sizes are embedded into the
|
||||
// allocation.
|
||||
//
|
||||
// // size_t[1] containing N, size_t[1] containing M, double[N], int[M].
|
||||
// using L = Layout<size_t, size_t, double, int>;
|
||||
//
|
||||
// unsigned char* Allocate(size_t n, size_t m) {
|
||||
// const L layout(1, 1, n, m);
|
||||
// unsigned char* p = new unsigned char[layout.AllocSize()];
|
||||
// *layout.Pointer<0>(p) = n;
|
||||
// *layout.Pointer<1>(p) = m;
|
||||
// return p;
|
||||
// }
|
||||
//
|
||||
// void Use(unsigned char* p) {
|
||||
// // First, extract N and M.
|
||||
// // Specify that the first array has only one element. Using `prefix` we
|
||||
// // can access the first two arrays but not more.
|
||||
// constexpr auto prefix = L::Partial(1);
|
||||
// size_t n = *prefix.Pointer<0>(p);
|
||||
// size_t m = *prefix.Pointer<1>(p);
|
||||
//
|
||||
// // Now we can get pointers to the payload.
|
||||
// const L layout(1, 1, n, m);
|
||||
// double* a = layout.Pointer<double>(p);
|
||||
// int* b = layout.Pointer<int>(p);
|
||||
// }
|
||||
//
|
||||
// The layout we used above combines fixed-size with dynamically-sized fields.
|
||||
// This is quite common. Layout is optimized for this use case and generates
|
||||
// optimal code. All computations that can be performed at compile time are
|
||||
// indeed performed at compile time.
|
||||
//
|
||||
// Efficiency tip: The order of fields matters. In `Layout<T1, ..., TN>` try to
|
||||
// ensure that `alignof(T1) >= ... >= alignof(TN)`. This way you'll have no
|
||||
// padding in between arrays.
|
||||
//
|
||||
// You can manually override the alignment of an array by wrapping the type in
|
||||
// `Aligned<T, N>`. `Layout<..., Aligned<T, N>, ...>` has exactly the same API
|
||||
// and behavior as `Layout<..., T, ...>` except that the first element of the
|
||||
// array of `T` is aligned to `N` (the rest of the elements follow without
|
||||
// padding). `N` cannot be less than `alignof(T)`.
|
||||
//
|
||||
// `AllocSize()` and `Pointer()` are the most basic methods for dealing with
|
||||
// memory layouts. Check out the reference or code below to discover more.
|
||||
//
|
||||
// EXAMPLE
|
||||
//
|
||||
// // Immutable move-only string with sizeof equal to sizeof(void*). The
|
||||
// // string size and the characters are kept in the same heap allocation.
|
||||
// class CompactString {
|
||||
// public:
|
||||
// CompactString(const char* s = "") {
|
||||
// const size_t size = strlen(s);
|
||||
// // size_t[1] followed by char[size + 1].
|
||||
// const L layout(1, size + 1);
|
||||
// p_.reset(new unsigned char[layout.AllocSize()]);
|
||||
// // If running under ASAN, mark the padding bytes, if any, to catch
|
||||
// // memory errors.
|
||||
// layout.PoisonPadding(p_.get());
|
||||
// // Store the size in the allocation.
|
||||
// *layout.Pointer<size_t>(p_.get()) = size;
|
||||
// // Store the characters in the allocation.
|
||||
// memcpy(layout.Pointer<char>(p_.get()), s, size + 1);
|
||||
// }
|
||||
//
|
||||
// size_t size() const {
|
||||
// // Equivalent to reinterpret_cast<size_t&>(*p).
|
||||
// return *L::Partial().Pointer<size_t>(p_.get());
|
||||
// }
|
||||
//
|
||||
// const char* c_str() const {
|
||||
// // Equivalent to reinterpret_cast<char*>(p.get() + sizeof(size_t)).
|
||||
// // The argument in Partial(1) specifies that we have size_t[1] in front
|
||||
// // of the characters.
|
||||
// return L::Partial(1).Pointer<char>(p_.get());
|
||||
// }
|
||||
//
|
||||
// private:
|
||||
// // Our heap allocation contains a size_t followed by an array of chars.
|
||||
// using L = Layout<size_t, char>;
|
||||
// std::unique_ptr<unsigned char[]> p_;
|
||||
// };
|
||||
//
|
||||
// int main() {
|
||||
// CompactString s = "hello";
|
||||
// assert(s.size() == 5);
|
||||
// assert(strcmp(s.c_str(), "hello") == 0);
|
||||
// }
|
||||
//
|
||||
// DOCUMENTATION
|
||||
//
|
||||
// The interface exported by this file consists of:
|
||||
// - class `Layout<>` and its public members.
|
||||
// - The public members of class `internal_layout::LayoutImpl<>`. That class
|
||||
// isn't intended to be used directly, and its name and template parameter
|
||||
// list are internal implementation details, but the class itself provides
|
||||
// most of the functionality in this file. See comments on its members for
|
||||
// detailed documentation.
|
||||
//
|
||||
// `Layout<T1,... Tn>::Partial(count1,..., countm)` (where `m` <= `n`) returns a
|
||||
// `LayoutImpl<>` object. `Layout<T1,..., Tn> layout(count1,..., countn)`
|
||||
// creates a `Layout` object, which exposes the same functionality by inheriting
|
||||
// from `LayoutImpl<>`.
|
||||
|
||||
#ifndef ABSL_CONTAINER_INTERNAL_LAYOUT_H_ |
||||
#define ABSL_CONTAINER_INTERNAL_LAYOUT_H_ |
||||
|
||||
#include <assert.h> |
||||
#include <stddef.h> |
||||
#include <stdint.h> |
||||
#include <ostream> |
||||
#include <string> |
||||
#include <tuple> |
||||
#include <type_traits> |
||||
#include <typeinfo> |
||||
#include <utility> |
||||
|
||||
#ifdef ADDRESS_SANITIZER |
||||
#include <sanitizer/asan_interface.h> |
||||
#endif |
||||
|
||||
#include "absl/meta/type_traits.h" |
||||
#include "absl/strings/str_cat.h" |
||||
#include "absl/types/span.h" |
||||
#include "absl/utility/utility.h" |
||||
|
||||
#if defined(__GXX_RTTI) |
||||
#define ABSL_INTERNAL_HAS_CXA_DEMANGLE |
||||
#endif |
||||
|
||||
#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE |
||||
#include <cxxabi.h> |
||||
#endif |
||||
|
||||
namespace absl { |
||||
inline namespace lts_2018_12_18 { |
||||
namespace container_internal { |
||||
|
||||
// A type wrapper that instructs `Layout` to use the specific alignment for the
|
||||
// array. `Layout<..., Aligned<T, N>, ...>` has exactly the same API
|
||||
// and behavior as `Layout<..., T, ...>` except that the first element of the
|
||||
// array of `T` is aligned to `N` (the rest of the elements follow without
|
||||
// padding).
|
||||
//
|
||||
// Requires: `N >= alignof(T)` and `N` is a power of 2.
|
||||
template <class T, size_t N> |
||||
struct Aligned; |
||||
|
||||
namespace internal_layout { |
||||
|
||||
template <class T> |
||||
struct NotAligned {}; |
||||
|
||||
template <class T, size_t N> |
||||
struct NotAligned<const Aligned<T, N>> { |
||||
static_assert(sizeof(T) == 0, "Aligned<T, N> cannot be const-qualified"); |
||||
}; |
||||
|
||||
template <size_t> |
||||
using IntToSize = size_t; |
||||
|
||||
template <class> |
||||
using TypeToSize = size_t; |
||||
|
||||
template <class T> |
||||
struct Type : NotAligned<T> { |
||||
using type = T; |
||||
}; |
||||
|
||||
template <class T, size_t N> |
||||
struct Type<Aligned<T, N>> { |
||||
using type = T; |
||||
}; |
||||
|
||||
template <class T> |
||||
struct SizeOf : NotAligned<T>, std::integral_constant<size_t, sizeof(T)> {}; |
||||
|
||||
template <class T, size_t N> |
||||
struct SizeOf<Aligned<T, N>> : std::integral_constant<size_t, sizeof(T)> {}; |
||||
|
||||
// Note: workaround for https://gcc.gnu.org/PR88115
|
||||
template <class T> |
||||
struct AlignOf : NotAligned<T> { |
||||
static constexpr size_t value = alignof(T); |
||||
}; |
||||
|
||||
template <class T, size_t N> |
||||
struct AlignOf<Aligned<T, N>> { |
||||
static_assert(N % alignof(T) == 0, |
||||
"Custom alignment can't be lower than the type's alignment"); |
||||
static constexpr size_t value = N; |
||||
}; |
||||
|
||||
// Does `Ts...` contain `T`?
|
||||
template <class T, class... Ts> |
||||
using Contains = absl::disjunction<std::is_same<T, Ts>...>; |
||||
|
||||
template <class From, class To> |
||||
using CopyConst = |
||||
typename std::conditional<std::is_const<From>::value, const To, To>::type; |
||||
|
||||
// Note: We're not qualifying this with absl:: because it doesn't compile under
|
||||
// MSVC.
|
||||
template <class T> |
||||
using SliceType = Span<T>; |
||||
|
||||
// This namespace contains no types. It prevents functions defined in it from
|
||||
// being found by ADL.
|
||||
namespace adl_barrier { |
||||
|
||||
template <class Needle, class... Ts> |
||||
constexpr size_t Find(Needle, Needle, Ts...) { |
||||
static_assert(!Contains<Needle, Ts...>(), "Duplicate element type"); |
||||
return 0; |
||||
} |
||||
|
||||
template <class Needle, class T, class... Ts> |
||||
constexpr size_t Find(Needle, T, Ts...) { |
||||
return adl_barrier::Find(Needle(), Ts()...) + 1; |
||||
} |
||||
|
||||
constexpr bool IsPow2(size_t n) { return !(n & (n - 1)); } |
||||
|
||||
// Returns `q * m` for the smallest `q` such that `q * m >= n`.
|
||||
// Requires: `m` is a power of two. It's enforced by IsLegalElementType below.
|
||||
constexpr size_t Align(size_t n, size_t m) { return (n + m - 1) & ~(m - 1); } |
||||
|
||||
constexpr size_t Min(size_t a, size_t b) { return b < a ? b : a; } |
||||
|
||||
constexpr size_t Max(size_t a) { return a; } |
||||
|
||||
template <class... Ts> |
||||
constexpr size_t Max(size_t a, size_t b, Ts... rest) { |
||||
return adl_barrier::Max(b < a ? a : b, rest...); |
||||
} |
||||
|
||||
template <class T> |
||||
std::string TypeName() { |
||||
std::string out; |
||||
int status = 0; |
||||
char* demangled = nullptr; |
||||
#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE |
||||
demangled = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status); |
||||
#endif |
||||
if (status == 0 && demangled != nullptr) { // Demangling succeeded.
|
||||
absl::StrAppend(&out, "<", demangled, ">"); |
||||
free(demangled); |
||||
} else { |
||||
#if defined(__GXX_RTTI) || defined(_CPPRTTI) |
||||
absl::StrAppend(&out, "<", typeid(T).name(), ">"); |
||||
#endif |
||||
} |
||||
return out; |
||||
} |
||||
|
||||
} // namespace adl_barrier
|
||||
|
||||
template <bool C> |
||||
using EnableIf = typename std::enable_if<C, int>::type; |
||||
|
||||
// Can `T` be a template argument of `Layout`?
|
||||
template <class T> |
||||
using IsLegalElementType = std::integral_constant< |
||||
bool, !std::is_reference<T>::value && !std::is_volatile<T>::value && |
||||
!std::is_reference<typename Type<T>::type>::value && |
||||
!std::is_volatile<typename Type<T>::type>::value && |
||||
adl_barrier::IsPow2(AlignOf<T>::value)>; |
||||
|
||||
template <class Elements, class SizeSeq, class OffsetSeq> |
||||
class LayoutImpl; |
||||
|
||||
// Public base class of `Layout` and the result type of `Layout::Partial()`.
|
||||
//
|
||||
// `Elements...` contains all template arguments of `Layout` that created this
|
||||
// instance.
|
||||
//
|
||||
// `SizeSeq...` is `[0, NumSizes)` where `NumSizes` is the number of arguments
|
||||
// passed to `Layout::Partial()` or `Layout::Layout()`.
|
||||
//
|
||||
// `OffsetSeq...` is `[0, NumOffsets)` where `NumOffsets` is
|
||||
// `Min(sizeof...(Elements), NumSizes + 1)` (the number of arrays for which we
|
||||
// can compute offsets).
|
||||
template <class... Elements, size_t... SizeSeq, size_t... OffsetSeq> |
||||
class LayoutImpl<std::tuple<Elements...>, absl::index_sequence<SizeSeq...>, |
||||
absl::index_sequence<OffsetSeq...>> { |
||||
private: |
||||
static_assert(sizeof...(Elements) > 0, "At least one field is required"); |
||||
static_assert(absl::conjunction<IsLegalElementType<Elements>...>::value, |
||||
"Invalid element type (see IsLegalElementType)"); |
||||
|
||||
enum { |
||||
NumTypes = sizeof...(Elements), |
||||
NumSizes = sizeof...(SizeSeq), |
||||
NumOffsets = sizeof...(OffsetSeq), |
||||
}; |
||||
|
||||
// These are guaranteed by `Layout`.
|
||||
static_assert(NumOffsets == adl_barrier::Min(NumTypes, NumSizes + 1), |
||||
"Internal error"); |
||||
static_assert(NumTypes > 0, "Internal error"); |
||||
|
||||
// Returns the index of `T` in `Elements...`. Results in a compilation error
|
||||
// if `Elements...` doesn't contain exactly one instance of `T`.
|
||||
template <class T> |
||||
static constexpr size_t ElementIndex() { |
||||
static_assert(Contains<Type<T>, Type<typename Type<Elements>::type>...>(), |
||||
"Type not found"); |
||||
return adl_barrier::Find(Type<T>(), |
||||
Type<typename Type<Elements>::type>()...); |
||||
} |
||||
|
||||
template <size_t N> |
||||
using ElementAlignment = |
||||
AlignOf<typename std::tuple_element<N, std::tuple<Elements...>>::type>; |
||||
|
||||
public: |
||||
// Element types of all arrays packed in a tuple.
|
||||
using ElementTypes = std::tuple<typename Type<Elements>::type...>; |
||||
|
||||
// Element type of the Nth array.
|
||||
template <size_t N> |
||||
using ElementType = typename std::tuple_element<N, ElementTypes>::type; |
||||
|
||||
constexpr explicit LayoutImpl(IntToSize<SizeSeq>... sizes) |
||||
: size_{sizes...} {} |
||||
|
||||
// Alignment of the layout, equal to the strictest alignment of all elements.
|
||||
// All pointers passed to the methods of layout must be aligned to this value.
|
||||
static constexpr size_t Alignment() { |
||||
return adl_barrier::Max(AlignOf<Elements>::value...); |
||||
} |
||||
|
||||
// Offset in bytes of the Nth array.
|
||||
//
|
||||
// // int[3], 4 bytes of padding, double[4].
|
||||
// Layout<int, double> x(3, 4);
|
||||
// assert(x.Offset<0>() == 0); // The ints starts from 0.
|
||||
// assert(x.Offset<1>() == 16); // The doubles starts from 16.
|
||||
//
|
||||
// Requires: `N <= NumSizes && N < sizeof...(Ts)`.
|
||||
template <size_t N, EnableIf<N == 0> = 0> |
||||
constexpr size_t Offset() const { |
||||
return 0; |
||||
} |
||||
|
||||
template <size_t N, EnableIf<N != 0> = 0> |
||||
constexpr size_t Offset() const { |
||||
static_assert(N < NumOffsets, "Index out of bounds"); |
||||
return adl_barrier::Align( |
||||
Offset<N - 1>() + SizeOf<ElementType<N - 1>>() * size_[N - 1], |
||||
ElementAlignment<N>::value); |
||||
} |
||||
|
||||
// Offset in bytes of the array with the specified element type. There must
|
||||
// be exactly one such array and its zero-based index must be at most
|
||||
// `NumSizes`.
|
||||
//
|
||||
// // int[3], 4 bytes of padding, double[4].
|
||||
// Layout<int, double> x(3, 4);
|
||||
// assert(x.Offset<int>() == 0); // The ints starts from 0.
|
||||
// assert(x.Offset<double>() == 16); // The doubles starts from 16.
|
||||
template <class T> |
||||
constexpr size_t Offset() const { |
||||
return Offset<ElementIndex<T>()>(); |
||||
} |
||||
|
||||
// Offsets in bytes of all arrays for which the offsets are known.
|
||||
constexpr std::array<size_t, NumOffsets> Offsets() const { |
||||
return {{Offset<OffsetSeq>()...}}; |
||||
} |
||||
|
||||
// The number of elements in the Nth array. This is the Nth argument of
|
||||
// `Layout::Partial()` or `Layout::Layout()` (zero-based).
|
||||
//
|
||||
// // int[3], 4 bytes of padding, double[4].
|
||||
// Layout<int, double> x(3, 4);
|
||||
// assert(x.Size<0>() == 3);
|
||||
// assert(x.Size<1>() == 4);
|
||||
//
|
||||
// Requires: `N < NumSizes`.
|
||||
template <size_t N> |
||||
constexpr size_t Size() const { |
||||
static_assert(N < NumSizes, "Index out of bounds"); |
||||
return size_[N]; |
||||
} |
||||
|
||||
// The number of elements in the array with the specified element type.
|
||||
// There must be exactly one such array and its zero-based index must be
|
||||
// at most `NumSizes`.
|
||||
//
|
||||
// // int[3], 4 bytes of padding, double[4].
|
||||
// Layout<int, double> x(3, 4);
|
||||
// assert(x.Size<int>() == 3);
|
||||
// assert(x.Size<double>() == 4);
|
||||
template <class T> |
||||
constexpr size_t Size() const { |
||||
return Size<ElementIndex<T>()>(); |
||||
} |
||||
|
||||
// The number of elements of all arrays for which they are known.
|
||||
constexpr std::array<size_t, NumSizes> Sizes() const { |
||||
return {{Size<SizeSeq>()...}}; |
||||
} |
||||
|
||||
// Pointer to the beginning of the Nth array.
|
||||
//
|
||||
// `Char` must be `[const] [signed|unsigned] char`.
|
||||
//
|
||||
// // int[3], 4 bytes of padding, double[4].
|
||||
// Layout<int, double> x(3, 4);
|
||||
// unsigned char* p = new unsigned char[x.AllocSize()];
|
||||
// int* ints = x.Pointer<0>(p);
|
||||
// double* doubles = x.Pointer<1>(p);
|
||||
//
|
||||
// Requires: `N <= NumSizes && N < sizeof...(Ts)`.
|
||||
// Requires: `p` is aligned to `Alignment()`.
|
||||
template <size_t N, class Char> |
||||
CopyConst<Char, ElementType<N>>* Pointer(Char* p) const { |
||||
using C = typename std::remove_const<Char>::type; |
||||
static_assert( |
||||
std::is_same<C, char>() || std::is_same<C, unsigned char>() || |
||||
std::is_same<C, signed char>(), |
||||
"The argument must be a pointer to [const] [signed|unsigned] char"); |
||||
constexpr size_t alignment = Alignment(); |
||||
(void)alignment; |
||||
assert(reinterpret_cast<uintptr_t>(p) % alignment == 0); |
||||
return reinterpret_cast<CopyConst<Char, ElementType<N>>*>(p + Offset<N>()); |
||||
} |
||||
|
||||
// Pointer to the beginning of the array with the specified element type.
|
||||
// There must be exactly one such array and its zero-based index must be at
|
||||
// most `NumSizes`.
|
||||
//
|
||||
// `Char` must be `[const] [signed|unsigned] char`.
|
||||
//
|
||||
// // int[3], 4 bytes of padding, double[4].
|
||||
// Layout<int, double> x(3, 4);
|
||||
// unsigned char* p = new unsigned char[x.AllocSize()];
|
||||
// int* ints = x.Pointer<int>(p);
|
||||
// double* doubles = x.Pointer<double>(p);
|
||||
//
|
||||
// Requires: `p` is aligned to `Alignment()`.
|
||||
template <class T, class Char> |
||||
CopyConst<Char, T>* Pointer(Char* p) const { |
||||
return Pointer<ElementIndex<T>()>(p); |
||||
} |
||||
|
||||
// Pointers to all arrays for which pointers are known.
|
||||
//
|
||||
// `Char` must be `[const] [signed|unsigned] char`.
|
||||
//
|
||||
// // int[3], 4 bytes of padding, double[4].
|
||||
// Layout<int, double> x(3, 4);
|
||||
// unsigned char* p = new unsigned char[x.AllocSize()];
|
||||
//
|
||||
// int* ints;
|
||||
// double* doubles;
|
||||
// std::tie(ints, doubles) = x.Pointers(p);
|
||||
//
|
||||
// Requires: `p` is aligned to `Alignment()`.
|
||||
//
|
||||
// Note: We're not using ElementType alias here because it does not compile
|
||||
// under MSVC.
|
||||
template <class Char> |
||||
std::tuple<CopyConst< |
||||
Char, typename std::tuple_element<OffsetSeq, ElementTypes>::type>*...> |
||||
Pointers(Char* p) const { |
||||
return std::tuple<CopyConst<Char, ElementType<OffsetSeq>>*...>( |
||||
Pointer<OffsetSeq>(p)...); |
||||
} |
||||
|
||||
// The Nth array.
|
||||
//
|
||||
// `Char` must be `[const] [signed|unsigned] char`.
|
||||
//
|
||||
// // int[3], 4 bytes of padding, double[4].
|
||||
// Layout<int, double> x(3, 4);
|
||||
// unsigned char* p = new unsigned char[x.AllocSize()];
|
||||
// Span<int> ints = x.Slice<0>(p);
|
||||
// Span<double> doubles = x.Slice<1>(p);
|
||||
//
|
||||
// Requires: `N < NumSizes`.
|
||||
// Requires: `p` is aligned to `Alignment()`.
|
||||
template <size_t N, class Char> |
||||
SliceType<CopyConst<Char, ElementType<N>>> Slice(Char* p) const { |
||||
return SliceType<CopyConst<Char, ElementType<N>>>(Pointer<N>(p), Size<N>()); |
||||
} |
||||
|
||||
// The array with the specified element type. There must be exactly one
|
||||
// such array and its zero-based index must be less than `NumSizes`.
|
||||
//
|
||||
// `Char` must be `[const] [signed|unsigned] char`.
|
||||
//
|
||||
// // int[3], 4 bytes of padding, double[4].
|
||||
// Layout<int, double> x(3, 4);
|
||||
// unsigned char* p = new unsigned char[x.AllocSize()];
|
||||
// Span<int> ints = x.Slice<int>(p);
|
||||
// Span<double> doubles = x.Slice<double>(p);
|
||||
//
|
||||
// Requires: `p` is aligned to `Alignment()`.
|
||||
template <class T, class Char> |
||||
SliceType<CopyConst<Char, T>> Slice(Char* p) const { |
||||
return Slice<ElementIndex<T>()>(p); |
||||
} |
||||
|
||||
// All arrays with known sizes.
|
||||
//
|
||||
// `Char` must be `[const] [signed|unsigned] char`.
|
||||
//
|
||||
// // int[3], 4 bytes of padding, double[4].
|
||||
// Layout<int, double> x(3, 4);
|
||||
// unsigned char* p = new unsigned char[x.AllocSize()];
|
||||
//
|
||||
// Span<int> ints;
|
||||
// Span<double> doubles;
|
||||
// std::tie(ints, doubles) = x.Slices(p);
|
||||
//
|
||||
// Requires: `p` is aligned to `Alignment()`.
|
||||
//
|
||||
// Note: We're not using ElementType alias here because it does not compile
|
||||
// under MSVC.
|
||||
template <class Char> |
||||
std::tuple<SliceType<CopyConst< |
||||
Char, typename std::tuple_element<SizeSeq, ElementTypes>::type>>...> |
||||
Slices(Char* p) const { |
||||
// Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63875 (fixed
|
||||
// in 6.1).
|
||||
(void)p; |
||||
return std::tuple<SliceType<CopyConst<Char, ElementType<SizeSeq>>>...>( |
||||
Slice<SizeSeq>(p)...); |
||||
} |
||||
|
||||
// The size of the allocation that fits all arrays.
|
||||
//
|
||||
// // int[3], 4 bytes of padding, double[4].
|
||||
// Layout<int, double> x(3, 4);
|
||||
// unsigned char* p = new unsigned char[x.AllocSize()]; // 48 bytes
|
||||
//
|
||||
// Requires: `NumSizes == sizeof...(Ts)`.
|
||||
constexpr size_t AllocSize() const { |
||||
static_assert(NumTypes == NumSizes, "You must specify sizes of all fields"); |
||||
return Offset<NumTypes - 1>() + |
||||
SizeOf<ElementType<NumTypes - 1>>() * size_[NumTypes - 1]; |
||||
} |
||||
|
||||
// If built with --config=asan, poisons padding bytes (if any) in the
|
||||
// allocation. The pointer must point to a memory block at least
|
||||
// `AllocSize()` bytes in length.
|
||||
//
|
||||
// `Char` must be `[const] [signed|unsigned] char`.
|
||||
//
|
||||
// Requires: `p` is aligned to `Alignment()`.
|
||||
template <class Char, size_t N = NumOffsets - 1, EnableIf<N == 0> = 0> |
||||
void PoisonPadding(const Char* p) const { |
||||
Pointer<0>(p); // verify the requirements on `Char` and `p`
|
||||
} |
||||
|
||||
template <class Char, size_t N = NumOffsets - 1, EnableIf<N != 0> = 0> |
||||
void PoisonPadding(const Char* p) const { |
||||
static_assert(N < NumOffsets, "Index out of bounds"); |
||||
(void)p; |
||||
#ifdef ADDRESS_SANITIZER |
||||
PoisonPadding<Char, N - 1>(p); |
||||
// The `if` is an optimization. It doesn't affect the observable behaviour.
|
||||
if (ElementAlignment<N - 1>::value % ElementAlignment<N>::value) { |
||||
size_t start = |
||||
Offset<N - 1>() + SizeOf<ElementType<N - 1>>() * size_[N - 1]; |
||||
ASAN_POISON_MEMORY_REGION(p + start, Offset<N>() - start); |
||||
} |
||||
#endif |
||||
} |
||||
|
||||
// Human-readable description of the memory layout. Useful for debugging.
|
||||
// Slow.
|
||||
//
|
||||
// // char[5], 3 bytes of padding, int[3], 4 bytes of padding, followed
|
||||
// // by an unknown number of doubles.
|
||||
// auto x = Layout<char, int, double>::Partial(5, 3);
|
||||
// assert(x.DebugString() ==
|
||||
// "@0<char>(1)[5]; @8<int>(4)[3]; @24<double>(8)");
|
||||
//
|
||||
// Each field is in the following format: @offset<type>(sizeof)[size] (<type>
|
||||
// may be missing depending on the target platform). For example,
|
||||
// @8<int>(4)[3] means that at offset 8 we have an array of ints, where each
|
||||
// int is 4 bytes, and we have 3 of those ints. The size of the last field may
|
||||
// be missing (as in the example above). Only fields with known offsets are
|
||||
// described. Type names may differ across platforms: one compiler might
|
||||
// produce "unsigned*" where another produces "unsigned int *".
|
||||
std::string DebugString() const { |
||||
const auto offsets = Offsets(); |
||||
const size_t sizes[] = {SizeOf<ElementType<OffsetSeq>>()...}; |
||||
const std::string types[] = {adl_barrier::TypeName<ElementType<OffsetSeq>>()...}; |
||||
std::string res = absl::StrCat("@0", types[0], "(", sizes[0], ")"); |
||||
for (size_t i = 0; i != NumOffsets - 1; ++i) { |
||||
absl::StrAppend(&res, "[", size_[i], "]; @", offsets[i + 1], types[i + 1], |
||||
"(", sizes[i + 1], ")"); |
||||
} |
||||
// NumSizes is a constant that may be zero. Some compilers cannot see that
|
||||
// inside the if statement "size_[NumSizes - 1]" must be valid.
|
||||
int last = static_cast<int>(NumSizes) - 1; |
||||
if (NumTypes == NumSizes && last >= 0) { |
||||
absl::StrAppend(&res, "[", size_[last], "]"); |
||||
} |
||||
return res; |
||||
} |
||||
|
||||
private: |
||||
// Arguments of `Layout::Partial()` or `Layout::Layout()`.
|
||||
size_t size_[NumSizes > 0 ? NumSizes : 1]; |
||||
}; |
||||
|
||||
template <size_t NumSizes, class... Ts> |
||||
using LayoutType = LayoutImpl< |
||||
std::tuple<Ts...>, absl::make_index_sequence<NumSizes>, |
||||
absl::make_index_sequence<adl_barrier::Min(sizeof...(Ts), NumSizes + 1)>>; |
||||
|
||||
} // namespace internal_layout
|
||||
|
||||
// Descriptor of arrays of various types and sizes laid out in memory one after
|
||||
// another. See the top of the file for documentation.
|
||||
//
|
||||
// Check out the public API of internal_layout::LayoutImpl above. The type is
|
||||
// internal to the library but its methods are public, and they are inherited
|
||||
// by `Layout`.
|
||||
template <class... Ts> |
||||
class Layout : public internal_layout::LayoutType<sizeof...(Ts), Ts...> { |
||||
public: |
||||
static_assert(sizeof...(Ts) > 0, "At least one field is required"); |
||||
static_assert( |
||||
absl::conjunction<internal_layout::IsLegalElementType<Ts>...>::value, |
||||
"Invalid element type (see IsLegalElementType)"); |
||||
|
||||
// The result type of `Partial()` with `NumSizes` arguments.
|
||||
template <size_t NumSizes> |
||||
using PartialType = internal_layout::LayoutType<NumSizes, Ts...>; |
||||
|
||||
// `Layout` knows the element types of the arrays we want to lay out in
|
||||
// memory but not the number of elements in each array.
|
||||
// `Partial(size1, ..., sizeN)` allows us to specify the latter. The
|
||||
// resulting immutable object can be used to obtain pointers to the
|
||||
// individual arrays.
|
||||
//
|
||||
// It's allowed to pass fewer array sizes than the number of arrays. E.g.,
|
||||
// if all you need is to the offset of the second array, you only need to
|
||||
// pass one argument -- the number of elements in the first array.
|
||||
//
|
||||
// // int[3] followed by 4 bytes of padding and an unknown number of
|
||||
// // doubles.
|
||||
// auto x = Layout<int, double>::Partial(3);
|
||||
// // doubles start at byte 16.
|
||||
// assert(x.Offset<1>() == 16);
|
||||
//
|
||||
// If you know the number of elements in all arrays, you can still call
|
||||
// `Partial()` but it's more convenient to use the constructor of `Layout`.
|
||||
//
|
||||
// Layout<int, double> x(3, 5);
|
||||
//
|
||||
// Note: The sizes of the arrays must be specified in number of elements,
|
||||
// not in bytes.
|
||||
//
|
||||
// Requires: `sizeof...(Sizes) <= sizeof...(Ts)`.
|
||||
// Requires: all arguments are convertible to `size_t`.
|
||||
template <class... Sizes> |
||||
static constexpr PartialType<sizeof...(Sizes)> Partial(Sizes&&... sizes) { |
||||
static_assert(sizeof...(Sizes) <= sizeof...(Ts), ""); |
||||
return PartialType<sizeof...(Sizes)>(absl::forward<Sizes>(sizes)...); |
||||
} |
||||
|
||||
// Creates a layout with the sizes of all arrays specified. If you know
|
||||
// only the sizes of the first N arrays (where N can be zero), you can use
|
||||
// `Partial()` defined above. The constructor is essentially equivalent to
|
||||
// calling `Partial()` and passing in all array sizes; the constructor is
|
||||
// provided as a convenient abbreviation.
|
||||
//
|
||||
// Note: The sizes of the arrays must be specified in number of elements,
|
||||
// not in bytes.
|
||||
constexpr explicit Layout(internal_layout::TypeToSize<Ts>... sizes) |
||||
: internal_layout::LayoutType<sizeof...(Ts), Ts...>(sizes...) {} |
||||
}; |
||||
|
||||
} // namespace container_internal
|
||||
} // inline namespace lts_2018_12_18
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_CONTAINER_INTERNAL_LAYOUT_H_
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue