Export of internal Abseil changes

--
074a799119ac881b8b8ce59ef7a3166d1aa025ac by Tom Manshreck <shreck@google.com>:

nit: Add return info for StrCat

PiperOrigin-RevId: 278647298

--
d58a2a39ab6f50266cc695506ba2e86bdb45d795 by Mark Barolak <mbar@google.com>:

Stop suppressing no-nested-anon-types warnings because there aren't actually any warnings to suppress.

PiperOrigin-RevId: 278440548

--
445051bd280b9a6f608a8c80b3d7cafcc1377a03 by Abseil Team <absl-team@google.com>:

ResetThreadIdentity does not need to clear identity->waiter_state.

ResetThreadIdentity is only called by NewThreadIdentity. NewThreadIdentity is
only called by CreateThreadIdentity. CreateThreadIdentity calls
PerThreadSem::Init, which initializes identity->waiter_state, immediately after
calling NewThreadIdentity. Therefore ResetThreadIdentity does not need to clear
identity->waiter_state.
PiperOrigin-RevId: 278429844

--
c2079b664d92be40d5e365abcca4e9b3505a75a6 by Abseil Team <absl-team@google.com>:

Delete the f->header.magic check in LowLevelAlloc::Free().

The f->header.magic check in LowLevelAlloc::Free() is redundant, because
AddToFreeList() will immediately perform the same check.

Also fix a typo in the comment that documents the lock requirements for
Next(). The comment should say "L >= arena->mu", which is equivalent to
EXCLUSIVE_LOCKS_REQUIRED(arena->mu).

NOTE: LowLevelAlloc::Free() performs the f->header.magic check without
holding the arena lock. This may have caused the TSAN data race warning
reported in bug 143697235.
PiperOrigin-RevId: 278414140

--
5534f35ce677165700117d868f51607ed1f0d73b by Greg Falcon <gfalcon@google.com>:

Add an internal (unsupported) PiecewiseCombiner class to allow hashing buffers piecewise.

PiperOrigin-RevId: 278388902
GitOrigin-RevId: 074a799119ac881b8b8ce59ef7a3166d1aa025ac
Change-Id: I61734850cbbb01c7585e8c736a5bb56e416512a8
pull/400/head
Abseil Team 5 years ago committed by Xiaoyi Zhang
parent 20de2db748
commit e96ae2203b
  1. 4
      absl/base/internal/low_level_alloc.cc
  2. 2
      absl/copts/GENERATED_AbseilCopts.cmake
  3. 2
      absl/copts/GENERATED_copts.bzl
  4. 1
      absl/copts/copts.py
  5. 2
      absl/hash/BUILD.bazel
  6. 114
      absl/hash/hash_test.cc
  7. 30
      absl/hash/internal/hash.cc
  8. 121
      absl/hash/internal/hash.h
  9. 13
      absl/hash/internal/spy_hash_state.h
  10. 3
      absl/strings/str_cat.h
  11. 1
      absl/synchronization/internal/create_thread_identity.cc

@ -447,7 +447,7 @@ static inline uintptr_t RoundUp(uintptr_t addr, uintptr_t align) {
// that the freelist is in the correct order, that it // that the freelist is in the correct order, that it
// consists of regions marked "unallocated", and that no two regions // consists of regions marked "unallocated", and that no two regions
// are adjacent in memory (they should have been coalesced). // are adjacent in memory (they should have been coalesced).
// L < arena->mu // L >= arena->mu
static AllocList *Next(int i, AllocList *prev, LowLevelAlloc::Arena *arena) { static AllocList *Next(int i, AllocList *prev, LowLevelAlloc::Arena *arena) {
ABSL_RAW_CHECK(i < prev->levels, "too few levels in Next()"); ABSL_RAW_CHECK(i < prev->levels, "too few levels in Next()");
AllocList *next = prev->next[i]; AllocList *next = prev->next[i];
@ -508,8 +508,6 @@ void LowLevelAlloc::Free(void *v) {
if (v != nullptr) { if (v != nullptr) {
AllocList *f = reinterpret_cast<AllocList *>( AllocList *f = reinterpret_cast<AllocList *>(
reinterpret_cast<char *>(v) - sizeof (f->header)); reinterpret_cast<char *>(v) - sizeof (f->header));
ABSL_RAW_CHECK(f->header.magic == Magic(kMagicAllocated, &f->header),
"bad magic number in Free()");
LowLevelAlloc::Arena *arena = f->header.arena; LowLevelAlloc::Arena *arena = f->header.arena;
ArenaLock section(arena); ArenaLock section(arena);
AddToFreelist(v, arena); AddToFreelist(v, arena);

@ -23,7 +23,6 @@ list(APPEND ABSL_CLANG_CL_FLAGS
"-Wno-gcc-compat" "-Wno-gcc-compat"
"-Wno-global-constructors" "-Wno-global-constructors"
"-Wno-exit-time-destructors" "-Wno-exit-time-destructors"
"-Wno-nested-anon-types"
"-Wno-non-modular-include-in-module" "-Wno-non-modular-include-in-module"
"-Wno-old-style-cast" "-Wno-old-style-cast"
"-Wno-range-loop-analysis" "-Wno-range-loop-analysis"
@ -123,7 +122,6 @@ list(APPEND ABSL_LLVM_FLAGS
"-Wno-gcc-compat" "-Wno-gcc-compat"
"-Wno-global-constructors" "-Wno-global-constructors"
"-Wno-exit-time-destructors" "-Wno-exit-time-destructors"
"-Wno-nested-anon-types"
"-Wno-non-modular-include-in-module" "-Wno-non-modular-include-in-module"
"-Wno-old-style-cast" "-Wno-old-style-cast"
"-Wno-range-loop-analysis" "-Wno-range-loop-analysis"

@ -24,7 +24,6 @@ ABSL_CLANG_CL_FLAGS = [
"-Wno-gcc-compat", "-Wno-gcc-compat",
"-Wno-global-constructors", "-Wno-global-constructors",
"-Wno-exit-time-destructors", "-Wno-exit-time-destructors",
"-Wno-nested-anon-types",
"-Wno-non-modular-include-in-module", "-Wno-non-modular-include-in-module",
"-Wno-old-style-cast", "-Wno-old-style-cast",
"-Wno-range-loop-analysis", "-Wno-range-loop-analysis",
@ -124,7 +123,6 @@ ABSL_LLVM_FLAGS = [
"-Wno-gcc-compat", "-Wno-gcc-compat",
"-Wno-global-constructors", "-Wno-global-constructors",
"-Wno-exit-time-destructors", "-Wno-exit-time-destructors",
"-Wno-nested-anon-types",
"-Wno-non-modular-include-in-module", "-Wno-non-modular-include-in-module",
"-Wno-old-style-cast", "-Wno-old-style-cast",
"-Wno-range-loop-analysis", "-Wno-range-loop-analysis",

@ -56,7 +56,6 @@ LLVM_DISABLE_WARNINGS_FLAGS = [
"-Wno-global-constructors", "-Wno-global-constructors",
"-Wno-exit-time-destructors", "-Wno-exit-time-destructors",
### ###
"-Wno-nested-anon-types",
"-Wno-non-modular-include-in-module", "-Wno-non-modular-include-in-module",
"-Wno-old-style-cast", "-Wno-old-style-cast",
# Warns on preferred usage of non-POD types such as string_view # Warns on preferred usage of non-POD types such as string_view

@ -71,9 +71,9 @@ cc_test(
deps = [ deps = [
":hash", ":hash",
":hash_testing", ":hash_testing",
":spy_hash_state",
"//absl/base:core_headers", "//absl/base:core_headers",
"//absl/container:flat_hash_set", "//absl/container:flat_hash_set",
"//absl/hash:spy_hash_state",
"//absl/meta:type_traits", "//absl/meta:type_traits",
"//absl/numeric:int128", "//absl/numeric:int128",
"@com_google_googletest//:gtest_main", "@com_google_googletest//:gtest_main",

@ -274,8 +274,8 @@ TEST(HashValueTest, Strings) {
const std::string small = "foo"; const std::string small = "foo";
const std::string dup = "foofoo"; const std::string dup = "foofoo";
const std::string large = "large"; const std::string large = std::string(2048, 'x'); // multiple of chunk size
const std::string huge = std::string(5000, 'a'); const std::string huge = std::string(5000, 'a'); // not a multiple
EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
std::string(), absl::string_view(), std::string(), absl::string_view(),
@ -378,6 +378,116 @@ struct Private {
} }
}; };
// Test helper for combine_piecewise_buffer. It holds a string_view to the
// buffer-to-be-hashed. Its AbslHashValue specialization will split up its
// contents at the character offsets requested.
class PiecewiseHashTester {
public:
// Create a hash view of a buffer to be hashed contiguously.
explicit PiecewiseHashTester(absl::string_view buf)
: buf_(buf), piecewise_(false), split_locations_() {}
// Create a hash view of a buffer to be hashed piecewise, with breaks at the
// given locations.
PiecewiseHashTester(absl::string_view buf, std::set<size_t> split_locations)
: buf_(buf),
piecewise_(true),
split_locations_(std::move(split_locations)) {}
template <typename H>
friend H AbslHashValue(H h, const PiecewiseHashTester& p) {
if (!p.piecewise_) {
return H::combine_contiguous(std::move(h), p.buf_.data(), p.buf_.size());
}
absl::hash_internal::PiecewiseCombiner combiner;
if (p.split_locations_.empty()) {
h = combiner.add_buffer(std::move(h), p.buf_.data(), p.buf_.size());
return combiner.finalize(std::move(h));
}
size_t begin = 0;
for (size_t next : p.split_locations_) {
absl::string_view chunk = p.buf_.substr(begin, next - begin);
h = combiner.add_buffer(std::move(h), chunk.data(), chunk.size());
begin = next;
}
absl::string_view last_chunk = p.buf_.substr(begin);
if (!last_chunk.empty()) {
h = combiner.add_buffer(std::move(h), last_chunk.data(),
last_chunk.size());
}
return combiner.finalize(std::move(h));
}
private:
absl::string_view buf_;
bool piecewise_;
std::set<size_t> split_locations_;
};
// Dummy object that hashes as two distinct contiguous buffers, "foo" followed
// by "bar"
struct DummyFooBar {
template <typename H>
friend H AbslHashValue(H h, const DummyFooBar&) {
const char* foo = "foo";
const char* bar = "bar";
h = H::combine_contiguous(std::move(h), foo, 3);
h = H::combine_contiguous(std::move(h), bar, 3);
return std::move(h);
}
};
TEST(HashValueTest, CombinePiecewiseBuffer) {
absl::Hash<PiecewiseHashTester> hash;
// Check that hashing an empty buffer through the piecewise API works.
EXPECT_EQ(hash(PiecewiseHashTester("")), hash(PiecewiseHashTester("", {})));
// Similarly, small buffers should give consistent results
EXPECT_EQ(hash(PiecewiseHashTester("foobar")),
hash(PiecewiseHashTester("foobar", {})));
EXPECT_EQ(hash(PiecewiseHashTester("foobar")),
hash(PiecewiseHashTester("foobar", {3})));
// But hashing "foobar" in pieces gives a different answer than hashing "foo"
// contiguously, then "bar" contiguously.
EXPECT_NE(hash(PiecewiseHashTester("foobar", {3})),
absl::Hash<DummyFooBar>()(DummyFooBar{}));
// Test hashing a large buffer incrementally, broken up in several different
// ways. Arrange for breaks on and near the stride boundaries to look for
// off-by-one errors in the implementation.
//
// This test is run on a buffer that is a multiple of the stride size, and one
// that isn't.
for (size_t big_buffer_size : {1024 * 2 + 512, 1024 * 3}) {
SCOPED_TRACE(big_buffer_size);
std::string big_buffer;
for (int i = 0; i < big_buffer_size; ++i) {
// Arbitrary std::string
big_buffer.push_back(32 + (i * (i / 3)) % 64);
}
auto big_buffer_hash = hash(PiecewiseHashTester(big_buffer));
const int possible_breaks = 9;
size_t breaks[possible_breaks] = {1, 512, 1023, 1024, 1025,
1536, 2047, 2048, 2049};
for (unsigned test_mask = 0; test_mask < (1u << possible_breaks);
++test_mask) {
SCOPED_TRACE(test_mask);
std::set<size_t> break_locations;
for (int j = 0; j < possible_breaks; ++j) {
if (test_mask & (1u << j)) {
break_locations.insert(breaks[j]);
}
}
EXPECT_EQ(
hash(PiecewiseHashTester(big_buffer, std::move(break_locations))),
big_buffer_hash);
}
}
}
TEST(HashValueTest, PrivateSanity) { TEST(HashValueTest, PrivateSanity) {
// Sanity check that Private is working as the tests below expect it to work. // Sanity check that Private is working as the tests below expect it to work.
EXPECT_TRUE(is_hashable<Private>::value); EXPECT_TRUE(is_hashable<Private>::value);

@ -17,6 +17,36 @@
namespace absl { namespace absl {
namespace hash_internal { namespace hash_internal {
uint64_t CityHashState::CombineLargeContiguousImpl32(uint64_t state,
const unsigned char* first,
size_t len) {
while (len >= PiecewiseChunkSize()) {
state =
Mix(state, absl::hash_internal::CityHash32(reinterpret_cast<const char*>(first),
PiecewiseChunkSize()));
len -= PiecewiseChunkSize();
first += PiecewiseChunkSize();
}
// Handle the remainder.
return CombineContiguousImpl(state, first, len,
std::integral_constant<int, 4>{});
}
uint64_t CityHashState::CombineLargeContiguousImpl64(uint64_t state,
const unsigned char* first,
size_t len) {
while (len >= PiecewiseChunkSize()) {
state =
Mix(state, absl::hash_internal::CityHash64(reinterpret_cast<const char*>(first),
PiecewiseChunkSize()));
len -= PiecewiseChunkSize();
first += PiecewiseChunkSize();
}
// Handle the remainder.
return CombineContiguousImpl(state, first, len,
std::integral_constant<int, 8>{});
}
ABSL_CONST_INIT const void* const CityHashState::kSeed = &kSeed; ABSL_CONST_INIT const void* const CityHashState::kSeed = &kSeed;
} // namespace hash_internal } // namespace hash_internal

@ -52,6 +52,12 @@
namespace absl { namespace absl {
namespace hash_internal { namespace hash_internal {
class PiecewiseCombiner;
// Internal detail: Large buffers are hashed in smaller chunks. This function
// returns the size of these chunks.
constexpr int PiecewiseChunkSize() { return 1024; }
// HashStateBase // HashStateBase
// //
// A hash state object represents an intermediate state in the computation // A hash state object represents an intermediate state in the computation
@ -68,7 +74,7 @@ namespace hash_internal {
// //
// `static H combine_contiguous(H state, const unsigned char*, size_t)`. // `static H combine_contiguous(H state, const unsigned char*, size_t)`.
// //
// `HashStateBase` will provide a complete implementations for a hash state // `HashStateBase` will provide a complete implementation for a hash state
// object in terms of this method. // object in terms of this method.
// //
// Example: // Example:
@ -117,6 +123,9 @@ class HashStateBase {
// for-loop instead. // for-loop instead.
template <typename T> template <typename T>
static H combine_contiguous(H state, const T* data, size_t size); static H combine_contiguous(H state, const T* data, size_t size);
private:
friend class PiecewiseCombiner;
}; };
// is_uniquely_represented // is_uniquely_represented
@ -187,6 +196,61 @@ H hash_bytes(H hash_state, const T& value) {
return H::combine_contiguous(std::move(hash_state), start, sizeof(value)); return H::combine_contiguous(std::move(hash_state), start, sizeof(value));
} }
// PiecewiseCombiner
//
// PiecewiseCombiner is an internal-only helper class for hashing a piecewise
// buffer of `char` or `unsigned char` as though it were contiguous. This class
// provides two methods:
//
// H add_buffer(state, data, size)
// H finalize(state)
//
// `add_buffer` can be called zero or more times, followed by a single call to
// `finalize`. This will produce the same hash expansion as concatenating each
// buffer piece into a single contiguous buffer, and passing this to
// `H::combine_contiguous`.
//
// Example usage:
// PiecewiseCombiner combiner;
// for (const auto& piece : pieces) {
// state = combiner.add_buffer(std::move(state), piece.data, piece.size);
// }
// return combiner.finalize(std::move(state));
class PiecewiseCombiner {
public:
PiecewiseCombiner() : position_(0) {}
PiecewiseCombiner(const PiecewiseCombiner&) = delete;
PiecewiseCombiner& operator=(const PiecewiseCombiner&) = delete;
// PiecewiseCombiner::add_buffer()
//
// Appends the given range of bytes to the sequence to be hashed, which may
// modify the provided hash state.
template <typename H>
H add_buffer(H state, const unsigned char* data, size_t size);
template <typename H>
H add_buffer(H state, const char* data, size_t size) {
return add_buffer(std::move(state),
reinterpret_cast<const unsigned char*>(data), size);
}
// PiecewiseCombiner::finalize()
//
// Finishes combining the hash sequence, which may may modify the provided
// hash state.
//
// Once finalize() is called, add_buffer() may no longer be called. The
// resulting hash state will be the same as if the pieces passed to
// add_buffer() were concatenated into a single flat buffer, and then provided
// to H::combine_contiguous().
template <typename H>
H finalize(H state);
private:
unsigned char buf_[PiecewiseChunkSize()];
size_t position_;
};
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// AbslHashValue for Basic Types // AbslHashValue for Basic Types
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -709,6 +773,16 @@ class CityHashState : public HashStateBase<CityHashState> {
std::integral_constant<int, 8> std::integral_constant<int, 8>
/* sizeof_size_t*/); /* sizeof_size_t*/);
// Slow dispatch path for calls to CombineContiguousImpl with a size argument
// larger than PiecewiseChunkSize(). Has the same effect as calling
// CombineContiguousImpl() repeatedly with the chunk stride size.
static uint64_t CombineLargeContiguousImpl32(uint64_t state,
const unsigned char* first,
size_t len);
static uint64_t CombineLargeContiguousImpl64(uint64_t state,
const unsigned char* first,
size_t len);
// Reads 9 to 16 bytes from p. // Reads 9 to 16 bytes from p.
// The first 8 bytes are in .first, the rest (zero padded) bytes are in // The first 8 bytes are in .first, the rest (zero padded) bytes are in
// .second. // .second.
@ -776,6 +850,9 @@ inline uint64_t CityHashState::CombineContiguousImpl(
// multiplicative hash. // multiplicative hash.
uint64_t v; uint64_t v;
if (len > 8) { if (len > 8) {
if (ABSL_PREDICT_FALSE(len > PiecewiseChunkSize())) {
return CombineLargeContiguousImpl32(state, first, len);
}
v = absl::hash_internal::CityHash32(reinterpret_cast<const char*>(first), len); v = absl::hash_internal::CityHash32(reinterpret_cast<const char*>(first), len);
} else if (len >= 4) { } else if (len >= 4) {
v = Read4To8(first, len); v = Read4To8(first, len);
@ -796,6 +873,9 @@ inline uint64_t CityHashState::CombineContiguousImpl(
// multiplicative hash. // multiplicative hash.
uint64_t v; uint64_t v;
if (len > 16) { if (len > 16) {
if (ABSL_PREDICT_FALSE(len > PiecewiseChunkSize())) {
return CombineLargeContiguousImpl64(state, first, len);
}
v = absl::hash_internal::CityHash64(reinterpret_cast<const char*>(first), len); v = absl::hash_internal::CityHash64(reinterpret_cast<const char*>(first), len);
} else if (len > 8) { } else if (len > 8) {
auto p = Read9To16(first, len); auto p = Read9To16(first, len);
@ -812,7 +892,6 @@ inline uint64_t CityHashState::CombineContiguousImpl(
return Mix(state, v); return Mix(state, v);
} }
struct AggregateBarrier {}; struct AggregateBarrier {};
// HashImpl // HashImpl
@ -849,6 +928,44 @@ template <typename T>
H HashStateBase<H>::combine_contiguous(H state, const T* data, size_t size) { H HashStateBase<H>::combine_contiguous(H state, const T* data, size_t size) {
return hash_internal::hash_range_or_bytes(std::move(state), data, size); return hash_internal::hash_range_or_bytes(std::move(state), data, size);
} }
// HashStateBase::PiecewiseCombiner::add_buffer()
template <typename H>
H PiecewiseCombiner::add_buffer(H state, const unsigned char* data,
size_t size) {
if (position_ + size < PiecewiseChunkSize()) {
// This partial chunk does not fill our existing buffer
memcpy(buf_ + position_, data, size);
position_ += size;
return std::move(state);
}
// Complete the buffer and hash it
const size_t bytes_needed = PiecewiseChunkSize() - position_;
memcpy(buf_ + position_, data, bytes_needed);
state = H::combine_contiguous(std::move(state), buf_, PiecewiseChunkSize());
data += bytes_needed;
size -= bytes_needed;
// Hash whatever chunks we can without copying
while (size >= PiecewiseChunkSize()) {
state = H::combine_contiguous(std::move(state), data, PiecewiseChunkSize());
data += PiecewiseChunkSize();
size -= PiecewiseChunkSize();
}
// Fill the buffer with the remainder
memcpy(buf_, data, size);
position_ = size;
return std::move(state);
}
// HashStateBase::PiecewiseCombiner::finalize()
template <typename H>
H PiecewiseCombiner::finalize(H state) {
// Hash the remainder left in the buffer, which may be empty
return H::combine_contiguous(std::move(state), buf_, position_);
}
} // namespace hash_internal } // namespace hash_internal
} // namespace absl } // namespace absl

@ -146,6 +146,19 @@ class SpyHashStateImpl : public HashStateBase<SpyHashStateImpl<T>> {
static SpyHashStateImpl combine_contiguous(SpyHashStateImpl hash_state, static SpyHashStateImpl combine_contiguous(SpyHashStateImpl hash_state,
const unsigned char* begin, const unsigned char* begin,
size_t size) { size_t size) {
const size_t large_chunk_stride = PiecewiseChunkSize();
if (size > large_chunk_stride) {
// Combining a large contiguous buffer must have the same effect as
// doing it piecewise by the stride length, followed by the (possibly
// empty) remainder.
while (size >= large_chunk_stride) {
hash_state = SpyHashStateImpl::combine_contiguous(
std::move(hash_state), begin, large_chunk_stride);
begin += large_chunk_stride;
size -= large_chunk_stride;
}
}
hash_state.hash_representation_.emplace_back( hash_state.hash_representation_.emplace_back(
reinterpret_cast<const char*>(begin), size); reinterpret_cast<const char*>(begin), size);
return hash_state; return hash_state;

@ -290,7 +290,8 @@ class AlphaNum {
// StrCat() // StrCat()
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// //
// Merges given strings or numbers, using no delimiter(s). // Merges given strings or numbers, using no delimiter(s), returning the merged
// result as a string.
// //
// `StrCat()` is designed to be the fastest possible way to construct a string // `StrCat()` is designed to be the fastest possible way to construct a string
// out of a mix of raw C strings, string_views, strings, bool values, // out of a mix of raw C strings, string_views, strings, bool values,

@ -86,7 +86,6 @@ static void ResetThreadIdentity(base_internal::ThreadIdentity* identity) {
pts->wake = false; pts->wake = false;
pts->cond_waiter = false; pts->cond_waiter = false;
pts->all_locks = nullptr; pts->all_locks = nullptr;
identity->waiter_state = {};
identity->blocked_count_ptr = nullptr; identity->blocked_count_ptr = nullptr;
identity->ticker.store(0, std::memory_order_relaxed); identity->ticker.store(0, std::memory_order_relaxed);
identity->wait_start.store(0, std::memory_order_relaxed); identity->wait_start.store(0, std::memory_order_relaxed);

Loading…
Cancel
Save