From 992b1f49f1e2736df22f0d1b4259940afa419fa1 Mon Sep 17 00:00:00 2001 From: Tomoki Nakagawa Date: Sat, 22 Feb 2025 03:40:10 +0000 Subject: [PATCH] Introduce constexpr flat containers. These containers offer `constexpr` construction, by creating a sorted array at compile time, and provide read-only lookup of values. They have a very minimal API surface, sufficient for Mozc's use, and deliberately diverge from the standard STL API for simplicity. * `FlatSet` has `bool contains(K)`: The standard C++ interface for sets. * `FlatMap` has `const T* FindOrNull(K)`, not `iterator find(K)`: No iterator involved. * `FlatMultiMap` has `absl::Span> EqualSpan(K)`, not `pair equal_range(K)`: No iterator, or no `std::pair` that lacks necessary `constexpr` support (yet). These are meant for replacing patterns like `const auto *map = new std::map<...>(...)` in the codebase, reducing runtime startup work and eliminating leaked heap allocations, which is particularly important when linked in the Windows DLL. PiperOrigin-RevId: 729741608 --- src/base/container/BUILD.bazel | 74 +++++++++++++ src/base/container/entry.h | 46 ++++++++ src/base/container/flat_internal.h | 128 +++++++++++++++++++++++ src/base/container/flat_map.h | 98 +++++++++++++++++ src/base/container/flat_map_test.cc | 78 ++++++++++++++ src/base/container/flat_multimap.h | 107 +++++++++++++++++++ src/base/container/flat_multimap_test.cc | 98 +++++++++++++++++ src/base/container/flat_set.h | 86 +++++++++++++++ src/base/container/flat_set_test.cc | 73 +++++++++++++ src/build_tools/progress_printer.py | 28 +++++ 10 files changed, 816 insertions(+) create mode 100644 src/base/container/entry.h create mode 100644 src/base/container/flat_internal.h create mode 100644 src/base/container/flat_map.h create mode 100644 src/base/container/flat_map_test.cc create mode 100644 src/base/container/flat_multimap.h create mode 100644 src/base/container/flat_multimap_test.cc create mode 100644 src/base/container/flat_set.h create mode 100644 src/base/container/flat_set_test.cc diff --git a/src/base/container/BUILD.bazel b/src/base/container/BUILD.bazel index 2d91399af..2fe20aeb8 100644 --- a/src/base/container/BUILD.bazel +++ b/src/base/container/BUILD.bazel @@ -53,6 +53,80 @@ mozc_cc_test( ], ) +mozc_cc_library( + name = "entry", + hdrs = ["entry.h"], +) + +mozc_cc_library( + name = "flat_internal", + hdrs = ["flat_internal.h"], + deps = [ + "@com_google_absl//absl/log", + "@com_google_absl//absl/types:span", + ], +) + +mozc_cc_library( + name = "flat_map", + hdrs = ["flat_map.h"], + deps = [ + ":entry", + ":flat_internal", + "@com_google_absl//absl/base:nullability", + "@com_google_absl//absl/types:span", + ], +) + +mozc_cc_test( + name = "flat_map_test", + srcs = ["flat_map_test.cc"], + deps = [ + ":flat_map", + "//testing:gunit_main", + "@com_google_absl//absl/strings", + ], +) + +mozc_cc_library( + name = "flat_multimap", + hdrs = ["flat_multimap.h"], + deps = [ + ":entry", + ":flat_internal", + "@com_google_absl//absl/types:span", + ], +) + +mozc_cc_test( + name = "flat_multimap_test", + srcs = ["flat_multimap_test.cc"], + deps = [ + ":flat_multimap", + "//testing:gunit_main", + "@com_google_absl//absl/strings", + ], +) + +mozc_cc_library( + name = "flat_set", + hdrs = ["flat_set.h"], + deps = [ + ":flat_internal", + "@com_google_absl//absl/types:span", + ], +) + +mozc_cc_test( + name = "flat_set_test", + srcs = ["flat_set_test.cc"], + deps = [ + ":flat_set", + "//testing:gunit_main", + "@com_google_absl//absl/strings", + ], +) + mozc_cc_library( name = "freelist", hdrs = ["freelist.h"], diff --git a/src/base/container/entry.h b/src/base/container/entry.h new file mode 100644 index 000000000..e2e196b2e --- /dev/null +++ b/src/base/container/entry.h @@ -0,0 +1,46 @@ +// Copyright 2010-2021, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef MOZC_BASE_CONTAINER_ENTRY_H_ +#define MOZC_BASE_CONTAINER_ENTRY_H_ + +namespace mozc { + +// Represents an entry in a map. +// +// We need constexpr copy/move, which `std::pair` doesn't get until C++20. +template +struct Entry { + K key; + V value; +}; + +} // namespace mozc + +#endif // MOZC_BASE_CONTAINER_ENTRY_H_ diff --git a/src/base/container/flat_internal.h b/src/base/container/flat_internal.h new file mode 100644 index 000000000..d7e5c5d98 --- /dev/null +++ b/src/base/container/flat_internal.h @@ -0,0 +1,128 @@ +// Copyright 2010-2021, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef MOZC_BASE_CONTAINER_FLAT_INTERNAL_H_ +#define MOZC_BASE_CONTAINER_FLAT_INTERNAL_H_ + +#include + +#include +#include +#include +#include +#include + +#include "absl/log/log.h" +#include "absl/types/span.h" + +namespace mozc::internal { + +// Sorts the given span in place. +// C++17 backport of constexpr `std::sort` from C++20. +template > +constexpr void Sort(absl::Span span, const Compare &cmp = {}) { + // Since we're dealing with compile-time constants, expected to be small, + // we use insertion sort. It's faster for small inputs, and easier to + // implement :) + for (int i = 1; i < span.size(); ++i) { + if (!cmp(span[i], span[i - 1])) continue; + + T tmp = std::move(span[i]); + int j = i; + do { + span[j] = std::move(span[j - 1]); + --j; + } while (j > 0 && cmp(tmp, span[j - 1])); + span[j] = std::move(tmp); + } +} + +template +constexpr std::array, N> ToArrayImpl( + T (&&a)[N], std::index_sequence) { + return {std::move(a[I])...}; +} + +// Converts a C-style array to a `std::array`. +// C++17 backport of `std::to_array` from C++20. +template +constexpr std::array, N> ToArray(T (&&a)[N]) { + return ToArrayImpl(std::move(a), std::make_index_sequence()); +} + +// Finds the first element satisfying the given predicate, or `span.end()` if +// none exists. +// +// `pred` must be non-decreasing in `span`: if `l <= r`, then +// `pred(span[l]) <= pred(span[r])`, where `false < true`. +// +// NOTE: To use `std::lower_bound`, we'd need an iterator adapter that extracts +// the first element of each pair, which would be more code than a custom binary +// search. +template +constexpr typename absl::Span::iterator FindFirst( + absl::Span span, Predicate pred) { + if (span.empty()) return span.end(); + if (pred(span.front())) return span.begin(); + + // Invariant: `pred(span[l])` is false and `pred(span[r])` is true (virtually + // assuming `pred(span[span.size()])` is true). Note that `mid` will never be + // `span.size()` (which would be out-of-bound access), because the largest + // possible `mid` occurs when `(l, r) = (span.size() - 2, span.size())`, + // resulting in `mid = span.size() - 1`. + int l = 0; + int r = span.size(); + while (r - l > 1) { + int mid = l + (r - l) / 2; + (pred(span[mid]) ? r : l) = mid; + } + + return span.begin() + r; +} + +// Non-constexpr function to be called when a duplicate entry is found, causing +// a compile-time error. +inline void DuplicateEntryFound() { LOG(FATAL) << "Duplicate entry found"; } + +// Sorts the given span in place, and verifies that the elements are unique +// according to `cmp`. +template > +constexpr void SortAndVerifyUnique(absl::Span span, + const Compare &cmp = {}) { + Sort(span, cmp); + for (int i = 0; i + i < span.size(); ++i) { + if (!cmp(span[i], span[i + 1]) && !cmp(span[i + 1], span[i])) { + DuplicateEntryFound(); + } + } +} + +} // namespace mozc::internal + +#endif // MOZC_BASE_CONTAINER_FLAT_INTERNAL_H_ diff --git a/src/base/container/flat_map.h b/src/base/container/flat_map.h new file mode 100644 index 000000000..1abc1ea91 --- /dev/null +++ b/src/base/container/flat_map.h @@ -0,0 +1,98 @@ +// Copyright 2010-2021, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef MOZC_BASE_CONTAINER_FLAT_MAP_H_ +#define MOZC_BASE_CONTAINER_FLAT_MAP_H_ + +#include + +#include +#include +#include +#include + +#include "absl/base/nullability.h" +#include "absl/types/span.h" +#include "base/container/entry.h" +#include "base/container/flat_internal.h" + +namespace mozc { + +// Read-only map that is backed by `constexpr`-sorted array. +template +class FlatMap { + public: + // Consider calling `CreateFlatMap` instead, so you don't have to manually + // specify the number of entries, `N`. + constexpr FlatMap(std::array, N> entries, + const CompareKey &cmp_key = {}) + : entries_(std::move(entries)), cmp_key_(cmp_key) { + internal::SortAndVerifyUnique( + absl::MakeSpan(entries_), + [&](const Entry &a, const Entry &b) { + return cmp_key_(a.key, b.key); + }); + } + + // Finds the value associated with the given key, or `nullptr` if not found. + constexpr absl::Nullable FindOrNull(const K &key) const { + auto lb = internal::FindFirst( + absl::MakeSpan(entries_), + [&](const Entry &e) { return !cmp_key_(e.key, key); }); + return lb == entries_.end() || cmp_key_(key, lb->key) ? nullptr + : &lb->value; + } + + private: + std::array, N> entries_; + CompareKey cmp_key_; +}; + +// Creates a `FlatMap` from a C-style array of `Entry`s. +// +// Example: +// +// constexpr auto kMap = CreateFlatMap({ +// {1, "one"}, +// {2, "two"}, +// {3, "three"}, +// }); +// +// Declare the variable as auto and use `CreateFlatMap`. The actual type is +// complex and explicitly declaring it would leak the number of entries, `N`. +template , size_t N> +constexpr auto CreateFlatMap(Entry (&&entries)[N], + const CompareKey &cmp_key = {}) { + return FlatMap(internal::ToArray(std::move(entries)), + cmp_key); +} + +} // namespace mozc + +#endif // MOZC_BASE_CONTAINER_FLAT_MAP_H_ diff --git a/src/base/container/flat_map_test.cc b/src/base/container/flat_map_test.cc new file mode 100644 index 000000000..a61daa1aa --- /dev/null +++ b/src/base/container/flat_map_test.cc @@ -0,0 +1,78 @@ +// Copyright 2010-2021, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "base/container/flat_map.h" + +#include + +#include "absl/strings/string_view.h" +#include "testing/gmock.h" +#include "testing/gunit.h" + +namespace mozc { +namespace { + +using ::testing::Eq; +using ::testing::IsNull; +using ::testing::Pointee; + +TEST(FlatMapTest, FindOrNull) { + constexpr auto kMap = CreateFlatMap({ + {1, "one"}, + {3, "three"}, + {5, "five"}, + }); + + EXPECT_THAT(kMap.FindOrNull(0), IsNull()); + EXPECT_THAT(kMap.FindOrNull(1), Pointee(Eq("one"))); + EXPECT_THAT(kMap.FindOrNull(2), IsNull()); + EXPECT_THAT(kMap.FindOrNull(3), Pointee(Eq("three"))); + EXPECT_THAT(kMap.FindOrNull(4), IsNull()); + EXPECT_THAT(kMap.FindOrNull(5), Pointee(Eq("five"))); + EXPECT_THAT(kMap.FindOrNull(6), IsNull()); +} + +TEST(FlatMapTest, CustomerCompare) { + constexpr auto kMap = CreateFlatMap>({ + {1, "one"}, + {3, "three"}, + {5, "five"}, + }); + + EXPECT_THAT(kMap.FindOrNull(0), IsNull()); + EXPECT_THAT(kMap.FindOrNull(1), Pointee(Eq("one"))); + EXPECT_THAT(kMap.FindOrNull(2), IsNull()); + EXPECT_THAT(kMap.FindOrNull(3), Pointee(Eq("three"))); + EXPECT_THAT(kMap.FindOrNull(4), IsNull()); + EXPECT_THAT(kMap.FindOrNull(5), Pointee(Eq("five"))); + EXPECT_THAT(kMap.FindOrNull(6), IsNull()); +} + +} // namespace +} // namespace mozc diff --git a/src/base/container/flat_multimap.h b/src/base/container/flat_multimap.h new file mode 100644 index 000000000..782048b33 --- /dev/null +++ b/src/base/container/flat_multimap.h @@ -0,0 +1,107 @@ +// Copyright 2010-2021, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef MOZC_BASE_CONTAINER_FLAT_MULTIMAP_H_ +#define MOZC_BASE_CONTAINER_FLAT_MULTIMAP_H_ + +#include + +#include +#include +#include +#include + +#include "absl/types/span.h" +#include "base/container/entry.h" +#include "base/container/flat_internal.h" + +namespace mozc { + +// Read-only multimap that is backed by `constexpr`-sorted array. +template +class FlatMultimap { + public: + // Consider calling `CreateFlatMultimap` instead, so you don't have to + // manually specify the number of entries, `N`. + constexpr FlatMultimap(std::array, N> entries, + const CompareKey &cmp_key = {}) + : entries_(std::move(entries)), cmp_key_(cmp_key) { + internal::Sort(absl::MakeSpan(entries_), + [&](const Entry &a, const Entry &b) { + return cmp_key_(a.key, b.key); + }); + } + + // Returns a span of entries with the given key. + // + // IMPORTANT: The order of the returned span is not guaranteed to be the + // same as the order of the entries given when the map was created. + // + // NOTE: The current implementation actually preserves the order, but this + // will change once we switch to `std::sort`, which is not stable (and + // `std::stable_sort` is not planned to receive constexpr support). + constexpr absl::Span> EqualSpan(const K &key) const { + auto lb = internal::FindFirst( + absl::MakeSpan(entries_), + [&](const Entry &e) { return !cmp_key_(e.key, key); }); + auto ub = internal::FindFirst( + absl::MakeSpan(entries_), + [&](const Entry &e) { return cmp_key_(key, e.key); }); + return absl::MakeConstSpan(lb, ub); + } + + private: + std::array, N> entries_; + CompareKey cmp_key_; +}; + +// Creates a `FlatMultimap` from a C-style array of `Entry`s. +// +// Example: +// +// constexpr auto kMultimap = CreateFlatMultimap({ +// {1, "one"}, +// {1, "ichi"}, +// {2, "two"}, +// {2, "ni"}, +// {3, "three"}, +// {3, "san"}, +// }); +// Declare the variable as auto and use `CreateFlatMultimap`. The actual type is +// complex and explicitly declaring it would leak the number of entries, `N`. +template , size_t N> +constexpr auto CreateFlatMultimap(Entry (&&entries)[N], + const CompareKey &cmp_key = {}) { + return FlatMultimap( + internal::ToArray(std::move(entries)), cmp_key); +} + +} // namespace mozc + +#endif // MOZC_BASE_CONTAINER_FLAT_MULTIMAP_H_ diff --git a/src/base/container/flat_multimap_test.cc b/src/base/container/flat_multimap_test.cc new file mode 100644 index 000000000..c7fa82b98 --- /dev/null +++ b/src/base/container/flat_multimap_test.cc @@ -0,0 +1,98 @@ +// Copyright 2010-2021, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "base/container/flat_multimap.h" + +#include + +#include "absl/strings/string_view.h" +#include "testing/gmock.h" +#include "testing/gunit.h" + +namespace mozc { +namespace { + +using ::testing::Eq; +using ::testing::FieldsAre; +using ::testing::IsEmpty; +using ::testing::UnorderedElementsAre; + +TEST(FlatMultimapTest, EqualSpan) { + constexpr auto kMultimap = CreateFlatMultimap({ + {1, "one"}, + {3, "three"}, + {5, "five"}, + {1, "ichi"}, + {3, "san"}, + {5, "go"}, + }); + + EXPECT_THAT(kMultimap.EqualSpan(0), IsEmpty()); + EXPECT_THAT(kMultimap.EqualSpan(1), + UnorderedElementsAre(FieldsAre(Eq(1), Eq("one")), + FieldsAre(Eq(1), Eq("ichi")))); + EXPECT_THAT(kMultimap.EqualSpan(2), IsEmpty()); + EXPECT_THAT(kMultimap.EqualSpan(3), + UnorderedElementsAre(FieldsAre(Eq(3), Eq("three")), + FieldsAre(Eq(3), Eq("san")))); + EXPECT_THAT(kMultimap.EqualSpan(4), IsEmpty()); + EXPECT_THAT(kMultimap.EqualSpan(5), + UnorderedElementsAre(FieldsAre(Eq(5), Eq("five")), + FieldsAre(Eq(5), Eq("go")))); + EXPECT_THAT(kMultimap.EqualSpan(6), IsEmpty()); +} + +TEST(FlatMultimapTest, CustomerCompare) { + constexpr auto kMultimap = + CreateFlatMultimap>({ + {1, "one"}, + {3, "three"}, + {5, "five"}, + {1, "ichi"}, + {3, "san"}, + {5, "go"}, + }); + + EXPECT_THAT(kMultimap.EqualSpan(0), IsEmpty()); + EXPECT_THAT(kMultimap.EqualSpan(1), + UnorderedElementsAre(FieldsAre(Eq(1), Eq("one")), + FieldsAre(Eq(1), Eq("ichi")))); + EXPECT_THAT(kMultimap.EqualSpan(2), IsEmpty()); + EXPECT_THAT(kMultimap.EqualSpan(3), + UnorderedElementsAre(FieldsAre(Eq(3), Eq("three")), + FieldsAre(Eq(3), Eq("san")))); + EXPECT_THAT(kMultimap.EqualSpan(4), IsEmpty()); + EXPECT_THAT(kMultimap.EqualSpan(5), + UnorderedElementsAre(FieldsAre(Eq(5), Eq("five")), + FieldsAre(Eq(5), Eq("go")))); + EXPECT_THAT(kMultimap.EqualSpan(6), IsEmpty()); +} + +} // namespace +} // namespace mozc diff --git a/src/base/container/flat_set.h b/src/base/container/flat_set.h new file mode 100644 index 000000000..0379584c1 --- /dev/null +++ b/src/base/container/flat_set.h @@ -0,0 +1,86 @@ +// Copyright 2010-2021, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef MOZC_BASE_CONTAINER_FLAT_SET_H_ +#define MOZC_BASE_CONTAINER_FLAT_SET_H_ + +#include + +#include +#include +#include +#include + +#include "absl/types/span.h" +#include "base/container/flat_internal.h" + +namespace mozc { + +// Read-only set that is backed by `constexpr`-sorted array. +template +class FlatSet { + public: + // Consider calling `CreateFlatSet` instead, so you don't have to manually + // specify the number of elements, `N`. + constexpr FlatSet(std::array elements, const Compare &cmp = {}) + : elements_(std::move(elements)), cmp_(cmp) { + internal::SortAndVerifyUnique(absl::MakeSpan(elements_), cmp_); + } + + // Returns if the given element is in the set. + constexpr bool contains(const T &x) const { + auto lb = internal::FindFirst(absl::MakeSpan(elements_), + [&](const T &e) { return !cmp_(e, x); }); + return lb != elements_.end() && !cmp_(x, *lb); + } + + private: + std::array elements_; + Compare cmp_; +}; + +// Creates a `FlatSet` from a C-style array of `Entry`s. +// +// Example: +// +// constexpr auto kSet = CreateFlatSet({ +// "one", +// "two", +// "three", +// }); +// Declare the variable as auto and use `CreateFlatSet`. The actual type is +// complex and explicitly declaring it would leak the number of elements, `N`. +template , size_t N> +constexpr auto CreateFlatSet(T (&&elements)[N], const Compare &cmp = {}) { + return FlatSet(internal::ToArray(std::move(elements)), cmp); +} + +} // namespace mozc + +#endif // MOZC_BASE_CONTAINER_FLAT_SET_H_ diff --git a/src/base/container/flat_set_test.cc b/src/base/container/flat_set_test.cc new file mode 100644 index 000000000..624b05a71 --- /dev/null +++ b/src/base/container/flat_set_test.cc @@ -0,0 +1,73 @@ +// Copyright 2010-2021, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "base/container/flat_set.h" + +#include + +#include "absl/strings/string_view.h" +#include "testing/gunit.h" + +namespace mozc { +namespace { + +TEST(FlatSetTest, Contains) { + constexpr auto kSet = CreateFlatSet({ + "one", + "three", + "five", + }); + + EXPECT_FALSE(kSet.contains("zero")); + EXPECT_TRUE(kSet.contains("one")); + EXPECT_FALSE(kSet.contains("two")); + EXPECT_TRUE(kSet.contains("three")); + EXPECT_FALSE(kSet.contains("four")); + EXPECT_TRUE(kSet.contains("five")); + EXPECT_FALSE(kSet.contains("six")); +} + +TEST(FlatSetTest, CustomerCompare) { + constexpr auto kSet = CreateFlatSet>({ + "one", + "three", + "five", + }); + + EXPECT_FALSE(kSet.contains("zero")); + EXPECT_TRUE(kSet.contains("one")); + EXPECT_FALSE(kSet.contains("two")); + EXPECT_TRUE(kSet.contains("three")); + EXPECT_FALSE(kSet.contains("four")); + EXPECT_TRUE(kSet.contains("five")); + EXPECT_FALSE(kSet.contains("six")); +} + +} // namespace +} // namespace mozc diff --git a/src/build_tools/progress_printer.py b/src/build_tools/progress_printer.py index 244f359e3..3b53e2c34 100644 --- a/src/build_tools/progress_printer.py +++ b/src/build_tools/progress_printer.py @@ -28,6 +28,34 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + """A console utility to show progress.""" import os