diff options
| author | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-11 15:07:35 -0300 |
|---|---|---|
| committer | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-11 15:07:35 -0300 |
| commit | 4cb8841196d0625dfa3825aa326f071cd27c7b8b (patch) | |
| tree | 1682a647d4463397c119183369ae6f750d5fdcff /src/mongo/util/immutable | |
| parent | aa03c6362cbaa767638e6eed9b031d86dd2643d1 (diff) | |
| parent | 8f0827553e09872941945a093b647a4211a9db7f (diff) | |
Update upstream source from tag 'upstream/6.0.0'master
Update to upstream version '6.0.0'
with Debian dir 5604a80ec1c96ca76f25f40d78e6ef855abec322
Diffstat (limited to 'src/mongo/util/immutable')
| -rw-r--r-- | src/mongo/util/immutable/README.md | 35 | ||||
| -rw-r--r-- | src/mongo/util/immutable/SConscript | 29 | ||||
| -rw-r--r-- | src/mongo/util/immutable/details/map.h | 233 | ||||
| -rw-r--r-- | src/mongo/util/immutable/details/memory_policy.h | 63 | ||||
| -rw-r--r-- | src/mongo/util/immutable/details/set.h | 110 | ||||
| -rw-r--r-- | src/mongo/util/immutable/immutable_absl_comparison_bm.cpp | 168 | ||||
| -rw-r--r-- | src/mongo/util/immutable/immutable_ordered_test.cpp | 1336 | ||||
| -rw-r--r-- | src/mongo/util/immutable/immutable_std_comparison_bm.cpp | 168 | ||||
| -rw-r--r-- | src/mongo/util/immutable/immutable_unordered_test.cpp | 234 | ||||
| -rw-r--r-- | src/mongo/util/immutable/immutable_vector_test.cpp | 126 | ||||
| -rw-r--r-- | src/mongo/util/immutable/map.h | 358 | ||||
| -rw-r--r-- | src/mongo/util/immutable/set.h | 199 | ||||
| -rw-r--r-- | src/mongo/util/immutable/unordered_map.h | 65 | ||||
| -rw-r--r-- | src/mongo/util/immutable/unordered_set.h | 65 | ||||
| -rw-r--r-- | src/mongo/util/immutable/vector.h | 61 |
15 files changed, 0 insertions, 3250 deletions
diff --git a/src/mongo/util/immutable/README.md b/src/mongo/util/immutable/README.md deleted file mode 100644 index 74f5e07259d..00000000000 --- a/src/mongo/util/immutable/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Immutable Containers - -This folder contains a number of _immutable_ container classes. Sometimes called _persistent data -structures_ in the literature, these classes provide interfaces similar to STL containers with one -key difference. Operations which "modify" the container are `const` and return a modified copy of -the container rather than modifying it in-place. This makes the containers implicitly thread-safe to -read and write, but external synchronization will still be needed in many cases to address isolation -and serializability concerns (i.e. -[MVCC](https://en.wikipedia.org/wiki/Multiversion_concurrency_control)). - -## When To Use Immutable Containers - -If the container will be copied frequently, e.g. to support a copy-on-write pattern, consider using -an immutable container. Otherwise, a standard container may make more sense. - -## Supported Containers - -The currently supported containers are all based on classes from the -[`immer`](https://sinusoid.es/immer/) library. - - - [`immutable::map`](map.h): ordered map interface backed by `immer::flex_vector` - - [`immutable::set`](set.h): ordered set interface backed by `immer::flex_vector` - - [`immutable::unordered_map`](unordered_map.h): typedef for `immer:map` - - [`immutable::unordered_set`](unordered_set.h): typedef for `immer:set` - - [`immutable::vector`](vector.h): typedef for `immer::vector` - -Both ordered and unordered map and set variants support heterogeneous lookup. - -## A Note on Performance - -The internal implementations of these containers are optimized to support an internal copy-on-write -pattern so that copies and modifications are $O(log(n))$ or even $O(1)$. However, the constants on -these runtime guarantees, as well as those for lookups, are typically worse than those of the -corresponding STL or Abseil containers. For this reason, they should not be considered a -general-purpose drop-in replacement. diff --git a/src/mongo/util/immutable/SConscript b/src/mongo/util/immutable/SConscript deleted file mode 100644 index 72e517d93c5..00000000000 --- a/src/mongo/util/immutable/SConscript +++ /dev/null @@ -1,29 +0,0 @@ -# -*- mode: python -*- - -Import("env") - -env = env.Clone() - -env.CppUnitTest( - target='immutable_test', - source=[ - 'immutable_ordered_test.cpp', - 'immutable_unordered_test.cpp', - 'immutable_vector_test.cpp', - ], - LIBDEPS=[], -) - -env.Benchmark( - target='immutable_absl_comparison_bm', - source=[ - 'immutable_absl_comparison_bm.cpp', - ], -) - -env.Benchmark( - target='immutable_std_comparison_bm', - source=[ - 'immutable_std_comparison_bm.cpp', - ], -) diff --git a/src/mongo/util/immutable/details/map.h b/src/mongo/util/immutable/details/map.h deleted file mode 100644 index a66db19b412..00000000000 --- a/src/mongo/util/immutable/details/map.h +++ /dev/null @@ -1,233 +0,0 @@ -/** - * Copyright (C) 2023-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#pragma once - -#include <algorithm> - -namespace mongo::immutable::details::map { - -template <typename map, typename SearchKey> -bool equal(const typename map::key_type& a, const SearchKey& b) { - return !typename map::comp{}(a, b) && !typename map::comp{}(b, a); -} - -template <typename map, typename SearchKey> -[[nodiscard]] typename map::iterator lower_bound(const typename map::storage_type& storage, - const SearchKey& key) { - return std::lower_bound(storage.begin(), - storage.end(), - key, - [](const typename map::value_type& a, const SearchKey& b) -> bool { - return typename map::comp{}(a.first, b); - }); -} - -template <typename map, typename SearchKey> -[[nodiscard]] typename map::iterator find(const typename map::storage_type& storage, - const SearchKey& key) { - auto it = lower_bound<map>(storage, key); - if (it != storage.end() && equal<map>(it->first, key)) { - return it; - } - - return storage.end(); -} - -template <typename map, typename S, typename K, typename V> -[[nodiscard]] typename map::storage_type insert(S&& storage, K&& key, V&& value) { - auto it = lower_bound<map>(storage, key); - if (it != storage.end() && equal<map>(it->first, key)) { - return std::forward<S>(storage); - } - if (it == storage.end()) { - return std::forward<S>(storage).push_back( - std::make_pair(std::forward<K>(key), std::forward<V>(value))); - } - return std::forward<S>(storage).insert( - it.index(), std::make_pair(std::forward<K>(key), std::forward<V>(value))); -} - -template <typename map, typename S, typename K, typename V> -[[nodiscard]] typename map::storage_type insert(S&& storage, - typename map::iterator it, - K&& key, - V&& value) { - if (it != storage.end() && equal<map>(it->first, key)) { - return std::forward<S>(storage); - } - - if (it == storage.end()) { - if (typename map::comp{}(storage[storage.size() - 1].first, key)) { - return std::forward<S>(storage).push_back( - std::make_pair(std::forward<K>(key), std::forward<V>(value))); - } - return insert<map>(std::forward<S>(storage), std::forward<K>(key), std::forward<V>(value)); - } - - if (typename map::comp{}(key, it->first) && - (it.index() == 0 || typename map::comp{}((it - 1)->first, key))) { - return std::forward<S>(storage).insert( - it.index(), std::make_pair(std::forward<K>(key), std::forward<V>(value))); - } - - return insert<map>(std::forward<S>(storage), std::forward<K>(key), std::forward<V>(value)); -} - -template <typename map, typename S, typename K, typename U> -[[nodiscard]] typename map::storage_type update(S&& storage, K&& key, U&& valueUpdate) { - auto it = lower_bound<map>(storage, key); - if (it == storage.end()) { - return std::forward<S>(storage).push_back( - std::make_pair(std::forward<K>(key), valueUpdate(typename map::default_value{}()))); - } - - if (equal<map>(it->first, key)) { - return std::forward<S>(storage).set( - it.index(), std::make_pair(std::forward<K>(key), valueUpdate(it->second))); - } - - return insert<map>(std::forward<S>(storage), - std::forward<K>(key), - valueUpdate(typename map::default_value{}())); -} - -template <typename map, typename S, typename K, typename U> -[[nodiscard]] typename map::storage_type update(S&& storage, - typename map::iterator it, - K&& key, - U&& valueUpdate) { - if (it == storage.end()) { - if (typename map::comp{}(storage[storage.size() - 1].first, key)) { - return std::forward<S>(storage).push_back( - std::make_pair(std::forward<K>(key), valueUpdate(typename map::default_value{}()))); - } - return update<map>( - std::forward<S>(storage), std::forward<K>(key), std::forward<U>(valueUpdate)); - } - - if (equal<map>(it->first, key)) { - return std::forward<S>(storage).set( - it.index(), std::make_pair(std::forward<K>(key), valueUpdate(it->second))); - } - - if (typename map::comp{}(key, it->first) && - (it.index() == 0 || typename map::comp{}((it - 1)->first, key))) { - return std::forward<S>(storage).insert( - it.index(), - std::make_pair(std::forward<K>(key), valueUpdate(typename map::default_value{}()))); - } - - return insert<map>(std::forward<S>(storage), - std::forward<K>(key), - valueUpdate(typename map::default_value{}())); -} - -template <typename map, typename S, typename K, typename U> -[[nodiscard]] typename map::storage_type update_if_exists(S&& storage, K&& key, U&& valueUpdate) { - auto it = find<map>(storage, key); - if (it == storage.end()) { - return std::forward<S>(storage); - } - return std::forward<S>(storage).set( - it.index(), std::make_pair(std::forward<K>(key), valueUpdate(it->second))); -} - -template <typename map, typename S, typename K, typename U> -[[nodiscard]] typename map::storage_type update_if_exists(S&& storage, - typename map::iterator it, - K&& key, - U&& valueUpdate) { - if (it == storage.end() || !equal<map>(it->first, key)) { - return update_if_exists<map>( - std::forward<S>(storage), std::forward<K>(key), std::forward<U>(valueUpdate)); - } - return std::forward<S>(storage).set( - it.index(), std::make_pair(std::forward<K>(key), valueUpdate(it->second))); -} - -template <typename map, typename S, typename K, typename V> -[[nodiscard]] typename map::storage_type set(S&& storage, K&& key, V&& value) { - auto it = lower_bound<map>(storage, key); - if (it == storage.end()) { - return std::forward<S>(storage).push_back( - std::make_pair(std::forward<K>(key), std::forward<V>(value))); - } else if (!equal<map>(it->first, key)) { - return std::forward<S>(storage).insert( - it.index(), std::make_pair(std::forward<K>(key), std::forward<V>(value))); - } - return std::forward<S>(storage).set( - it.index(), std::make_pair(std::forward<K>(key), std::forward<V>(value))); -} - -template <typename map, typename S, typename K, typename V> -[[nodiscard]] typename map::storage_type set(S&& storage, - typename map::iterator it, - K&& key, - V&& value) { - if (it == storage.end()) { - if (typename map::comp{}(storage[storage.size() - 1].first, key)) { - return std::forward<S>(storage).push_back( - std::make_pair(std::forward<K>(key), std::forward<V>(value))); - } - return set<map>(std::forward<S>(storage), std::forward<K>(key), std::forward<V>(value)); - } - - if (equal<map>(it->first, key)) { - return std::forward<S>(storage).set( - it.index(), std::make_pair(std::forward<K>(key), std::forward<V>(value))); - } - - if (typename map::comp{}(key, it->first) && - (it.index() == 0 || typename map::comp{}((it - 1)->first, key))) { - return std::forward<S>(storage).insert( - it.index(), std::make_pair(std::forward<K>(key), std::forward<V>(value))); - } - - return set<map>(std::forward<S>(storage), std::forward<K>(key), std::forward<V>(value)); -} - -template <typename map, typename S, typename K> -[[nodiscard]] typename map::storage_type erase(S&& storage, K&& key) { - auto it = find<map>(storage, key); - if (it == storage.end()) { - return std::forward<S>(storage); - } - return std::forward<S>(storage).erase(it.index()); -} - -template <typename map, typename S, typename K> -[[nodiscard]] typename map::storage_type erase(S&& storage, typename map::iterator it, K&& key) { - if (it == storage.end() || !equal<map>(it->first, key)) { - return erase<map>(std::forward<S>(storage), std::forward<K>(key)); - } - return std::forward<S>(storage).erase(it.index()); -} - -} // namespace mongo::immutable::details::map diff --git a/src/mongo/util/immutable/details/memory_policy.h b/src/mongo/util/immutable/details/memory_policy.h deleted file mode 100644 index 01923f032b7..00000000000 --- a/src/mongo/util/immutable/details/memory_policy.h +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright (C) 2023-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#pragma once - -#include <immer/memory_policy.hpp> - -namespace mongo::immutable::detail { - -// Memory allocations using regular new/delete operators -using HeapPolicy = immer::heap_policy<immer::cpp_heap>; - -// Refcounting using atomics for thread safety -using RefcountPolicy = immer::refcount_policy; - -// We are not using any features from immer that requires locking. Use 'void' as the lock type which -// would fail compilation if locking was needed anywhere. -using LockPolicy = void; - -// No transience policy (this is just used for garbage collection) -using TransiencePolicy = immer::no_transience_policy; - -using MemoryPolicy = immer::memory_policy<HeapPolicy, - RefcountPolicy, - LockPolicy, - TransiencePolicy, - /*PreferFewerBiggerObjects*/ true, - /*UseTransientRValues*/ true>; - -// Verify that the recommended settings for our memory policy is as expected. We need to investigate -// if any of these fire during a library upgrade. -static_assert( - std::is_same<immer::get_transience_policy_t<RefcountPolicy>, TransiencePolicy>::value); -static_assert(immer::get_prefer_fewer_bigger_objects_v<HeapPolicy> == true); -static_assert(immer::get_use_transient_rvalues_v<RefcountPolicy> == true); - -} // namespace mongo::immutable::detail diff --git a/src/mongo/util/immutable/details/set.h b/src/mongo/util/immutable/details/set.h deleted file mode 100644 index 491412e696f..00000000000 --- a/src/mongo/util/immutable/details/set.h +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright (C) 2023-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#pragma once - -#include <algorithm> - -namespace mongo::immutable::details::set { - -template <typename set, typename SearchKey> -bool equal(const typename set::key_type& a, const SearchKey& b) { - return !typename set::comp{}(a, b) && !typename set::comp{}(b, a); -} - -template <typename set, class SearchKey> -[[nodiscard]] typename set::iterator lower_bound(const typename set::storage_type& storage, - const SearchKey& key) { - return std::lower_bound(storage.begin(), storage.end(), key, typename set::comp{}); -} - -template <typename set, typename SearchKey> -[[nodiscard]] typename set::iterator find(const typename set::storage_type& storage, - const SearchKey& key) { - auto it = lower_bound<set>(storage, key); - if (it != storage.end() && equal<set>(*it, key)) { - return it; - } - - return storage.end(); -} - -template <typename set, typename S, typename K> -[[nodiscard]] typename set::storage_type insert(S&& storage, K&& key) { - auto it = lower_bound<set>(storage, key); - if (it == storage.end()) { - return std::forward<S>(storage).push_back(std::forward<K>(key)); - } - - if (equal<set>(*it, key)) { - return std::forward<S>(storage); - } - - return std::forward<S>(storage).insert(it.index(), std::forward<K>(key)); -} - -template <typename set, typename S, typename K> -[[nodiscard]] typename set::storage_type insert(S&& storage, typename set::iterator it, K&& key) { - if (it != storage.end() && equal<set>(*it, key)) { - return std::forward<S>(storage); - } - - if (it == storage.end()) { - if (typename set::comp{}(storage[storage.size() - 1], key)) { - return std::forward<S>(storage).push_back(std::forward<K>(key)); - } - return insert<set>(std::forward<S>(storage), std::forward<K>(key)); - } - - if (typename set::comp{}(key, *it) && - (it.index() == 0 || typename set::comp{}(*(it - 1), key))) { - return std::forward<S>(storage).insert(it.index(), std::forward<K>(key)); - } - - return insert<set>(std::forward<S>(storage), std::forward<K>(key)); -} - -template <typename set, typename S, typename K> -[[nodiscard]] typename set::storage_type erase(S&& storage, K&& key) { - auto it = find<set>(storage, key); - if (it == storage.end()) { - return std::forward<S>(storage); - } - return std::forward<S>(storage).erase(it.index()); -} - -template <typename set, typename S, typename K> -[[nodiscard]] typename set::storage_type erase(S&& storage, typename set::iterator it, K&& key) { - if (it == storage.end() || !equal<set>(*it, key)) { - return erase<set>(std::forward<S>(storage), std::forward<K>(key)); - } - return std::forward<S>(storage).erase(it.index()); -} - -} // namespace mongo::immutable::details::set diff --git a/src/mongo/util/immutable/immutable_absl_comparison_bm.cpp b/src/mongo/util/immutable/immutable_absl_comparison_bm.cpp deleted file mode 100644 index 78358aae0ff..00000000000 --- a/src/mongo/util/immutable/immutable_absl_comparison_bm.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/** - * Copyright (C) 2023-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#include <benchmark/benchmark.h> - -#include "mongo/stdx/unordered_map.h" -#include "mongo/util/immutable/unordered_map.h" - -namespace mongo { - -static void BM_absl_insert_op(benchmark::State& state) { - stdx::unordered_map<int, int> map; - - int64_t i = 0; - for (; i < state.range(0); i++) { - map[i] = i; - } - - for (auto _ : state) { - map[i] = i; - i++; - benchmark::ClobberMemory(); - } -} - -static void BM_absl_copy_op(benchmark::State& state) { - stdx::unordered_map<int, int> map; - - int64_t i = 0; - for (; i < state.range(0); i++) { - map[i] = i; - } - - stdx::unordered_map<int, int> mapCopy; - for (auto _ : state) { - mapCopy = map; - benchmark::ClobberMemory(); - } -} - -static void BM_absl_find_op(benchmark::State& state) { - stdx::unordered_map<int, int> map; - - int64_t i = 0; - for (; i < state.range(0); i++) { - map[i] = i; - } - - for (auto _ : state) { - benchmark::DoNotOptimize(map.find(i - 1)); - benchmark::ClobberMemory(); - } -} - -static void BM_absl_copy_and_insert_op(benchmark::State& state) { - stdx::unordered_map<int, int> map; - - int64_t i = 0; - for (; i < state.range(0); i++) { - map[i] = i; - } - - stdx::unordered_map<int, int> mapCopy; - for (auto _ : state) { - mapCopy = map; - mapCopy[i] = i; - benchmark::ClobberMemory(); - } -} - -static void BM_immutable_insert_op(benchmark::State& state) { - immutable::unordered_map<int, int> map; - - int64_t i = 0; - for (; i < state.range(0); i++) { - map = std::move(map).set(i, i); - } - - for (auto _ : state) { - map = std::move(map).set(i, i); - i++; - benchmark::ClobberMemory(); - } -} - -static void BM_immutable_copy_op(benchmark::State& state) { - immutable::unordered_map<int, int> map; - - int64_t i = 0; - for (; i < state.range(0); i++) { - map = std::move(map).set(i, i); - } - - immutable::unordered_map<int, int> mapCopy; - for (auto _ : state) { - mapCopy = map; - benchmark::ClobberMemory(); - } -} - -static void BM_immutable_find_op(benchmark::State& state) { - immutable::unordered_map<int, int> map; - - int64_t i = 0; - for (; i < state.range(0); i++) { - map = std::move(map).set(i, i); - } - - for (auto _ : state) { - benchmark::DoNotOptimize(map.find(i - 1)); - benchmark::ClobberMemory(); - } -} - -static void BM_immutable_copy_and_insert_op(benchmark::State& state) { - immutable::unordered_map<int, int> map; - - int64_t i = 0; - for (; i < state.range(0); i++) { - map = std::move(map).set(i, i); - } - - immutable::unordered_map<int, int> mapCopy; - for (auto _ : state) { - mapCopy = map.set(i, i); - benchmark::ClobberMemory(); - } -} - -// Run with varying container sizes: [ 8, 16, 32, 64, 128, 256, 512, 1024, 2k, 4k, 8k ]. -BENCHMARK(BM_absl_insert_op)->RangeMultiplier(2)->Range(8, 8 << 10); -BENCHMARK(BM_immutable_insert_op)->RangeMultiplier(2)->Range(8, 8 << 10); - -BENCHMARK(BM_absl_copy_op)->RangeMultiplier(2)->Range(8, 8 << 10); -BENCHMARK(BM_immutable_copy_op)->RangeMultiplier(2)->Range(8, 8 << 10); - -BENCHMARK(BM_absl_find_op)->RangeMultiplier(2)->Range(8, 8 << 10); -BENCHMARK(BM_immutable_find_op)->RangeMultiplier(2)->Range(8, 8 << 10); - -BENCHMARK(BM_absl_copy_and_insert_op)->RangeMultiplier(2)->Range(8, 8 << 10); -BENCHMARK(BM_immutable_copy_and_insert_op)->RangeMultiplier(2)->Range(8, 8 << 10); -} // namespace mongo diff --git a/src/mongo/util/immutable/immutable_ordered_test.cpp b/src/mongo/util/immutable/immutable_ordered_test.cpp deleted file mode 100644 index 2b9308d5caf..00000000000 --- a/src/mongo/util/immutable/immutable_ordered_test.cpp +++ /dev/null @@ -1,1336 +0,0 @@ -/** - * Copyright (C) 2023-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - - -#include "mongo/unittest/unittest.h" - -#include "mongo/util/immutable/map.h" -#include "mongo/util/immutable/set.h" -#include "mongo/util/string_map.h" -#include <stdexcept> - -#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kTest - -namespace mongo { -namespace { - -class UserDefinedKey { -public: - UserDefinedKey() = default; - explicit UserDefinedKey(int val) : a(val) {} - - bool operator==(const UserDefinedKey& rhs) const { - return a == rhs.a; - } - - bool operator<(const UserDefinedKey& rhs) const { - return a < rhs.a; - } - - std::string toString() const { - return std::to_string(a); - } - -private: - int a = 0; -}; - - -class Incomparable { - friend struct CompareIncomparable; - -public: - Incomparable() = default; - explicit Incomparable(int val) : a(val) {} - - bool operator==(const Incomparable&) = delete; - bool operator<(const Incomparable&) = delete; - - std::string toString() const { - return std::to_string(a); - } - -private: - int a = 0; -}; - -struct CompareIncomparable { - bool operator()(const Incomparable& a, const Incomparable& b) const { - return a.a < b.a; - } - - // Pair comparator needed for some testing macros to function properly for both maps and sets. - bool operator()(const std::pair<Incomparable, int>& a, - const std::pair<Incomparable, int>& b) const { - return a.first.a < b.first.a; - } -}; - -struct StringCompare { - bool operator()(const std::string& a, const std::string& b) const { - return a < b; - } - bool operator()(const std::string& a, const StringData& b) const { - return a < b; - } - bool operator()(const StringData& a, const std::string& b) const { - return a < b; - } - bool operator()(const std::string& a, const char* b) const { - return a < b; - } - bool operator()(const char* a, const std::string& b) const { - return a < b; - } -}; - -template <typename C, typename L> -void ensureContainerInvariants(const C& container, L&& less) { - size_t visited = 0; - for (auto it = container.begin(); it != container.end(); ++it) { - if (++visited > 1) { - ASSERT(less(*(it - 1), *it)); - } - } - ASSERT_EQ(visited, container.size()); -} - - -template <typename C> -void ensureContainerInvariants(const C& container) { - size_t visited = 0; - for (auto it = container.begin(); it != container.end(); ++it) { - if (++visited > 1) { - ASSERT(*(it - 1) < *it); - } - } - ASSERT_EQ(visited, container.size()); -} - -template <typename C> -void ensureContainerInvariants(const std::tuple<C, C, C, C>& containers) { - ensureContainerInvariants(std::get<0>(containers)); - ensureContainerInvariants(std::get<1>(containers)); - ensureContainerInvariants(std::get<2>(containers)); - ensureContainerInvariants(std::get<3>(containers)); -} - -template <typename C> -void ensureContainerInvariants(std::initializer_list<const C> containers) { - for (auto& c : containers) { - ensureContainerInvariants(c); - } -} - -template <typename K, typename V, typename M = immutable::map<K, V>> -std::tuple<M, M, M, M> init_maps() { - return std::make_tuple(M{}, M{}, M{}, M{}); -} - -template <typename K, typename S = immutable::set<K>> -std::tuple<S, S, S, S> init_sets() { - return std::make_tuple(S{}, S{}, S{}, S{}); -} - -std::ostream& operator<<(std::ostream& s, const UserDefinedKey& k) { - return s << k.toString(); -} - -std::ostream& operator<<(std::ostream& s, const Incomparable& k) { - return s << k.toString(); -} - -template <typename T, typename U> -std::ostream& operator<<(std::ostream& s, const std::pair<T, U> pair) { - return s << "(" << pair.first << "," << pair.second << ")"; -} - -template <typename C> -struct ContainerWrapper { - ContainerWrapper(const C& c) : container{c} {} - const C& container; -}; -template <typename T> -std::ostream& operator<<(std::ostream& str, const ContainerWrapper<T>& wrapper) { - str << "{"; - bool first = true; - for (auto& el : wrapper.container) { - if (first) { - first = false; - } else { - str << ", "; - } - str << el; - } - str << "}"; - return str; -} - -#define ASSERT_CONTAINS(containers, k) \ - { \ - ASSERT_TRUE(std::get<0>(containers).contains(k)) \ - << ContainerWrapper{std::get<0>(containers)}; \ - ASSERT_TRUE(std::get<1>(containers).contains(k)) \ - << ContainerWrapper{std::get<0>(containers)}; \ - ASSERT_TRUE(std::get<2>(containers).contains(k)) \ - << ContainerWrapper{std::get<0>(containers)}; \ - ASSERT_TRUE(std::get<3>(containers).contains(k)) \ - << ContainerWrapper{std::get<0>(containers)}; \ - } - -#define ASSERT_NOT_CONTAINS(containers, k) \ - { \ - ASSERT_FALSE(std::get<0>(containers).contains(k)) \ - << ContainerWrapper{std::get<0>(containers)}; \ - ASSERT_FALSE(std::get<1>(containers).contains(k)) \ - << ContainerWrapper{std::get<0>(containers)}; \ - ASSERT_FALSE(std::get<2>(containers).contains(k)) \ - << ContainerWrapper{std::get<0>(containers)}; \ - ASSERT_FALSE(std::get<3>(containers).contains(k)) \ - << ContainerWrapper{std::get<0>(containers)}; \ - } - -#define ASSERT_VALUE_EQ(its, v) \ - { \ - ASSERT_EQ(std::get<0>(its)->second, v); \ - ASSERT_EQ(std::get<1>(its)->second, v); \ - ASSERT_EQ(std::get<2>(its)->second, v); \ - ASSERT_EQ(std::get<3>(its)->second, v); \ - } - -#define MUTATE_KV(containers, fn, k, v) \ - [&]() { \ - auto _k1 = k; \ - auto _v1 = v; \ - auto _m1 = std::get<0>(containers).fn(_k1, _v1); \ - ensureContainerInvariants(_m1); \ - \ - auto _k2 = k; \ - auto _v2 = v; \ - auto _m2 = std::move(std::get<1>(containers)).fn(_k2, _v2); \ - ensureContainerInvariants(_m2); \ - \ - auto _k3 = k; \ - auto _v3 = v; \ - auto _m3 = std::get<2>(containers).fn(std::move(_k3), std::move(_v3)); \ - ensureContainerInvariants(_m3); \ - \ - auto _k4 = k; \ - auto _v4 = v; \ - auto _m4 = std::move(std::get<3>(containers)).fn(std::move(_k4), std::move(_v4)); \ - ensureContainerInvariants(_m4); \ - \ - return std::make_tuple(_m1, _m2, _m3, _m4); \ - }(); - -#define MUTATE_K(containers, fn, k) \ - ([&]() { \ - auto _k1 = k; \ - auto _m1 = std::get<0>(containers).fn(_k1); \ - ensureContainerInvariants(_m1); \ - \ - auto _k2 = k; \ - auto _m2 = std::move(std::get<1>(containers)).fn(_k2); \ - ensureContainerInvariants(_m2); \ - \ - auto _k3 = k; \ - auto _m3 = std::get<2>(containers).fn(std::move(_k3)); \ - ensureContainerInvariants(_m3); \ - \ - auto _k4 = k; \ - auto _m4 = std::move(std::get<3>(containers)).fn(std::move(_k4)); \ - ensureContainerInvariants(_m4); \ - \ - return std::make_tuple(_m1, _m2, _m3, _m4); \ - }()); - -#define MUTATE_IT_KV(containers, fn, its, k, v) \ - [&]() { \ - auto _k1 = k; \ - auto _v1 = v; \ - auto _m1 = std::get<0>(containers).fn(std::get<0>(its), _k1, _v1); \ - ensureContainerInvariants(_m1); \ - \ - auto _k2 = k; \ - auto _v2 = v; \ - auto _m2 = std::move(std::get<1>(containers)).fn(std::get<1>(its), _k2, _v2); \ - ensureContainerInvariants(_m2); \ - \ - auto _k3 = k; \ - auto _v3 = v; \ - auto _m3 = std::get<2>(containers).fn(std::get<2>(its), std::move(_k3), std::move(_v3)); \ - ensureContainerInvariants(_m3); \ - \ - auto _k4 = k; \ - auto _v4 = v; \ - auto _m4 = std::move(std::get<3>(containers)) \ - .fn(std::get<3>(its), std::move(_k4), std::move(_v4)); \ - ensureContainerInvariants(_m4); \ - \ - return std::make_tuple(_m1, _m2, _m3, _m4); \ - }(); - -#define MUTATE_IT_K(containers, fn, its, k) \ - [&]() { \ - auto _k1 = k; \ - auto _m1 = std::get<0>(containers).fn(std::get<0>(its), _k1); \ - ensureContainerInvariants(_m1); \ - \ - auto _k2 = k; \ - auto _m2 = std::move(std::get<1>(containers)).fn(std::get<1>(its), _k2); \ - ensureContainerInvariants(_m2); \ - \ - auto _k3 = k; \ - auto _m3 = std::get<2>(containers).fn(std::get<2>(its), std::move(_k3)); \ - ensureContainerInvariants(_m3); \ - \ - auto _k4 = k; \ - auto _m4 = std::move(std::get<3>(containers)).fn(std::get<3>(its), std::move(_k4)); \ - ensureContainerInvariants(_m4); \ - \ - return std::make_tuple(_m1, _m2, _m3, _m4); \ - }(); - -#define SEARCH(containers, fn, k) \ - [&]() { \ - auto _i1 = std::get<0>(containers).fn(k); \ - auto _i2 = std::get<1>(containers).fn(k); \ - auto _i3 = std::get<2>(containers).fn(k); \ - auto _i4 = std::get<3>(containers).fn(k); \ - return std::make_tuple(_i1, _i2, _i3, _i4); \ - }(); - -#define END(containers) \ - [&]() { \ - auto _i1 = std::get<0>(containers).end(); \ - auto _i2 = std::get<1>(containers).end(); \ - auto _i3 = std::get<2>(containers).end(); \ - auto _i4 = std::get<3>(containers).end(); \ - return std::make_tuple(_i1, _i2, _i3, _i4); \ - }(); - -TEST(ImmutableMap, Basic) { - // Insert some values and verify that the data structure is behaving as expected - immutable::map<int, int> v0; - auto v1 = v0.set(1, 2); - // Record the pointer to the value '1', verify that this doesn't change after performing more - // inserts - auto v1Val = v1.find(1); - - // Create distinct branches of the history from v1. v0 and v1 should be unaffected - auto v2 = v1.update_if_exists(1, [](int v) { return v += 1; }); - auto v3 = v1.set(2, 3); - - // Verify that values are as expected - ASSERT_EQ(v0.size(), 0); - - ASSERT_EQ(v1.size(), 1); - ASSERT_TRUE(v1.contains(1)); - ASSERT_EQ(v1.find(1)->second, 2); - - ASSERT_EQ(v2.size(), 1); - ASSERT_TRUE(v2.contains(1)); - ASSERT_EQ(v2.find(1)->second, 3); - ASSERT_FALSE(v2.contains(2)); - - ASSERT_EQ(v3.size(), 2); - ASSERT_TRUE(v3.contains(1)); - ASSERT_EQ(v3.find(1)->second, 2); - ASSERT_TRUE(v3.contains(2)); - ASSERT_EQ(v3.find(2)->second, 3); - - // Verify that v1's value did not change - ASSERT(v1.find(1) == v1Val); - - // Verify that erase works as expected, and preserves history. - auto v4 = v3.erase(1).erase(2); - ASSERT_FALSE(v4.contains(1)); - ASSERT_FALSE(v4.contains(2)); - ASSERT_TRUE(v3.contains(1)); - ASSERT_EQ(v3.find(1)->second, 2); - ASSERT_TRUE(v3.contains(2)); - ASSERT_EQ(v3.find(2)->second, 3); - ASSERT_TRUE(v2.contains(1)); - ASSERT_EQ(v2.find(1)->second, 3); - ASSERT_FALSE(v2.contains(2)); - ASSERT_TRUE(v1.contains(1)); - ASSERT_EQ(v1.find(1)->second, 2); - ASSERT_FALSE(v1.contains(2)); - - ensureContainerInvariants({v0, v1, v2, v3, v4}); -} - -TEST(ImmutableMap, UserDefinedType) { - immutable::map<UserDefinedKey, int> v0; - auto v1 = v0.set(UserDefinedKey(1), 2); - ASSERT(v1.find(UserDefinedKey(1)) != v1.end()); - ASSERT_EQ(v1.find(UserDefinedKey(1))->second, 2); - - ensureContainerInvariants({v0, v1}); -} - -TEST(ImmutableMap, IncomparableType) { - immutable::map<Incomparable, int, CompareIncomparable> v0; - auto v1 = v0.set(Incomparable(1), 2); - ASSERT(v1.find(Incomparable(1)) != v1.end()); - ASSERT_EQ(v1.find(Incomparable(1))->second, 2); - - ensureContainerInvariants(v0, CompareIncomparable{}); - ensureContainerInvariants(v1, CompareIncomparable{}); -} - -TEST(ImmutableMap, HeterogeneousLookup) { - immutable::map<std::string, int, StringCompare> v0; - auto v1 = v0.set("str", 1); - - // Lookup using StringData without the need to convert to string. - ASSERT(v1.find("str"_sd) != v1.end()); - - ensureContainerInvariants({v0, v1}); -} - -TEST(ImmutableMap, Accessors) { - immutable::map<int, int> v0; - auto v1 = v0.insert(1, 1).insert(2, 2).insert(3, 3); - - ASSERT_EQ(v1[1], 1); - ASSERT_EQ(v1[2], 2); - ASSERT_EQ(v1[3], 3); - ASSERT_EQ(v1.at(1), 1); - ASSERT_EQ(v1.at(2), 2); - ASSERT_EQ(v1.at(3), 3); - - // Handling of missing elements - ASSERT_EQ(v1[4], 0); - ASSERT_THROWS(v1.at(4), std::out_of_range); - - ensureContainerInvariants({v0, v1}); -} - -TEST(ImmutableMap, Bounds) { - immutable::map<int, int> map; - constexpr int numKeys = 100; - for (int i = 0; i < numKeys; ++i) { - map = map.set(2 * i, 2 * i); - ensureContainerInvariants(map); - } - - for (int i = 0; i < numKeys - 1; ++i) { - auto lowerExact = map.lower_bound(2 * i); - ASSERT(lowerExact != map.end() && lowerExact->first == 2 * i); - auto upperExact = map.upper_bound(2 * i); - ASSERT(upperExact != map.end() && upperExact->first == 2 * (i + 1)); - - auto lowerNear = map.lower_bound(2 * i + 1); - ASSERT(lowerNear != map.end() && lowerNear->first == 2 * (i + 1)); - auto upperNear = map.upper_bound(2 * i + 1); - ASSERT(upperNear != map.end() && upperNear->first == 2 * (i + 1)); - } -} - -TEST(ImmutableMap, Iteration) { - immutable::map<int, int> map; - constexpr int numKeys = 100; - for (int i = 0; i < numKeys; ++i) { - map = map.set(2 * i, 2 * i); - ensureContainerInvariants(map); - } - - auto map0 = map; - auto it0 = map0.begin(); - - for (int i = 0; i < numKeys; ++i) { - map = map.set(2 * i + 1, 2 * i + 1); - ensureContainerInvariants(map); - } - - auto map1 = map; - auto it1 = map1.begin(); - - for (int i = 0; i < numKeys; ++i) { - ASSERT(it0 != map0.end()); - ASSERT_EQ(it0->first, 2 * i); - ++it0; - - ASSERT(it1 != map1.end()); - ASSERT_EQ(it1->first, 2 * i); - ++it1; - - ASSERT(it1 != map1.end()); - ASSERT_EQ(it1->first, 2 * i + 1); - ++it1; - } - ASSERT(it0 == map0.end()); - ASSERT(it1 == map1.end()); -} - -TEST(ImmutableMap, Insert) { - auto v0 = init_maps<int, int>(); - - // Populate an initial set of values. - auto v1 = MUTATE_KV(v0, insert, 1, 1); - auto v2 = MUTATE_KV(v1, insert, 2, 2); - auto v3 = MUTATE_KV(v2, insert, 3, 3); - auto v4 = MUTATE_KV(v3, insert, 5, 5); - ASSERT_CONTAINS(v4, 1); - ASSERT_CONTAINS(v4, 2); - ASSERT_CONTAINS(v4, 3); - ASSERT_CONTAINS(v4, 5); - - // Insert of existing key is a noop. - auto v5 = MUTATE_KV(v4, insert, 1, 2); - ASSERT_CONTAINS(v5, 1); - auto i5 = SEARCH(v5, find, 1); - ASSERT_VALUE_EQ(i5, 1); - - // Insert at beginning works. - auto v6 = MUTATE_KV(v4, insert, 0, 0); - ASSERT_CONTAINS(v6, 0); - auto i6 = SEARCH(v6, find, 0); - ASSERT_VALUE_EQ(i6, 0); - - // Insert at end works. - auto v7 = MUTATE_KV(v4, insert, 6, 6); - ASSERT_CONTAINS(v7, 6); - auto i7 = SEARCH(v7, find, 6); - ASSERT_VALUE_EQ(i7, 6); - - // Insert in middle works. - auto v8 = MUTATE_KV(v4, insert, 4, 4); - ASSERT_CONTAINS(v8, 4); - auto i8 = SEARCH(v8, find, 4); - ASSERT_VALUE_EQ(i8, 4); -} - -TEST(ImmutableMap, InsertViaIterator) { - auto v0 = init_maps<int, int>(); - - // Populate an initial set of values. - auto v1 = MUTATE_KV(v0, insert, 1, 1); - auto v2 = MUTATE_KV(v1, insert, 2, 2); - auto v3 = MUTATE_KV(v2, insert, 3, 3); - auto v4 = MUTATE_KV(v3, insert, 6, 6); - ASSERT_CONTAINS(v4, 1); - ASSERT_CONTAINS(v4, 2); - ASSERT_CONTAINS(v4, 3); - ASSERT_CONTAINS(v4, 6); - - // Giving the iterator for an existing element does a noop. - - auto it4_2 = SEARCH(v4, find, 2); - auto v5 = MUTATE_IT_KV(v4, insert, it4_2, 2, 5); - auto it5_2 = SEARCH(v5, find, 2); - ASSERT_VALUE_EQ(it5_2, 2); - - // Giving end() as hint works appropriately whether hint is accurate or not. - - auto end5 = END(v5); - auto v6 = MUTATE_IT_KV(v5, insert, end5, 2, 5); - auto it6_2 = SEARCH(v6, find, 2); - ASSERT_VALUE_EQ(it6_2, 2); - - auto end6 = END(v6); - auto v7 = MUTATE_IT_KV(v6, insert, end6, 4, 4); - ASSERT_NOT_CONTAINS(v6, 4); - ASSERT_CONTAINS(v7, 4); - auto it7_4 = SEARCH(v7, find, 4); - ASSERT_VALUE_EQ(it7_4, 4); - - auto end7 = END(v7); - auto v8 = MUTATE_IT_KV(v7, insert, end7, 7, 7); - ASSERT_NOT_CONTAINS(v7, 7); - ASSERT_CONTAINS(v8, 7); - auto it8_7 = SEARCH(v8, find, 7); - ASSERT_VALUE_EQ(it8_7, 7); - - // Giving hint from lower_bound works for both existing and new entries. - - auto lb8_2 = SEARCH(v8, lower_bound, 2); - auto v9 = MUTATE_IT_KV(v8, insert, lb8_2, 2, 5); - auto it9_2 = SEARCH(v9, find, 2); - ASSERT_VALUE_EQ(it9_2, 2); - - auto lb9_0 = SEARCH(v9, lower_bound, 0); - auto v10 = MUTATE_IT_KV(v9, insert, lb9_0, 0, 0); - ASSERT_NOT_CONTAINS(v9, 0); - ASSERT_CONTAINS(v10, 0); - auto it10_0 = SEARCH(v10, find, 0); - ASSERT_VALUE_EQ(it10_0, 0); - - auto lb10_5 = SEARCH(v10, lower_bound, 5); - auto v11 = MUTATE_IT_KV(v10, insert, lb10_5, 5, 5); - ASSERT_NOT_CONTAINS(v10, 5); - ASSERT_CONTAINS(v11, 5); - auto it11_5 = SEARCH(v11, find, 5); - ASSERT_VALUE_EQ(it11_5, 5); - - auto lb11_8 = SEARCH(v11, lower_bound, 8); - auto v12 = MUTATE_IT_KV(v11, insert, lb11_8, 8, 8); - ASSERT_NOT_CONTAINS(v11, 8); - ASSERT_CONTAINS(v12, 8); - auto it12_8 = SEARCH(v12, find, 8); - ASSERT_VALUE_EQ(it12_8, 8); -} - -TEST(ImmutableMap, Set) { - auto v0 = init_maps<int, int>(); - - // Populate an initial set of values. - auto v1 = MUTATE_KV(v0, insert, 1, 1); - auto v2 = MUTATE_KV(v1, insert, 2, 2); - auto v3 = MUTATE_KV(v2, insert, 3, 3); - auto v4 = MUTATE_KV(v3, insert, 5, 5); - ASSERT_CONTAINS(v4, 1); - ASSERT_CONTAINS(v4, 2); - ASSERT_CONTAINS(v4, 3); - ASSERT_CONTAINS(v4, 5); - - // Set on existing key updates value. - auto v5 = MUTATE_KV(v4, set, 1, 2); - ASSERT_CONTAINS(v5, 1); - auto i5 = SEARCH(v5, find, 1); - ASSERT_VALUE_EQ(i5, 2); - - // Set to insert at beginning works. - auto v6 = MUTATE_KV(v4, set, 0, 0); - ASSERT_CONTAINS(v6, 0); - auto i6 = SEARCH(v6, find, 0); - ASSERT_VALUE_EQ(i6, 0); - - // Set to insert at end works. - auto v7 = MUTATE_KV(v4, set, 6, 6); - ASSERT_CONTAINS(v7, 6); - auto i7 = SEARCH(v7, find, 6); - ASSERT_VALUE_EQ(i7, 6); - - // Set to insert in middle works. - auto v8 = MUTATE_KV(v4, set, 4, 4); - ASSERT_CONTAINS(v8, 4); - auto i8 = SEARCH(v8, find, 4); - ASSERT_VALUE_EQ(i8, 4); -} - -TEST(ImmutableMap, SetViaIterator) { - auto v0 = init_maps<int, int>(); - - // Populate an initial set of values. - auto v1 = MUTATE_KV(v0, insert, 1, 1); - auto v2 = MUTATE_KV(v1, insert, 2, 2); - auto v3 = MUTATE_KV(v2, insert, 3, 3); - auto v4 = MUTATE_KV(v3, insert, 6, 6); - ASSERT_CONTAINS(v4, 1); - ASSERT_CONTAINS(v4, 2); - ASSERT_CONTAINS(v4, 3); - ASSERT_CONTAINS(v4, 6); - - // Giving the iterator for an existing element updates the value. - - auto it4_2 = SEARCH(v4, find, 2); - auto v5 = MUTATE_IT_KV(v4, set, it4_2, 2, 5); - auto it5_2 = SEARCH(v5, find, 2); - ASSERT_VALUE_EQ(it5_2, 5); - - // Giving end() as hint works appropriately whether hint is accurate or not. - - auto end5 = END(v5); - auto v6 = MUTATE_IT_KV(v5, set, end5, 2, 7); - auto it6_2 = SEARCH(v6, find, 2); - ASSERT_VALUE_EQ(it6_2, 7); - - auto end6 = END(v6); - auto v7 = MUTATE_IT_KV(v6, set, end6, 4, 4); - ASSERT_NOT_CONTAINS(v6, 4); - ASSERT_CONTAINS(v7, 4); - auto it7_4 = SEARCH(v7, find, 4); - ASSERT_VALUE_EQ(it7_4, 4); - - auto end7 = END(v7); - auto v8 = MUTATE_IT_KV(v7, set, end7, 7, 7); - ASSERT_NOT_CONTAINS(v7, 7); - ASSERT_CONTAINS(v8, 7); - auto it8_7 = SEARCH(v8, find, 7); - ASSERT_VALUE_EQ(it8_7, 7); - - // Giving hint from lower_bound works for both existing and new entries. - - auto lb8_2 = SEARCH(v8, lower_bound, 2); - auto v9 = MUTATE_IT_KV(v8, set, lb8_2, 2, 9); - auto it9_2 = SEARCH(v9, find, 2); - ASSERT_VALUE_EQ(it9_2, 9); - - auto lb9_0 = SEARCH(v9, lower_bound, 0); - auto v10 = MUTATE_IT_KV(v9, set, lb9_0, 0, 0); - ASSERT_NOT_CONTAINS(v9, 0); - ASSERT_CONTAINS(v10, 0); - auto it10_0 = SEARCH(v10, find, 0); - ASSERT_VALUE_EQ(it10_0, 0); - - auto lb10_5 = SEARCH(v10, lower_bound, 5); - auto v11 = MUTATE_IT_KV(v10, set, lb10_5, 5, 5); - ASSERT_NOT_CONTAINS(v10, 5); - ASSERT_CONTAINS(v11, 5); - auto it11_5 = SEARCH(v11, find, 5); - ASSERT_VALUE_EQ(it11_5, 5); - - auto lb11_8 = SEARCH(v11, lower_bound, 8); - auto v12 = MUTATE_IT_KV(v11, set, lb11_8, 8, 8); - ASSERT_NOT_CONTAINS(v11, 8); - ASSERT_CONTAINS(v12, 8); - auto it12_8 = SEARCH(v12, find, 8); - ASSERT_VALUE_EQ(it12_8, 8); -} - -TEST(ImmutableMap, Update) { - auto v0 = init_maps<int, int>(); - - // Populate an initial set of values. - auto v1 = MUTATE_KV(v0, insert, 1, 1); - auto v2 = MUTATE_KV(v1, insert, 2, 2); - auto v3 = MUTATE_KV(v2, insert, 3, 3); - auto v4 = MUTATE_KV(v3, insert, 5, 5); - ASSERT_CONTAINS(v4, 1); - ASSERT_CONTAINS(v4, 2); - ASSERT_CONTAINS(v4, 3); - ASSERT_CONTAINS(v4, 5); - - // Update on existing key updates value. - auto v5 = MUTATE_KV(v4, update, 1, [](int x) { return x + 1; }); - ASSERT_CONTAINS(v5, 1); - auto i5 = SEARCH(v5, find, 1); - ASSERT_VALUE_EQ(i5, 2); - - // Update to insert at beginning works. - auto v6 = MUTATE_KV(v4, update, 0, [](int x) { return x + 1; }); - ASSERT_CONTAINS(v6, 0); - auto i6 = SEARCH(v6, find, 0); - ASSERT_VALUE_EQ(i6, 1); - - // Update to insert at end works. - auto v7 = MUTATE_KV(v4, update, 6, [](int x) { return x + 1; }); - ASSERT_CONTAINS(v7, 6); - auto i7 = SEARCH(v7, find, 6); - ASSERT_VALUE_EQ(i7, 1); - - // Update to insert in middle works. - auto v8 = MUTATE_KV(v4, update, 4, [](int x) { return x + 1; }); - ASSERT_CONTAINS(v8, 4); - auto i8 = SEARCH(v8, find, 4); - ASSERT_VALUE_EQ(i8, 1); -} - -TEST(ImmutableMap, UpdateViaIterator) { - auto v0 = init_maps<int, int>(); - - // Populate an initial set of values. - auto v1 = MUTATE_KV(v0, insert, 1, 1); - auto v2 = MUTATE_KV(v1, insert, 2, 2); - auto v3 = MUTATE_KV(v2, insert, 3, 3); - auto v4 = MUTATE_KV(v3, insert, 6, 6); - ASSERT_CONTAINS(v4, 1); - ASSERT_CONTAINS(v4, 2); - ASSERT_CONTAINS(v4, 3); - ASSERT_CONTAINS(v4, 6); - - // Giving the iterator for an existing element updates the value. - - auto it4_2 = SEARCH(v4, find, 2); - auto v5 = MUTATE_IT_KV(v4, update, it4_2, 2, [](int x) { return x + 1; }); - auto it5_2 = SEARCH(v5, find, 2); - ASSERT_VALUE_EQ(it5_2, 3); - - // Giving end() as hint works appropriately whether hint is accurate or not. - - auto end5 = END(v5); - auto v6 = MUTATE_IT_KV(v5, update, end5, 2, [](int x) { return x + 1; }); - auto it6_2 = SEARCH(v6, find, 2); - ASSERT_VALUE_EQ(it6_2, 4); - - auto end6 = END(v6); - auto v7 = MUTATE_IT_KV(v6, update, end6, 4, [](int x) { return x + 1; }); - ASSERT_NOT_CONTAINS(v6, 4); - ASSERT_CONTAINS(v7, 4); - auto it7_4 = SEARCH(v7, find, 4); - ASSERT_VALUE_EQ(it7_4, 1); - - auto end7 = END(v7); - auto v8 = MUTATE_IT_KV(v7, update, end7, 7, [](int x) { return x + 1; }); - ASSERT_NOT_CONTAINS(v7, 7); - ASSERT_CONTAINS(v8, 7); - auto it8_7 = SEARCH(v8, find, 7); - ASSERT_VALUE_EQ(it8_7, 1); - - // Giving hint from lower_bound works for both existing and new entries. - - auto lb8_2 = SEARCH(v8, lower_bound, 2); - auto v9 = MUTATE_IT_KV(v8, update, lb8_2, 2, [](int x) { return x + 1; }); - auto it9_2 = SEARCH(v9, find, 2); - ASSERT_VALUE_EQ(it9_2, 5); - - auto lb9_0 = SEARCH(v9, lower_bound, 0); - auto v10 = MUTATE_IT_KV(v9, update, lb9_0, 0, [](int x) { return x + 1; }); - ASSERT_NOT_CONTAINS(v9, 0); - ASSERT_CONTAINS(v10, 0); - auto it10_0 = SEARCH(v10, find, 0); - ASSERT_VALUE_EQ(it10_0, 1); - - auto lb10_5 = SEARCH(v10, lower_bound, 5); - auto v11 = MUTATE_IT_KV(v10, update, lb10_5, 5, [](int x) { return x + 1; }); - ASSERT_NOT_CONTAINS(v10, 5); - ASSERT_CONTAINS(v11, 5); - auto it11_5 = SEARCH(v11, find, 5); - ASSERT_VALUE_EQ(it11_5, 1); - - auto lb11_8 = SEARCH(v11, lower_bound, 8); - auto v12 = MUTATE_IT_KV(v11, update, lb11_8, 8, [](int x) { return x + 1; }); - ASSERT_NOT_CONTAINS(v11, 8); - ASSERT_CONTAINS(v12, 8); - auto it12_8 = SEARCH(v12, find, 8); - ASSERT_VALUE_EQ(it12_8, 1); -} - -TEST(ImmutableMap, UpdateIfExists) { - auto v0 = init_maps<int, int>(); - - // Populate an initial set of values. - auto v1 = MUTATE_KV(v0, insert, 1, 1); - auto v2 = MUTATE_KV(v1, insert, 2, 2); - auto v3 = MUTATE_KV(v2, insert, 3, 3); - auto v4 = MUTATE_KV(v3, insert, 5, 5); - ASSERT_CONTAINS(v4, 1); - ASSERT_CONTAINS(v4, 2); - ASSERT_CONTAINS(v4, 3); - ASSERT_CONTAINS(v4, 5); - - // Update on existing key updates value. - auto v5 = MUTATE_KV(v4, update_if_exists, 1, [](int x) { return x + 1; }); - ASSERT_CONTAINS(v5, 1); - auto i5 = SEARCH(v5, find, 1); - ASSERT_VALUE_EQ(i5, 2); - - // Update to insert at beginning does nothing. - auto v6 = MUTATE_KV(v4, update_if_exists, 0, [](int x) { return x + 1; }); - ASSERT_NOT_CONTAINS(v6, 0); - - // Update to insert at end does nothing. - auto v7 = MUTATE_KV(v4, update_if_exists, 6, [](int x) { return x + 1; }); - ASSERT_NOT_CONTAINS(v7, 6); - - // Update to insert in middle does nothing. - auto v8 = MUTATE_KV(v4, update_if_exists, 4, [](int x) { return x + 1; }); - ASSERT_NOT_CONTAINS(v8, 4); -} - -TEST(ImmutableMap, UpdateIfExistsViaIterator) { - auto v0 = init_maps<int, int>(); - - // Populate an initial set of values. - auto v1 = MUTATE_KV(v0, insert, 1, 1); - auto v2 = MUTATE_KV(v1, insert, 2, 2); - auto v3 = MUTATE_KV(v2, insert, 3, 3); - auto v4 = MUTATE_KV(v3, insert, 6, 6); - ASSERT_CONTAINS(v4, 1); - ASSERT_CONTAINS(v4, 2); - ASSERT_CONTAINS(v4, 3); - ASSERT_CONTAINS(v4, 6); - - // Giving the iterator for an existing element updates the value. - - auto it4_2 = SEARCH(v4, find, 2); - auto v5 = MUTATE_IT_KV(v4, update_if_exists, it4_2, 2, [](int x) { return x + 1; }); - auto it5_2 = SEARCH(v5, find, 2); - ASSERT_VALUE_EQ(it5_2, 3); - - // Giving end() as hint works appropriately whether hint is accurate or not. - - auto end5 = END(v5); - auto v6 = MUTATE_IT_KV(v5, update_if_exists, end5, 2, [](int x) { return x + 1; }); - auto it6_2 = SEARCH(v6, find, 2); - ASSERT_VALUE_EQ(it6_2, 4); - - auto end6 = END(v6); - auto v7 = MUTATE_IT_KV(v6, update_if_exists, end6, 4, [](int x) { return x + 1; }); - ASSERT_NOT_CONTAINS(v7, 4); - - auto end7 = END(v7); - auto v8 = MUTATE_IT_KV(v7, update_if_exists, end7, 7, [](int x) { return x + 1; }); - ASSERT_NOT_CONTAINS(v8, 7); - - // Giving hint from lower_bound works for both existing and non-existing entries. - - auto lb8_2 = SEARCH(v8, lower_bound, 2); - auto v9 = MUTATE_IT_KV(v8, update_if_exists, lb8_2, 2, [](int x) { return x + 1; }); - auto it9_2 = SEARCH(v9, find, 2); - ASSERT_VALUE_EQ(it9_2, 5); - - auto lb9_0 = SEARCH(v9, lower_bound, 0); - auto v10 = MUTATE_IT_KV(v9, update_if_exists, lb9_0, 0, [](int x) { return x + 1; }); - ASSERT_NOT_CONTAINS(v10, 0); - - auto lb10_5 = SEARCH(v10, lower_bound, 5); - auto v11 = MUTATE_IT_KV(v10, update_if_exists, lb10_5, 5, [](int x) { return x + 1; }); - ASSERT_NOT_CONTAINS(v11, 5); - - auto lb11_8 = SEARCH(v11, lower_bound, 8); - auto v12 = MUTATE_IT_KV(v11, update_if_exists, lb11_8, 8, [](int x) { return x + 1; }); - ASSERT_NOT_CONTAINS(v12, 8); -} - -TEST(ImmutableMap, Erase) { - auto v0 = init_maps<int, int>(); - - // Populate an initial set of values. - auto v1 = MUTATE_KV(v0, insert, 1, 1); - auto v2 = MUTATE_KV(v1, insert, 2, 2); - auto v3 = MUTATE_KV(v2, insert, 3, 3); - auto v4 = MUTATE_KV(v3, insert, 5, 5); - ASSERT_CONTAINS(v4, 1); - ASSERT_CONTAINS(v4, 2); - ASSERT_CONTAINS(v4, 3); - ASSERT_CONTAINS(v4, 5); - - // Erase on existing key removes value. - auto v5 = MUTATE_K(v4, erase, 1); - ASSERT_NOT_CONTAINS(v5, 1); - - // Erase on non-existent key does nothing. - auto v6 = MUTATE_K(v5, erase, 0); - ASSERT_NOT_CONTAINS(v6, 0); - ASSERT_CONTAINS(v6, 2); - ASSERT_CONTAINS(v6, 3); - ASSERT_CONTAINS(v6, 5); -} - -TEST(ImmutableMap, EraseViaIterator) { - auto v0 = init_maps<int, int>(); - - // Populate an initial set of values. - auto v1 = MUTATE_KV(v0, insert, 1, 1); - auto v2 = MUTATE_KV(v1, insert, 2, 2); - auto v3 = MUTATE_KV(v2, insert, 3, 3); - auto v4 = MUTATE_KV(v3, insert, 5, 5); - auto v5 = MUTATE_KV(v4, insert, 6, 6); - ASSERT_CONTAINS(v5, 1); - ASSERT_CONTAINS(v5, 2); - ASSERT_CONTAINS(v5, 3); - ASSERT_CONTAINS(v5, 5); - ASSERT_CONTAINS(v5, 6); - - // Giving the iterator for an existing element erases the value. - - auto it5_2 = SEARCH(v5, find, 2); - auto v6 = MUTATE_IT_K(v5, erase, it5_2, 2); - ASSERT_NOT_CONTAINS(v6, 2); - - // Giving end() as hint works appropriately whether hint is accurate or not. - - auto end6 = END(v6); - auto v7 = MUTATE_IT_K(v6, erase, end6, 3); - ASSERT_NOT_CONTAINS(v7, 3); - - auto end7 = END(v7); - auto v8 = MUTATE_IT_K(v7, erase, end7, 4); - ASSERT_NOT_CONTAINS(v8, 4); - - auto end8 = END(v8); - auto v9 = MUTATE_IT_K(v8, erase, end8, 7); - ASSERT_NOT_CONTAINS(v9, 7); - - // Giving hint from lower_bound works for both existing and non-existing entries. - - auto lb9_5 = SEARCH(v9, lower_bound, 5); - auto v10 = MUTATE_IT_K(v9, erase, lb9_5, 5); - ASSERT_NOT_CONTAINS(v10, 5); - - auto lb10_0 = SEARCH(v10, lower_bound, 0); - auto v11 = MUTATE_IT_K(v10, erase, lb10_0, 0); - - auto lb11_4 = SEARCH(v10, lower_bound, 4); - auto v12 = MUTATE_IT_K(v11, erase, lb11_4, 4); - - auto lb12_7 = SEARCH(v12, lower_bound, 7); - auto v13 = MUTATE_IT_K(v12, erase, lb12_7, 7); - - ASSERT_CONTAINS(v13, 1); - ASSERT_CONTAINS(v13, 6); - ASSERT_NOT_CONTAINS(v13, 0); - ASSERT_NOT_CONTAINS(v13, 2); - ASSERT_NOT_CONTAINS(v13, 3); - ASSERT_NOT_CONTAINS(v13, 4); - ASSERT_NOT_CONTAINS(v13, 5); - ASSERT_NOT_CONTAINS(v13, 7); -} - -TEST(ImmutableMap, ExclusiveOwnership) { - immutable::map<int, int> v0; - - auto v1 = v0.set(1, 1); - auto v2 = v1.set(2, 2); - auto v3 = v2.set(3, 3); - - ASSERT_TRUE(v1.contains(1)); - ASSERT_TRUE(v2.contains(1)); - ASSERT_TRUE(v3.contains(1)); - - // Claiming exclusive ownership over v3 means v3 will no longer be valid after mutation, but - // older versions should be unperturbed. - auto v4 = std::move(v3).erase(1); - ASSERT_TRUE(v1.contains(1)); - ASSERT_TRUE(v2.contains(1)); - ASSERT_FALSE(v4.contains(1)); -} - -TEST(ImmutableSet, Basic) { - // Insert some values and verify that the data structure is behaving as expected - immutable::set<int> v0; - auto v1 = v0.insert(1); - // Record the iterator for the key '1', verify that this doesn't change after performing - // more inserts - auto v1it = v1.find(1); - - // Create distinct branches of the history from v1. v0 and v1 should be unaffected - auto v2 = v1.insert(2); - auto v3 = v1.insert(3); - - // Verify that values are as expected - ASSERT_EQ(v0.size(), 0); - - ASSERT_EQ(v1.size(), 1); - ASSERT_TRUE(v1.contains(1)); - ASSERT_EQ(*v1.find(1), 1); - ASSERT_FALSE(v1.contains(2)); - ASSERT_FALSE(v1.contains(3)); - - ASSERT_EQ(v2.size(), 2); - ASSERT_TRUE(v2.contains(1)); - ASSERT_EQ(*v2.find(1), 1); - ASSERT_TRUE(v2.contains(2)); - ASSERT_EQ(*v2.find(2), 2); - ASSERT_FALSE(v2.contains(3)); - - ASSERT_EQ(v3.size(), 2); - ASSERT_TRUE(v3.contains(1)); - ASSERT_EQ(*v3.find(1), 1); - ASSERT_TRUE(v3.contains(3)); - ASSERT_EQ(*v3.find(3), 3); - ASSERT_FALSE(v3.contains(2)); - - // Verify that v1's iterator did not change - ASSERT(v1.find(1) == v1it); - - // Verify that erase works as expected, and preserves history. - auto v4 = v3.erase(1).erase(3); - ASSERT_EQ(v4.size(), 0); - ASSERT_FALSE(v4.contains(1)); - ASSERT_FALSE(v4.contains(2)); - ASSERT_FALSE(v4.contains(3)); - ASSERT_EQ(v3.size(), 2); - ASSERT_TRUE(v3.contains(1)); - ASSERT_TRUE(v3.contains(3)); - ASSERT_EQ(v2.size(), 2); - ASSERT_TRUE(v2.contains(1)); - ASSERT_TRUE(v2.contains(2)); - ASSERT_EQ(v1.size(), 1); - ASSERT_TRUE(v1.contains(1)); - - ensureContainerInvariants({v0, v1, v2, v3, v4}); -} - -TEST(ImmutableSet, UserDefinedType) { - immutable::set<UserDefinedKey> v0; - auto v1 = v0.insert(UserDefinedKey(1)); - ASSERT(v1.find(UserDefinedKey(1)) != v1.end()); - ASSERT_EQ(*v1.find(UserDefinedKey(1)), UserDefinedKey(1)); - - ensureContainerInvariants({v0, v1}); -} - -TEST(ImmutableSet, IncomparableType) { - immutable::set<Incomparable, CompareIncomparable> v0; - auto v1 = v0.insert(Incomparable(1)); - ASSERT_TRUE(v1.contains(Incomparable(1))); - - ensureContainerInvariants(v0, CompareIncomparable{}); - ensureContainerInvariants(v1, CompareIncomparable{}); -} - -TEST(ImmutableSet, HeterogeneousLookup) { - immutable::set<std::string, StringCompare> v0; - auto v1 = v0.insert("str"); - - // Lookup using StringData without the need to convert to string. - ASSERT(v1.find("str"_sd) != v1.end()); - - ensureContainerInvariants({v0, v1}); -} - -TEST(ImmutableSet, Bounds) { - immutable::set<int> set; - constexpr int numKeys = 100; - for (int i = 0; i < numKeys; ++i) { - set = set.insert(2 * i); - ensureContainerInvariants(set); - } - - for (int i = 0; i < numKeys - 1; ++i) { - auto lowerExact = set.lower_bound(2 * i); - ASSERT(lowerExact != set.end() && *lowerExact == 2 * i); - auto upperExact = set.upper_bound(2 * i); - ASSERT(upperExact != set.end() && *upperExact == 2 * (i + 1)); - - auto lowerNear = set.lower_bound(2 * i + 1); - ASSERT(lowerNear != set.end() && *lowerNear == 2 * (i + 1)); - auto upperNear = set.upper_bound(2 * i + 1); - ASSERT(upperNear != set.end() && *upperNear == 2 * (i + 1)); - } -} - -TEST(ImmutableSet, Iteration) { - immutable::set<int> set; - constexpr int numKeys = 100; - for (int i = 0; i < numKeys; ++i) { - set = set.insert(2 * i); - ensureContainerInvariants(set); - } - - auto set0 = set; - auto it0 = set0.begin(); - - for (int i = 0; i < numKeys; ++i) { - set = set.insert(2 * i + 1); - ensureContainerInvariants(set); - } - - auto set1 = set; - auto it1 = set1.begin(); - - for (int i = 0; i < numKeys; ++i) { - ASSERT(it0 != set0.end()); - ASSERT_EQ(*it0, 2 * i); - ++it0; - - ASSERT(it1 != set1.end()); - ASSERT_EQ(*it1, 2 * i); - ++it1; - - ASSERT(it1 != set1.end()); - ASSERT_EQ(*it1, 2 * i + 1); - ++it1; - } - ASSERT(it0 == set0.end()); - ASSERT(it1 == set1.end()); -} - -TEST(ImmutableSet, Insert) { - auto v0 = init_sets<int>(); - - // Populate an initial set of values. - auto v1 = MUTATE_K(v0, insert, 1); - auto v2 = MUTATE_K(v1, insert, 2); - auto v3 = MUTATE_K(v2, insert, 3); - auto v4 = MUTATE_K(v3, insert, 5); - ASSERT_CONTAINS(v4, 1); - ASSERT_CONTAINS(v4, 2); - ASSERT_CONTAINS(v4, 3); - ASSERT_CONTAINS(v4, 5); - - // Insert of existing key is a noop. - auto v5 = MUTATE_K(v4, insert, 1); // enforces no duplicates - ASSERT_CONTAINS(v5, 1); - - // Insert at beginning works. - auto v6 = MUTATE_K(v4, insert, 0); - ASSERT_CONTAINS(v6, 0); - - // Insert at end works. - auto v7 = MUTATE_K(v4, insert, 6); - ASSERT_CONTAINS(v7, 6); - - // Insert in middle works. - auto v8 = MUTATE_K(v4, insert, 4); - ASSERT_CONTAINS(v8, 4); -} - -TEST(ImmutableSet, InsertViaIterator) { - auto v0 = init_sets<int>(); - - // Populate an initial set of values. - auto v1 = MUTATE_K(v0, insert, 1); - auto v2 = MUTATE_K(v1, insert, 2); - auto v3 = MUTATE_K(v2, insert, 3); - auto v4 = MUTATE_K(v3, insert, 6); - ASSERT_CONTAINS(v4, 1); - ASSERT_CONTAINS(v4, 2); - ASSERT_CONTAINS(v4, 3); - ASSERT_CONTAINS(v4, 6); - - // Giving the iterator for an existing element does a noop. - - auto it4_2 = SEARCH(v4, find, 2); - auto v5 = MUTATE_IT_K(v4, insert, it4_2, 2); // enforces no duplicates - ASSERT_CONTAINS(v5, 2); - - // Giving end() as hint works appropriately whether hint is accurate or not. - - auto end5 = END(v5); - auto v6 = MUTATE_IT_K(v5, insert, end5, 2); // enforces no duplicates - ASSERT_CONTAINS(v6, 2); - - auto end6 = END(v6); - auto v7 = MUTATE_IT_K(v6, insert, end6, 4); - ASSERT_CONTAINS(v7, 4); - - auto end7 = END(v7); - auto v8 = MUTATE_IT_K(v7, insert, end7, 7); - ASSERT_CONTAINS(v8, 7); - - // Giving hint from lower_bound works for both existing and new entries. - - auto lb8_2 = SEARCH(v8, lower_bound, 2); - auto v9 = MUTATE_IT_K(v8, insert, lb8_2, 2); // enforces no duplicates - ASSERT_CONTAINS(v9, 2); - - auto lb9_0 = SEARCH(v9, lower_bound, 0); - auto v10 = MUTATE_IT_K(v9, insert, lb9_0, 0); - ASSERT_CONTAINS(v10, 0); - - auto lb10_5 = SEARCH(v10, lower_bound, 5); - auto v11 = MUTATE_IT_K(v10, insert, lb10_5, 5); - ASSERT_CONTAINS(v11, 5); - - auto lb11_8 = SEARCH(v11, lower_bound, 8); - auto v12 = MUTATE_IT_K(v11, insert, lb11_8, 8); - ASSERT_CONTAINS(v12, 8); -} - -TEST(ImmutableSet, Erase) { - auto v0 = init_sets<int>(); - - // Populate an initial set of values. - auto v1 = MUTATE_K(v0, insert, 1); - auto v2 = MUTATE_K(v1, insert, 2); - auto v3 = MUTATE_K(v2, insert, 3); - auto v4 = MUTATE_K(v3, insert, 5); - ASSERT_CONTAINS(v4, 1); - ASSERT_CONTAINS(v4, 2); - ASSERT_CONTAINS(v4, 3); - ASSERT_CONTAINS(v4, 5); - - // Erase on existing key removes value. - auto v5 = MUTATE_K(v4, erase, 1); - ASSERT_NOT_CONTAINS(v5, 1); - - // Erase on non-existent key does nothing. - auto v6 = MUTATE_K(v5, erase, 0); - ASSERT_NOT_CONTAINS(v6, 0); - ASSERT_CONTAINS(v6, 2); - ASSERT_CONTAINS(v6, 3); - ASSERT_CONTAINS(v6, 5); -} - -TEST(ImmutableSet, EraseViaIterator) { - auto v0 = init_sets<int>(); - - // Populate an initial set of values. - auto v1 = MUTATE_K(v0, insert, 1); - auto v2 = MUTATE_K(v1, insert, 2); - auto v3 = MUTATE_K(v2, insert, 3); - auto v4 = MUTATE_K(v3, insert, 5); - auto v5 = MUTATE_K(v4, insert, 6); - ASSERT_CONTAINS(v5, 1); - ASSERT_CONTAINS(v5, 2); - ASSERT_CONTAINS(v5, 3); - ASSERT_CONTAINS(v5, 5); - ASSERT_CONTAINS(v5, 6); - - // Giving the iterator for an existing element erases the value. - - auto it5_2 = SEARCH(v5, find, 2); - auto v6 = MUTATE_IT_K(v5, erase, it5_2, 2); - ASSERT_NOT_CONTAINS(v6, 2); - - // Giving end() as hint works appropriately whether hint is accurate or not. - - auto end6 = END(v6); - auto v7 = MUTATE_IT_K(v6, erase, end6, 3); - ASSERT_NOT_CONTAINS(v7, 3); - - auto end7 = END(v7); - auto v8 = MUTATE_IT_K(v7, erase, end7, 4); - ASSERT_NOT_CONTAINS(v8, 4); - - auto end8 = END(v8); - auto v9 = MUTATE_IT_K(v8, erase, end8, 7); - ASSERT_NOT_CONTAINS(v9, 7); - - // Giving hint from lower_bound works for both existing and non-existing entries. - - auto lb9_5 = SEARCH(v9, lower_bound, 5); - auto v10 = MUTATE_IT_K(v9, erase, lb9_5, 5); - ASSERT_NOT_CONTAINS(v10, 5); - - auto lb10_0 = SEARCH(v10, lower_bound, 0); - auto v11 = MUTATE_IT_K(v10, erase, lb10_0, 0); - - auto lb11_4 = SEARCH(v10, lower_bound, 4); - auto v12 = MUTATE_IT_K(v11, erase, lb11_4, 4); - - auto lb12_7 = SEARCH(v12, lower_bound, 7); - auto v13 = MUTATE_IT_K(v12, erase, lb12_7, 7); - - ASSERT_CONTAINS(v13, 1); - ASSERT_CONTAINS(v13, 6); - ASSERT_NOT_CONTAINS(v13, 0); - ASSERT_NOT_CONTAINS(v13, 2); - ASSERT_NOT_CONTAINS(v13, 3); - ASSERT_NOT_CONTAINS(v13, 4); - ASSERT_NOT_CONTAINS(v13, 5); - ASSERT_NOT_CONTAINS(v13, 7); -} - -TEST(ImmutableSet, ExclusiveOwnership) { - immutable::set<int> v0; - - auto v1 = v0.insert(1); - auto v2 = v1.insert(2); - auto v3 = v2.insert(3); - - ASSERT_TRUE(v1.contains(1)); - ASSERT_TRUE(v2.contains(1)); - ASSERT_TRUE(v3.contains(1)); - - // Claiming exclusive ownership over v3 means v3 will no longer be valid after mutation, but - // older versions should be unperturbed. - auto v4 = std::move(v3).erase(1); - ASSERT_TRUE(v1.contains(1)); - ASSERT_TRUE(v2.contains(1)); - ASSERT_FALSE(v4.contains(1)); -} - -} // namespace -} // namespace mongo diff --git a/src/mongo/util/immutable/immutable_std_comparison_bm.cpp b/src/mongo/util/immutable/immutable_std_comparison_bm.cpp deleted file mode 100644 index 503984ca1d2..00000000000 --- a/src/mongo/util/immutable/immutable_std_comparison_bm.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/** - * Copyright (C) 2023-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#include <benchmark/benchmark.h> - -#include "mongo/util/immutable/map.h" -#include <map> - -namespace mongo { - -static void BM_std_insert_op(benchmark::State& state) { - std::map<int, int> map; - - int64_t i = 0; - for (; i < state.range(0); i++) { - map[i] = i; - } - - for (auto _ : state) { - map[i] = i; - i++; - benchmark::ClobberMemory(); - } -} - -static void BM_std_copy_op(benchmark::State& state) { - std::map<int, int> map; - - int64_t i = 0; - for (; i < state.range(0); i++) { - map[i] = i; - } - - std::map<int, int> mapCopy; - for (auto _ : state) { - mapCopy = map; - benchmark::ClobberMemory(); - } -} - -static void BM_std_find_op(benchmark::State& state) { - std::map<int, int> map; - - int64_t i = 0; - for (; i < state.range(0); i++) { - map[i] = i; - } - - for (auto _ : state) { - benchmark::DoNotOptimize(map.find(i - 1)); - benchmark::ClobberMemory(); - } -} - -static void BM_std_copy_and_insert_op(benchmark::State& state) { - std::map<int, int> map; - - int64_t i = 0; - for (; i < state.range(0); i++) { - map[i] = i; - } - - std::map<int, int> mapCopy; - for (auto _ : state) { - mapCopy = map; - mapCopy[i] = i; - benchmark::ClobberMemory(); - } -} - -static void BM_immutable_insert_op(benchmark::State& state) { - immutable::map<int, int> map; - - int64_t i = 0; - for (; i < state.range(0); i++) { - map = std::move(map).set(i, i); - } - - for (auto _ : state) { - map = std::move(map).set(i, i); - i++; - benchmark::ClobberMemory(); - } -} - -static void BM_immutable_copy_op(benchmark::State& state) { - immutable::map<int, int> map; - - int64_t i = 0; - for (; i < state.range(0); i++) { - map = std::move(map).set(i, i); - } - - immutable::map<int, int> mapCopy; - for (auto _ : state) { - mapCopy = map; - benchmark::ClobberMemory(); - } -} - -static void BM_immutable_find_op(benchmark::State& state) { - immutable::map<int, int> map; - - int64_t i = 0; - for (; i < state.range(0); i++) { - map = std::move(map).set(i, i); - } - - for (auto _ : state) { - benchmark::DoNotOptimize(map.find(i - 1)); - benchmark::ClobberMemory(); - } -} - -static void BM_immutable_copy_and_insert_op(benchmark::State& state) { - immutable::map<int, int> map; - - int64_t i = 0; - for (; i < state.range(0); i++) { - map = std::move(map).set(i, i); - } - - immutable::map<int, int> mapCopy; - for (auto _ : state) { - mapCopy = map.set(i, i); - benchmark::ClobberMemory(); - } -} - -// Run with varying container sizes: [ 8, 16, 32, 64, 128, 256, 512, 1024, 2k, 4k, 8k ]. -BENCHMARK(BM_std_insert_op)->RangeMultiplier(2)->Range(8, 8 << 10); -BENCHMARK(BM_immutable_insert_op)->RangeMultiplier(2)->Range(8, 8 << 10); - -BENCHMARK(BM_std_copy_op)->RangeMultiplier(2)->Range(8, 8 << 10); -BENCHMARK(BM_immutable_copy_op)->RangeMultiplier(2)->Range(8, 8 << 10); - -BENCHMARK(BM_std_find_op)->RangeMultiplier(2)->Range(8, 8 << 10); -BENCHMARK(BM_immutable_find_op)->RangeMultiplier(2)->Range(8, 8 << 10); - -BENCHMARK(BM_std_copy_and_insert_op)->RangeMultiplier(2)->Range(8, 8 << 10); -BENCHMARK(BM_immutable_copy_and_insert_op)->RangeMultiplier(2)->Range(8, 8 << 10); -} // namespace mongo diff --git a/src/mongo/util/immutable/immutable_unordered_test.cpp b/src/mongo/util/immutable/immutable_unordered_test.cpp deleted file mode 100644 index d6e2aa055dd..00000000000 --- a/src/mongo/util/immutable/immutable_unordered_test.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/** - * Copyright (C) 2023-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - - -#include "mongo/unittest/unittest.h" - -#include "mongo/util/immutable/unordered_map.h" -#include "mongo/util/immutable/unordered_set.h" -#include "mongo/util/string_map.h" - -#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kTest - -namespace mongo { -namespace { - -class UserDefinedKey { -public: - UserDefinedKey() = default; - explicit UserDefinedKey(int val) : a(val) {} - - bool operator==(const UserDefinedKey& rhs) const { - return a == rhs.a; - } - - // Use the Abseil hashing framework - template <typename H> - friend H AbslHashValue(H h, const UserDefinedKey& obj) { - return H::combine(std::move(h), obj.a); - } - -private: - int a = 0; -}; - -TEST(ImmutableUnorderedMap, Basic) { - // Insert some values and verify that the data structure is behaving as expected - immutable::unordered_map<int, int> v0; - auto v1 = v0.set(1, 2); - // Record the pointer to the value '1', verify that this doesn't change after performing more - // inserts - auto v1Val = v1.find(1); - - // Create distinct branches of the history from v1. v0 and v1 should be unaffected - auto v2 = v1.update(1, [](int v) { return v += 1; }); - auto v3 = v1.set(2, 3); - - // Verify that values are as expected - ASSERT_EQ(v0.size(), 0); - - ASSERT_EQ(v1.size(), 1); - ASSERT(v1.find(1)); - ASSERT_EQ(*v1.find(1), 2); - - ASSERT_EQ(v2.size(), 1); - ASSERT(v2.find(1)); - ASSERT_EQ(*v2.find(1), 3); - ASSERT(!v2.find(2)); - - ASSERT_EQ(v3.size(), 2); - ASSERT(v3.find(1)); - ASSERT_EQ(*v3.find(1), 2); - ASSERT(v3.find(2)); - ASSERT_EQ(*v3.find(2), 3); - - // Verify that pointer to v1's value did not change - ASSERT_EQ(v1.find(1), v1Val); -} - -TEST(ImmutableUnorderedMap, UserDefinedType) { - immutable::unordered_map<UserDefinedKey, int> v0; - auto v1 = v0.set(UserDefinedKey(1), 2); - ASSERT(v1.find(UserDefinedKey(1))); -} - -TEST(ImmutableUnorderedMap, HeterogeneousLookup) { - immutable::unordered_map<std::string, int, StringMapHasher, StringMapEq> v0; - auto v1 = v0.set("str", 1); - - // Lookup using StringData without the need to convert to string. - ASSERT(v1.find("str"_sd)); - - // Lookup using pre-hash - StringMapHashedKey hashedKey = StringMapHasher().hashed_key("str"_sd); - ASSERT(v1.find(hashedKey)); -} - -TEST(ImmutableUnorderedMap, BatchWrite) { - immutable::unordered_map<int, int> v0; - - auto transient = v0.transient(); - transient.set(1, 2); - transient.set(2, 3); - immutable::unordered_map<int, int> v1 = transient.persistent(); - - ASSERT(!v0.find(1)); - ASSERT(!v0.find(2)); - ASSERT(v1.find(1)); - ASSERT(v1.find(2)); -} - -TEST(ImmutableUnorderedMap, ExclusiveOwnership) { - immutable::unordered_map<int, int> v0; - - auto v1 = v0.set(1, 1); - auto v2 = v1.set(2, 2); - auto v3 = v2.set(3, 3); - - ASSERT(v1.find(1)); - ASSERT(v2.find(1)); - ASSERT(v3.find(1)); - - // Claiming exclusive ownership over v3 means v3 will no longer be valid after mutation, but - // older versions should be unperturbed. - auto v4 = std::move(v3).erase(1); - ASSERT(v1.find(1)); - ASSERT(v2.find(1)); - ASSERT(!v4.find(1)); -} - -TEST(ImmutableUnorderedSet, Basic) { - // Insert some values and verify that the data structure is behaving as expected - immutable::unordered_set<int> v0; - auto v1 = v0.insert(1); - // Record the pointer to the value '1', verify that this doesn't change after performing more - // inserts - auto v1Val = v1.find(1); - - // Make more versions of the data structure, v2 and v3 are now distinct history branches from - // v1. v0 and v1 should be unaffected - auto v2 = v1.insert(2); - auto v3 = v1.insert(2); - - // Verify that values are as expected - ASSERT_EQ(v0.size(), 0); - - ASSERT_EQ(v1.size(), 1); - ASSERT(v1.find(1)); - ASSERT_EQ(*v1.find(1), 1); - - ASSERT_EQ(v2.size(), 2); - ASSERT(v2.find(1)); - ASSERT_EQ(*v2.find(1), 1); - ASSERT(v2.find(2)); - ASSERT_EQ(*v2.find(2), 2); - - ASSERT_EQ(v3.size(), 2); - ASSERT(v3.find(1)); - ASSERT_EQ(*v3.find(1), 1); - ASSERT(v3.find(2)); - ASSERT_EQ(*v3.find(2), 2); - - // Verify that pointer to v1's value did not change - ASSERT_EQ(v1.find(1), v1Val); - // Key is stored in v2 and v3 with different addresses - ASSERT_NE(v2.find(2), v3.find(2)); -} - -TEST(ImmutableUnorderedSet, UserDefinedType) { - immutable::unordered_set<UserDefinedKey> v0; - auto v1 = v0.insert(UserDefinedKey(1)); - ASSERT(v1.find(UserDefinedKey(1))); -} - -TEST(ImmutableUnorderedSet, HeterogeneousLookup) { - immutable::unordered_set<std::string, StringMapHasher, StringMapEq> v0; - auto v1 = v0.insert("str"); - - // Lookup using StringData without the need to convert to string. - ASSERT(v1.find("str"_sd)); - - // Lookup using pre-hash - StringMapHashedKey hashedKey = StringMapHasher().hashed_key("str"_sd); - ASSERT(v1.find(hashedKey)); -} - -TEST(ImmutableUnorderedSet, BatchWrite) { - immutable::unordered_set<int> v0; - - auto transient = v0.transient(); - transient.insert(1); - transient.insert(2); - immutable::unordered_set<int> v1 = transient.persistent(); - - ASSERT(!v0.find(1)); - ASSERT(!v0.find(2)); - ASSERT(v1.find(1)); - ASSERT(v1.find(2)); -} - -TEST(ImmutableUnorderedSet, ExclusiveOwnership) { - immutable::unordered_set<int> v0; - - auto v1 = v0.insert(1); - auto v2 = v1.insert(2); - auto v3 = v2.insert(3); - - ASSERT(v1.find(1)); - ASSERT(v2.find(1)); - ASSERT(v3.find(1)); - - auto v4 = std::move(v3).erase(1); - ASSERT(v1.find(1)); - ASSERT(v2.find(1)); - ASSERT(!v4.find(1)); -} - -} // namespace -} // namespace mongo diff --git a/src/mongo/util/immutable/immutable_vector_test.cpp b/src/mongo/util/immutable/immutable_vector_test.cpp deleted file mode 100644 index 60329423403..00000000000 --- a/src/mongo/util/immutable/immutable_vector_test.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Copyright (C) 2023-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - - -#include "mongo/unittest/unittest.h" - -#include "mongo/util/immutable/vector.h" - -#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kTest - -namespace mongo { -namespace { - -class UserDefinedType { -public: - UserDefinedType() = default; - explicit UserDefinedType(int val) : a(val) {} - - bool operator==(const UserDefinedType& rhs) const { - return a == rhs.a; - } - -private: - int a = 0; -}; - -TEST(ImmutableUnorderedMap, Basic) { - // Insert some values and verify that the data structure is behaving as expected - immutable::vector<int> v0; - auto v1 = v0.push_back(1); - // Record the pointer to the value at index '0', verify that this doesn't change after - // performing additional modifications - auto* v1Val = &(*v1.begin()); - - // Create distinct branches of the history from v1. v0 and v1 should be unaffected - auto v2 = v1.update(0, [](int v) { return v += 1; }); - auto v3 = v1.push_back(3); - auto v4 = v1.set(0, 4); - - // Verify that values are as expected - ASSERT_EQ(v0.size(), 0); - - ASSERT_EQ(v1.size(), 1); - ASSERT_EQ(v1.at(0), 1); - - ASSERT_EQ(v2.size(), 1); - ASSERT_EQ(v2.at(0), 2); - - ASSERT_EQ(v3.size(), 2); - ASSERT_EQ(v3.at(0), 1); - ASSERT_EQ(v3.at(1), 3); - - ASSERT_EQ(v4.size(), 1); - ASSERT_EQ(v4.at(0), 4); - - // Verify that pointer to v1's value did not change - ASSERT_EQ(&(*v1.begin()), v1Val); -} - -TEST(ImmutableUnorderedMap, UserDefinedType) { - immutable::vector<UserDefinedType> v0; - auto v1 = v0.push_back(UserDefinedType(1)); - ASSERT(v1.at(0) == UserDefinedType(1)); -} - -TEST(ImmutableUnorderedMap, BatchWrite) { - immutable::vector<int> v0; - - auto transient = v0.transient(); - transient.push_back(1); - transient.push_back(2); - immutable::vector<int> v1 = transient.persistent(); - - ASSERT_EQ(v0.size(), 0); - ASSERT_EQ(v1.size(), 2); - ASSERT_EQ(v1.at(0), 1); - ASSERT_EQ(v1.at(1), 2); -} - -TEST(ImmutableUnorderedMap, ExclusiveOwnership) { - immutable::vector<int> v0; - - auto v1 = v0.push_back(1); - auto v2 = v1.push_back(2); - auto v3 = v2.push_back(3); - - ASSERT_EQ(v1.size(), 1); - ASSERT_EQ(v2.size(), 2); - ASSERT_EQ(v3.size(), 3); - - // Claiming exclusive ownership over v3 means v3 will no longer be valid after mutation, but - // older versions should be unperturbed. - auto v4 = std::move(v3).take(0); - ASSERT_EQ(v1.size(), 1); - ASSERT_EQ(v2.size(), 2); - ASSERT_EQ(v4.size(), 0); -} - -} // namespace -} // namespace mongo diff --git a/src/mongo/util/immutable/map.h b/src/mongo/util/immutable/map.h deleted file mode 100644 index 351cfd560da..00000000000 --- a/src/mongo/util/immutable/map.h +++ /dev/null @@ -1,358 +0,0 @@ -/** - * Copyright (C) 2023-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#pragma once - -#include <algorithm> - -#include <immer/flex_vector.hpp> - -#include "mongo/util/immutable/details/map.h" -#include "mongo/util/immutable/details/memory_policy.h" - - -namespace mongo::immutable { - -/** - * Immutable ordered dictionary. - * - * Interfaces that "modify" the map are 'const' and return a new version of the map with the - * modifications applied and leaves the original version untouched. - * - * It is optimized for efficient copies and low memory usage when multiple versions of the map exist - * simultaneously at the expense of regular lookups not being as efficient as the regular std - * ordered containers. Suitable for use in code that uses the copy-on-write pattern. - * - * Thread-safety: All methods are const, it is safe to perform modifications that result in new - * versions from multiple threads concurrently. - * - * Memory management: Internal memory management is done using reference counting, memory is free'd - * as references to different versions of the map are released. - * - * Built on top of 'immer::flex_vector'. - * Documentation: 'immer/flex_vector.h' and https://sinusoid.es/immer/ - */ -template <typename Key, typename Value, typename Compare = std::less<Key>> -class map { -public: - using key_type = Key; - using mapped_type = Value; - using value_type = std::pair<Key, Value>; - using storage_type = immer::flex_vector<value_type, detail::MemoryPolicy>; - using iterator = typename storage_type::iterator; - using size_type = typename storage_type::size_type; - using diference_type = std::ptrdiff_t; - using reference = const value_type&; - using const_reference = const value_type&; - using memory_policy_type = detail::MemoryPolicy; - using comp = Compare; - - map() = default; - - struct default_value { - const mapped_type& operator()() const { - static mapped_type v = mapped_type{}; - return v; - } - }; - - struct error_value { - const mapped_type& operator()() const { - throw std::out_of_range{"key not found"}; - } - }; - - bool operator==(const map& other) const { - return _storage == other._storage; - } - - bool operator!=(const map& other) const { - return !(*this == other); - } - - [[nodiscard]] iterator begin() const { - return _storage.begin(); - } - - [[nodiscard]] iterator end() const { - return _storage.end(); - } - - size_t size() const { - return _storage.size(); - } - - /** - * Returns a reference to the value associated with 'key' if one exists, otherwise a default - * constructed Value. - */ - const mapped_type& operator[](const key_type& key) const { - auto it = find(key); - if (it == end()) { - return default_value{}(); - } - return it->second; - } - - /** - * Returns a reference to the value associated with 'key' if one exists, otherwise throws. - */ - const mapped_type& at(const key_type& key) const { - auto it = find(key); - if (it == end()) { - return error_value{}(); - } - return it->second; - } - - /** - * Insert a new 'key' and 'value' pair to the map. - * - * Returns the modified map, or the original if 'key' was already contained. - */ - template <typename K, typename V> - [[nodiscard]] map insert(K&& key, V&& value) const& { - return map{details::map::insert<map<key_type, mapped_type, comp>>( - _storage, std::forward<K>(key), std::forward<V>(value))}; - } - template <typename K, typename V> - [[nodiscard]] map insert(K&& key, V&& value) && { - return map{details::map::insert<map<key_type, mapped_type, comp>>( - std::move(_storage), std::forward<K>(key), std::forward<V>(value))}; - } - - /** - * Insert a new 'key' and 'value' pair to the map. Uses 'it' as a hint. - * - * Returns the modified map, or the original if 'key' was already contained. - */ - template <typename K, typename V> - [[nodiscard]] map insert(iterator it, K&& key, V&& value) const& { - return map{details::map::insert<map<key_type, mapped_type, comp>>( - _storage, it, std::forward<K>(key), std::forward<V>(value))}; - } - template <typename K, typename V> - [[nodiscard]] map insert(iterator it, K&& key, V&& value) && { - return map{details::map::insert<map<key_type, mapped_type, comp>>( - std::move(_storage), it, std::forward<K>(key), std::forward<V>(value))}; - } - - /** - * Sets the value associated with 'key' to 'value', inserting the new pair if 'key' does not - * exist. - * - * Returns the modified map. - */ - template <typename K, typename V> - [[nodiscard]] map set(K&& key, V&& value) const& { - return map{details::map::set<map<key_type, mapped_type, comp>>( - _storage, std::forward<K>(key), std::forward<V>(value))}; - } - template <typename K, typename V> - [[nodiscard]] map set(K&& key, V&& value) && { - return map{details::map::set<map<key_type, mapped_type, comp>>( - std::move(_storage), std::forward<K>(key), std::forward<V>(value))}; - } - - /** - * Sets the value associated with 'key' to 'value', inserting the new pair if 'key' does not - * exist. Treats 'it' as a hint. - * - * Returns the modified map. - */ - template <typename K, typename V> - [[nodiscard]] map set(iterator it, K&& key, V&& value) const& { - return map{details::map::set<map<key_type, mapped_type, comp>>( - _storage, it, std::forward<K>(key), std::forward<V>(value))}; - } - template <typename K, typename V> - [[nodiscard]] map set(iterator it, K&& key, V&& value) && { - return map{details::map::set<map<key_type, mapped_type, comp>>( - std::move(_storage), it, std::forward<K>(key), std::forward<V>(value))}; - } - - /** - * Sets the value associated with 'key' by applying 'valueUpdate' to the existing value, or to a - * default-constructed value if no entry for 'key' exists. - * - * The signature of 'valueUpdate' should be equivalent to - * std::function<mapped_type(const mapped_type&)>. Returns the modified map. - */ - template <typename K, typename U> - [[nodiscard]] map update(K&& key, U&& valueUpdate) const& { - return map{details::map::update<map<key_type, mapped_type, comp>>( - _storage, std::forward<K>(key), std::forward<U>(valueUpdate))}; - } - template <typename K, typename U> - [[nodiscard]] map update(K&& key, U&& valueUpdate) && { - return map{details::map::update<map<key_type, mapped_type, comp>>( - std::move(_storage), std::forward<K>(key), std::forward<U>(valueUpdate))}; - } - - /** - * Updates the value associated with 'key' by applying 'valueUpdate' to the existing value, or - * to a default-constructed value if no entry for 'key' exists. Uses 'it' as a hint. - * - * The signature of 'valueUpdate' should be equivalent to - * std::function<mapped_type(const mapped_type&)>. Returns the modified map. - */ - template <typename K, typename U> - [[nodiscard]] map update(iterator it, K&& key, U&& valueUpdate) const& { - return map{details::map::update<map<key_type, mapped_type, comp>>( - _storage, it, std::forward<K>(key), std::forward<U>(valueUpdate))}; - } - template <typename K, typename U> - [[nodiscard]] map update(iterator it, K&& key, U&& valueUpdate) && { - return map{details::map::update<map<key_type, mapped_type, comp>>( - std::move(_storage), it, std::forward<K>(key), std::forward<U>(valueUpdate))}; - } - - /** - * Updates the value associated with 'key' if it exists by applying 'valueUpdate' to the - * existing value. - * - * The signature of 'valueUpdate' should be equivalent to - * std::function<mapped_type(const mapped_type&)>. Returns the modified map, or the original if - * 'key' does not exist. - */ - template <typename K, typename U> - [[nodiscard]] map update_if_exists(K&& key, U&& valueUpdate) const& { - return map{details::map::update_if_exists<map<key_type, mapped_type, comp>>( - _storage, std::forward<K>(key), std::forward<U>(valueUpdate))}; - } - template <typename K, typename U> - [[nodiscard]] map update_if_exists(K&& key, U&& valueUpdate) && { - return map{details::map::update_if_exists<map<key_type, mapped_type, comp>>( - std::move(_storage), std::forward<K>(key), std::forward<U>(valueUpdate))}; - } - - /** - * Updates the value associated with 'key' if it exists by applying 'valueUpdate' to the - * existing value. Uses 'it' as a hint. - * - * The signature of 'valueUpdate' should be equivalent to - * std::function<mapped_type(const mapped_type&)>. Returns the modified map, or the original if - * 'key' does not exist. - */ - template <typename K, typename U> - [[nodiscard]] map update_if_exists(iterator it, K&& key, U&& valueUpdate) const& { - return map{details::map::update_if_exists<map<key_type, mapped_type, comp>>( - _storage, it, std::forward<K>(key), std::forward<U>(valueUpdate))}; - } - template <typename K, typename U> - [[nodiscard]] map update_if_exists(iterator it, K&& key, U&& valueUpdate) && { - return map{details::map::update_if_exists<map<key_type, mapped_type, comp>>( - std::move(_storage), it, std::forward<K>(key), std::forward<U>(valueUpdate))}; - } - - /** - * Removes 'key' and its associated value from the map. - * - * Returns the modified map, or the original if 'key' does not exist. - */ - template <typename K> - [[nodiscard]] map erase(K&& key) const& { - return map{ - details::map::erase<map<key_type, mapped_type, comp>>(_storage, std::forward<K>(key))}; - } - template <typename K> - [[nodiscard]] map erase(K&& key) && { - return map{details::map::erase<map<key_type, mapped_type, comp>>(std::move(_storage), - std::forward<K>(key))}; - } - - /** - * Removes entry assocated with 'it' from the map. 'it' must match 'key' or it will not be - * erased. - * - * Returns the modified map, or the original if 'it' is equal to 'end()'. - */ - template <typename K> - [[nodiscard]] map erase(iterator it, K&& key) const& { - return map{details::map::erase<map<key_type, mapped_type, comp>>( - _storage, it, std::forward<K>(key))}; - } - template <typename K> - [[nodiscard]] map erase(iterator it, K&& key) && { - return map{details::map::erase<map<key_type, mapped_type, comp>>( - std::move(_storage), it, std::forward<K>(key))}; - } - - /** - * Returns an iterator to the entry for 'key' if it exists, 'end()' otherwise. - * - * Supports heterogeneous lookup. - */ - template <typename SearchKey> - [[nodiscard]] iterator find(const SearchKey& key) const { - return details::map::find<map<key_type, mapped_type, comp>>(_storage, key); - } - - /** - * Returns true if map contains an entry for 'key'. - * - * Supports heterogeneous lookup. - */ - template <typename SearchKey> - bool contains(const SearchKey& key) const { - return find(key) != end(); - } - - /** - * Returns the first the entry greater than or equal to 'key' if one exists, 'end()' otherwise. - * - * Supports heterogeneous lookup. - */ - template <typename SearchKey> - [[nodiscard]] iterator lower_bound(const SearchKey& key) const { - return details::map::lower_bound<map<key_type, mapped_type, comp>>(_storage, key); - } - - /** - * Returns the first the entry strictly greater than 'key' if one exists, 'end()' otherwise. - * - * Supports heterogeneous lookup. - */ - template <typename SearchKey> - [[nodiscard]] iterator upper_bound(const SearchKey& key) const { - return std::upper_bound( - _storage.begin(), - _storage.end(), - key, - [](const SearchKey& a, const value_type& b) -> bool { return comp{}(a, b.first); }); - } - -private: - template <typename S> - explicit map(S&& s) : _storage{std::forward<S>(s)} {} - - storage_type _storage; -}; - -} // namespace mongo::immutable diff --git a/src/mongo/util/immutable/set.h b/src/mongo/util/immutable/set.h deleted file mode 100644 index 24e5183c34f..00000000000 --- a/src/mongo/util/immutable/set.h +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Copyright (C) 2023-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#pragma once - -#include <algorithm> - -#include <immer/flex_vector.hpp> - -#include "mongo/util/immutable/details/memory_policy.h" -#include "mongo/util/immutable/details/set.h" - -namespace mongo::immutable { - -/** - * Immutable ordered set. - * - * Interfaces that "modify" the set are 'const' and return a new version of the set with the - * modifications applied and leaves the original version untouched. - * - * It is optimized for efficient copies and low memory usage when multiple versions of the set exist - * simultaneously at the expense of regular lookups not being as efficient as the regular std - * ordered containers. Suitable for use in code that uses the copy-on-write pattern. - * - * Thread-safety: All methods are const, it is safe to perform modifications that result in new - * versions from multiple threads concurrently. - * - * Memory management: Internal memory management is done using reference counting, memory is free'd - * as references to different versions of the set are released. - * - * Built on top of 'immer::flex_vector'. - * Documentation: 'immer/flex_vector.h' and https://sinusoid.es/immer/ - */ -template <typename Key, typename Compare = std::less<Key>> -class set { -public: - using key_type = Key; - using storage_type = immer::flex_vector<key_type, detail::MemoryPolicy>; - using iterator = typename storage_type::iterator; - using size_type = typename storage_type::size_type; - using diference_type = std::ptrdiff_t; - using reference = const key_type&; - using const_reference = const key_type&; - using memory_policy_type = detail::MemoryPolicy; - using comp = Compare; - - set() = default; - - bool operator==(const set& other) const { - return _storage == other._storage; - } - - bool operator!=(const set& other) const { - return !(*this == other); - } - - [[nodiscard]] iterator begin() const { - return _storage.begin(); - } - - [[nodiscard]] iterator end() const { - return _storage.end(); - } - - size_t size() const { - return _storage.size(); - } - - /** - * Insert a new 'key' to the set. - * - * Returns the modified set, or the original if 'key' was already contained. - */ - template <typename K> - [[nodiscard]] set insert(K&& key) const& { - return set{details::set::insert<set>(_storage, std::forward<K>(key))}; - } - template <typename K> - [[nodiscard]] set insert(K&& key) && { - return set{details::set::insert<set>(std::move(_storage), std::forward<K>(key))}; - } - - /** - * Insert a new 'key' to the set. Uses 'it' as a hint. - * - * Returns the modified set, or the original if 'key' was already contained. - */ - template <typename K> - [[nodiscard]] set insert(iterator it, K&& key) const& { - return set{details::set::insert<set>(_storage, it, std::forward<K>(key))}; - } - template <typename K> - [[nodiscard]] set insert(iterator it, K&& key) && { - return set{details::set::insert<set>(std::move(_storage), it, std::forward<K>(key))}; - } - - /** - * Removes 'key' from the set. - * - * Returns the modified set, or the original if 'key' does not exist. - */ - template <typename K> - [[nodiscard]] set erase(K&& key) const& { - return set{details::set::erase<set>(_storage, std::forward<K>(key))}; - } - template <typename K> - [[nodiscard]] set erase(K&& key) && { - return set{details::set::erase<set>(std::move(_storage), std::forward<K>(key))}; - } - - /** - * Removes key associated with 'key' from the set. Uses 'it' as a hint. - * - * Returns the modified set, or the original if 'key' does not exist. - */ - template <typename K> - [[nodiscard]] set erase(iterator it, K&& key) const& { - return set{details::set::erase<set>(_storage, it, std::forward<K>(key))}; - } - template <typename K> - [[nodiscard]] set erase(iterator it, K&& key) && { - return set{details::set::erase<set>(std::move(_storage), it, std::forward<K>(key))}; - } - - /** - * Returns an iterator to the element for 'key' if it exists, 'end()' otherwise. - * - * Supports heterogeneous lookup. - */ - template <class SearchKey> - [[nodiscard]] iterator find(const SearchKey& key) const { - return details::set::find<set>(_storage, key); - } - - /** - * Returns true if set contains 'key'. - * - * Supports heterogeneous lookup. - */ - template <class SearchKey> - bool contains(const SearchKey& key) const { - return find(key) != end(); - } - - /** - * Returns the first the element greater than or equal to 'key' if one exists, 'end()' - * otherwise. - * - * Supports heterogeneous lookup. - */ - template <class SearchKey> - [[nodiscard]] iterator lower_bound(const SearchKey& key) const { - return details::set::lower_bound<set>(_storage, key); - } - - /** - * Returns the first the element strictly greater than 'key' if one exists, 'end()' otherwise. - * - * Supports heterogeneous lookup. - */ - template <class SearchKey> - [[nodiscard]] iterator upper_bound(const SearchKey& key) const { - return std::upper_bound(_storage.begin(), _storage.end(), key, comp{}); - } - -private: - template <typename S> - explicit set(S&& s) : _storage{std::forward<S>(s)} {} - - storage_type _storage; -}; - -} // namespace mongo::immutable diff --git a/src/mongo/util/immutable/unordered_map.h b/src/mongo/util/immutable/unordered_map.h deleted file mode 100644 index b024bd5b64a..00000000000 --- a/src/mongo/util/immutable/unordered_map.h +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (C) 2023-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#pragma once - -#include <immer/map.hpp> -#include <immer/map_transient.hpp> - -#include "mongo/stdx/trusted_hasher.h" -#include "mongo/util/immutable/details/memory_policy.h" - -namespace mongo::immutable { - -/** - * Immutable unordered hash table. - * - * Interfaces that "modify" the hash table are 'const' and returns a new version of the table with - * the modifications applied and leaves the original version untouched. - * - * It is optimized for efficient copies and low memory usage when multiple versions of the table - * exist simultaneously at the expense of regular lookups not being as efficient as the regular - * stdx unordered containers. Suitable for use in code that uses the copy-on-write pattern. - * - * Thread-safety: All methods are const, it is safe to perform modifications that result in new - * versions from multiple threads concurrently. - * - * Memory management: Internal memory management is done using reference counting, memory is free'd - * as references to different versions of the table are released. - * - * Multiple modifications can be done efficiently using the 'transient()' interface. - * - * Documentation: 'immer/map.h' and https://sinusoid.es/immer/ - */ -template <class K, - class V, - class Hasher = DefaultHasher<K>, - class Eq = absl::container_internal::hash_default_eq<K>> -using unordered_map = immer::map<K, V, EnsureTrustedHasher<Hasher, K>, Eq, detail::MemoryPolicy>; -} // namespace mongo::immutable diff --git a/src/mongo/util/immutable/unordered_set.h b/src/mongo/util/immutable/unordered_set.h deleted file mode 100644 index c5133218007..00000000000 --- a/src/mongo/util/immutable/unordered_set.h +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (C) 2023-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#pragma once - -#include <immer/set.hpp> -#include <immer/set_transient.hpp> - -#include "mongo/stdx/trusted_hasher.h" -#include "mongo/util/immutable/details/memory_policy.h" - -namespace mongo::immutable { - -/** - * Immutable unordered hash set. - * - * Interfaces that "modify" the hash set are 'const' and returns a new version of the set with - * the modifications applied and leaves the original version untouched. - * - * It is optimized for efficient copies and low memory usage when multiple versions of the set - * exist simultaneously at the expense of regular lookups not being as efficient as the regular - * stdx unordered containers. Suitable for use in code that uses the copy-on-write - * pattern. - * - * Thread-safety: All methods are const, it is safe to perform modifications that result in new - * versions from multiple threads concurrently. - * - * Memory management: Internal memory management is done using reference counting, memory is free'd - * as references to different versions of the set are released. - * - * Multiple modifications can be done efficiently using the 'transient()' interface. - * - * Documentation: 'immer/set.h' and https://sinusoid.es/immer/ - */ -template <class T, - class Hasher = DefaultHasher<T>, - class Eq = absl::container_internal::hash_default_eq<T>> -using unordered_set = immer::set<T, EnsureTrustedHasher<Hasher, T>, Eq, detail::MemoryPolicy>; -} // namespace mongo::immutable diff --git a/src/mongo/util/immutable/vector.h b/src/mongo/util/immutable/vector.h deleted file mode 100644 index b9cc205575d..00000000000 --- a/src/mongo/util/immutable/vector.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (C) 2023-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#pragma once - -#include <immer/vector.hpp> -#include <immer/vector_transient.hpp> - -#include "mongo/util/immutable/details/memory_policy.h" - -namespace mongo::immutable { - -/** - * Immutable vector. - * - * Interfaces that "modify" the vector are 'const' and return a new version of the vector with - * the modifications applied and leave the original version untouched. - * - * It is optimized for efficient copies and low memory usage when multiple versions of the vector - * exist simultaneously at the expense of regular lookups not being as efficient as the regular - * std::vector implementation. Suitable for use in code that uses the copy-on-write pattern. - * - * Thread-safety: All methods are const, it is safe to perform modifications that result in new - * versions from multiple threads concurrently. - * - * Memory management: Internal memory management is done using reference counting, memory is free'd - * as references to different versions of the vector are released. - * - * Multiple modifications can be done efficiently using the 'transient()' interface. - * - * Documentation: 'immer/vector.h' and https://sinusoid.es/immer/ - */ -template <class T> -using vector = immer::vector<T, detail::MemoryPolicy>; -} // namespace mongo::immutable |
