165 lines
5.5 KiB
C++
165 lines
5.5 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <functional>
|
|
#include <string>
|
|
|
|
#include <folly/Range.h>
|
|
#include <folly/Traits.h>
|
|
#include <folly/container/HeterogeneousAccess-fwd.h>
|
|
#include <folly/hash/Hash.h>
|
|
|
|
namespace folly {
|
|
|
|
// folly::HeterogeneousAccessEqualTo<T>, and
|
|
// folly::HeterogeneousAccessHash<T> are functors suitable as defaults
|
|
// for containers that support heterogeneous access. When possible, they
|
|
// will be marked as transparent. When no transparent implementation
|
|
// is available then they fall back to std::equal_to and std::hash
|
|
// respectively. Since the fallbacks are not marked as transparent,
|
|
// heterogeneous lookup won't be available in that case. A corresponding
|
|
// HeterogeneousAccessLess<T> could be easily added if desired.
|
|
//
|
|
// If T can be implicitly converted to a StringPiece or
|
|
// to a Range<T::value_type const*> that is hashable, then
|
|
// HeterogeneousAccess{EqualTo,Hash}<T> will be transparent without any
|
|
// additional work. In practice this is true for T that can be convered to
|
|
// StringPiece or Range<IntegralType const*>. This includes std::string,
|
|
// std::string_view (when available), std::array, folly::Range,
|
|
// std::vector, and folly::small_vector.
|
|
//
|
|
// Additional specializations of HeterogeneousAccess*<T> should go in
|
|
// the header that declares T. Don't forget to typedef is_transparent to
|
|
// void and folly_is_avalanching to std::true_type in the specializations.
|
|
|
|
template <typename T, typename Enable>
|
|
struct HeterogeneousAccessEqualTo : std::equal_to<T> {};
|
|
|
|
template <typename T, typename Enable>
|
|
struct HeterogeneousAccessHash : std::hash<T> {
|
|
using folly_is_avalanching = IsAvalanchingHasher<std::hash<T>, T>;
|
|
};
|
|
|
|
//////// strings
|
|
|
|
namespace detail {
|
|
|
|
template <typename T, typename Enable = void>
|
|
struct ValueTypeForTransparentConversionToRange {
|
|
using type = char;
|
|
};
|
|
|
|
// We assume that folly::hasher<folly::Range<T const*>> won't be enabled
|
|
// when it would be lower quality than std::hash<U> for a U that is
|
|
// convertible to folly::Range<T const*>.
|
|
template <typename T>
|
|
struct ValueTypeForTransparentConversionToRange<
|
|
T,
|
|
void_t<decltype(
|
|
std::declval<hasher<Range<typename T::value_type const*>>>()(
|
|
std::declval<Range<typename T::value_type const*>>()))>> {
|
|
using type = std::remove_const_t<typename T::value_type>;
|
|
};
|
|
|
|
template <typename T>
|
|
using TransparentlyConvertibleToRange = std::is_convertible<
|
|
T,
|
|
Range<typename ValueTypeForTransparentConversionToRange<T>::type const*>>;
|
|
|
|
template <typename T>
|
|
struct TransparentRangeEqualTo {
|
|
using is_transparent = void;
|
|
|
|
template <typename U1, typename U2>
|
|
bool operator()(U1 const& lhs, U2 const& rhs) const {
|
|
return Range<T const*>{lhs} == Range<T const*>{rhs};
|
|
}
|
|
|
|
// This overload is not required for functionality, but
|
|
// guarantees that replacing std::equal_to<std::string> with
|
|
// HeterogeneousAccessEqualTo<std::string> is truly zero overhead
|
|
bool operator()(std::string const& lhs, std::string const& rhs) const {
|
|
return lhs == rhs;
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
struct TransparentRangeHash {
|
|
using is_transparent = void;
|
|
using folly_is_avalanching = std::true_type;
|
|
|
|
private:
|
|
template <typename U>
|
|
static std::size_t hashImpl(Range<U const*> piece) {
|
|
return hasher<Range<U const*>>{}(piece);
|
|
}
|
|
|
|
static std::size_t hashImpl(StringPiece piece) {
|
|
#if defined(_GLIBCXX_STRING)
|
|
return std::_Hash_impl::hash(piece.begin(), piece.size());
|
|
#elif defined(_LIBCPP_STRING)
|
|
return std::__do_string_hash(piece.begin(), piece.end());
|
|
#else
|
|
return hasher<StringPiece>{}(piece);
|
|
#endif
|
|
}
|
|
|
|
public:
|
|
template <typename U>
|
|
std::size_t operator()(U const& stringish) const {
|
|
return hashImpl(Range<T const*>{stringish});
|
|
}
|
|
|
|
// Neither this overload nor the platform-conditional compilation
|
|
// is required for functionality, but implementing it this
|
|
// way guarantees that replacing std::hash<std::string> with
|
|
// HeterogeneousAccessHash<std::string> is actually zero overhead
|
|
// in the case that the underlying implementations make different
|
|
// optimality tradeoffs (short versus long string performance, for
|
|
// example). If folly::hasher<StringPiece> dominated the performance
|
|
// of std::hash<std::string> then we should consider using it all of
|
|
// the time.
|
|
std::size_t operator()(std::string const& str) const {
|
|
#if defined(_GLIBCXX_STRING) || defined(_LIBCPP_STRING)
|
|
return std::hash<std::string>{}(str);
|
|
#else
|
|
return hasher<StringPiece>{}(str);
|
|
#endif
|
|
}
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
template <typename T>
|
|
struct HeterogeneousAccessEqualTo<
|
|
T,
|
|
std::enable_if_t<detail::TransparentlyConvertibleToRange<T>::value>>
|
|
: detail::TransparentRangeEqualTo<
|
|
typename detail::ValueTypeForTransparentConversionToRange<T>::type> {
|
|
};
|
|
|
|
template <typename T>
|
|
struct HeterogeneousAccessHash<
|
|
T,
|
|
std::enable_if_t<detail::TransparentlyConvertibleToRange<T>::value>>
|
|
: detail::TransparentRangeHash<
|
|
typename detail::ValueTypeForTransparentConversionToRange<T>::type> {
|
|
};
|
|
|
|
} // namespace folly
|