MySQL 9.6.0
Source Code Documentation
boundary_container.h
Go to the documentation of this file.
1// Copyright (c) 2024, 2025, Oracle and/or its affiliates.
2//
3// This program is free software; you can redistribute it and/or modify
4// it under the terms of the GNU General Public License, version 2.0,
5// as published by the Free Software Foundation.
6//
7// This program is designed to work with certain software (including
8// but not limited to OpenSSL) that is licensed under separate terms,
9// as designated in a particular file or component or in included license
10// documentation. The authors of MySQL hereby grant you an additional
11// permission to link the program and your derivative works with the
12// separately licensed software that they have either included with
13// the program or referenced in the documentation.
14//
15// This program is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License, version 2.0, for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with this program; if not, write to the Free Software
22// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23
24#ifndef MYSQL_SETS_THROWING_BOUNDARY_CONTAINER_H
25#define MYSQL_SETS_THROWING_BOUNDARY_CONTAINER_H
26
27/// @file
28/// Experimental API header
29
30#include <iterator> // prev
31#include "mysql/allocators/memory_resource.h" // Memory_resource
32#include "mysql/containers/basic_container_wrapper.h" // Basic_container_wrapper
33#include "mysql/iterators/meta.h" // Is_declared_legacy_bidirectional_iterator
34#include "mysql/meta/is_same_ignore_const.h" // Is_same_ignore_const
35#include "mysql/ranges/disjoint_pairs.h" // make_disjoint_pairs_view
36#include "mysql/sets/base_binary_operation_views.h" // make_binary_operation_view
37#include "mysql/sets/base_complement_view.h" // make_complement_view
38#include "mysql/sets/binary_operation.h" // Binary_operation
39#include "mysql/sets/boundary_set_binary_operation_view_base.h" // make_complement_view
40#include "mysql/sets/boundary_set_complement_view.h" // Complement_view<Boundary_set>
41#include "mysql/sets/boundary_set_interface.h" // Boundary_set_interface
42#include "mysql/sets/boundary_set_intersection_view.h" // Boundary_set_intersection_view
43#include "mysql/sets/boundary_set_meta.h" // Is_boundary_storage
44#include "mysql/sets/boundary_set_subtraction_view.h" // Subtraction_view
45#include "mysql/sets/boundary_set_union_view.h" // Union_view
46#include "mysql/sets/set_container_helpers.h" // handle_inplace_op_trivial_cases
47
48/// @addtogroup GroupLibsMysqlSets
49/// @{
50
51namespace mysql::sets::throwing {
52
53/// Container that stores boundaries.
54///
55/// This implements the Is_boundary_set concept, and additionally has
56/// functionality for in-place union, intersection, and subtraction, as well as
57/// copy from another Boundary_set and making this container empty.
58///
59/// All members provide basic exception safety guarantee, as defined at
60/// https://en.cppreference.com/w/cpp/language/exceptions#Exception_safety .
61/// Some additionally provide strong exception safety guarantee or nothrow
62/// exception guarantee: see documentation of each member for details.
63template <Is_boundary_storage Storage_tp>
65 Boundary_container<Storage_tp>, Storage_tp> {
66 public:
67 using Storage_t = Storage_tp;
69 static_assert(std::bidirectional_iterator<Iterator_t>);
70 static_assert(
73 using Set_traits_t = Storage_t::Set_traits_t;
74 using Element_t = Set_traits_t::Element_t;
76 using Allocator_t = typename Storage_t::Allocator_t;
79
80 static constexpr bool has_fast_insertion = Storage_t::has_fast_insertion;
81
82 /// Default constructor.
84
85 // Default rule-of-5.
88 Boundary_container &operator=(const Boundary_container &source) = default;
90 ~Boundary_container() = default;
91
92 /// Construct using the given Memory_resource.
93 explicit Boundary_container(const Memory_resource_t &memory_resource) noexcept
94 : Base_t(memory_resource) {}
95
96 /// Construct from any other Boundary_set over the same Set traits.
97 ///
98 /// @throws bad_alloc This may occur when the operation is half-completed,
99 /// which may leave the container as a subset of @c source.
102 const Memory_resource_t &memory_resource)
103 : Base_t(source.begin(), source.end(), memory_resource) {}
104
105 /// Construct from any other Boundary_set over the same Set traits.
106 ///
107 /// @throws bad_alloc This may occur when the operation is half-completed,
108 /// which may leave the container as a subset of @c source.
113
114 /// Construct from iterators.
115 ///
116 /// @throws bad_alloc This may occur when the operation is half-completed,
117 /// which may leave the container as a subset of @c source.
118 template <Is_boundary_iterator_over_type<Element_t> First_iterator_t>
120 const First_iterator_t &first,
121 const std::sentinel_for<First_iterator_t> auto &last,
122 const Memory_resource_t &memory_resource)
123 : Base_t(first, last, memory_resource) {}
124
125 /// Construct from iterators.
126 ///
127 /// @throws bad_alloc This may occur when the operation is half-completed,
128 /// which may leave the container as a subset of @c source.
129 template <Is_boundary_iterator_over_type<Element_t> First_iterator_t>
131 const First_iterator_t &first,
132 const std::sentinel_for<First_iterator_t> auto &last)
133 : Base_t(first, last) {}
134
135 /// Assign from any other Boundary_set over the same Set traits.
136 ///
137 /// @throws bad_alloc This may occur when the operation is half-completed,
138 /// which may leave the container as a subset of @c source.
141 this->assign(source);
142 return *this;
143 }
144
145 using Base_t::assign;
146
147 template <Is_boundary_set_over_traits_unqualified<Set_traits_t> Source_t>
149 // The requires clause ensures that Source_t is an rvalue reference.
150 // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
151 void assign(Source_t &&source) {
152 storage() = std::move(source.storage());
153 }
154
155 /// Return a const reference to the underlying storage.
156 [[nodiscard]] const Storage_t &storage() const noexcept {
157 return this->wrapped();
158 }
159
160 /// Return a const reference to the underlying storage.
161 [[nodiscard]] Storage_t &storage() noexcept { return this->wrapped(); }
162
163 /// Insert the given element (inplace union).
164 ///
165 /// This may insert a new one-element interval, extend an existing interval at
166 /// one end, merge two intervals that were separated by only the given
167 /// element, or, if the element was already in this container, do nothing.
168 ///
169 /// @param element The element to insert.
170 ///
171 /// @throws bad_alloc If an out-of-memory condition occurred while inserting
172 /// the interval. This leaves the container unmodified.
173 void insert(const Element_t &element) {
174 inplace_union(element, Set_traits_t::next(element));
175 }
176
177 /// Remove the given element (inplace subtraction).
178 ///
179 /// This may split an interval into two parts, shorten an existing interval in
180 /// one end, remove a one-element interval, or, if the element was not in this
181 /// container, do nothing.
182 ///
183 /// @param element The element to remove.
184 ///
185 /// @throws bad_alloc If an out-of-memory condition occurred while splitting
186 /// an interval. This leaves the container unmodified.
187 void remove(const Element_t &element) {
188 inplace_subtract(element, Set_traits_t::next(element));
189 }
190
191 private:
192 /// Whether a `hint` parameter is guaranteed to satisfy the precondition that
193 /// of being a lower bound.
194 // NOLINTNEXTLINE(performance-enum-size): silence clang-tidy's pointless hint
195 enum class Hint_guaranteed { no, yes };
196
197 public:
198 /// Insert the given interval (inplace union).
199 ///
200 /// This may merge intervals that overlap or are adjacent to the given
201 /// interval, or insert the interval between existing intervals, or, if the
202 /// interval was a subset of this container, do nothing.
203 ///
204 /// @param start The left boundary of the interval, inclusive.
205 ///
206 /// @param exclusive_end The right boundary of the interval, exclusive.
207 ///
208 /// @throws bad_alloc If an out-of-memory condition occurred while inserting
209 /// the interval. This leaves the container unmodified.
210 void inplace_union(const Element_t &start, const Element_t &exclusive_end) {
211 Iterator_t hint = this->begin();
212 inplace_union_or_subtract<Binary_operation::op_union, Hint_guaranteed::yes>(
213 hint, start, exclusive_end);
214 }
215
216 /// Insert the given interval (inplace union), reading and updating the given
217 /// cursor.
218 ///
219 /// This may merge intervals that overlap or are adjacent to the given
220 /// interval, or insert the interval between existing intervals, or, if the
221 /// interval was a subset of this container, do nothing.
222 ///
223 /// @param[in,out] cursor Hint for the insertion position. If it refers to
224 /// `lower_bound(start)`, this function finds the insertion position in O(1);
225 /// if it refers to a boundary less than `lower_bound(start)`, it may reduce
226 /// the search space when finding the insertion position; otherwise, the hint
227 /// is ignored and the entire set has to be searched. It will be updated to
228 /// `upper_bound(exclusive_end)`, which makes it perfect to reuse for future
229 /// calls to this function, with intervals following this one.
230 ///
231 /// @param start The left boundary of the interval, inclusive.
232 ///
233 /// @param exclusive_end The right boundary of the interval, exclusive.
234 ///
235 /// @throws bad_alloc If an out-of-memory condition occurred while inserting
236 /// the interval. This leaves the container unmodified.
238 const Element_t &exclusive_end) {
239 inplace_union_or_subtract<Binary_operation::op_union, Hint_guaranteed::no>(
240 cursor, start, exclusive_end);
241 }
242
243 /// In-place insert the intervals of the given set into this container
244 /// (inplace union).
245 ///
246 /// This may merge intervals that overlap or are adjacent to a given
247 /// interval, and/or insert intervals between existing intervals, or, if the
248 /// set was a subset of this container, do nothing.
249 ///
250 /// This uses one of two algorithms, depending on the nature of the underlying
251 /// container:
252 ///
253 /// - If the underlying container supports fast insertion in the middle (e.g.
254 /// set or list), then it uses a true in-place algorithm, possibly adjusting
255 /// endpoints of existing intervals, and reusing memory as much as possible.
256 ///
257 /// - Otherwise (e.g. sorted vector), it uses an out-of-place algorithm that
258 /// computes the result in a new container and then move-assigns the new
259 /// container to the current container.
260 ///
261 /// The complexity depends on the underlying container:
262 ///
263 /// - set: O(number_of_removed_intervals + input.size() * log(this->size()))
264 ///
265 /// - list: Normally, O(input.size() + this->size()); or O(input.size()) if
266 /// input_set.front() >= this->back().
267 ///
268 /// - vector: Normally, O(input.size() + this->size()); or O(input.size()) if
269 /// input_set.front() >= this->back().
270 ///
271 /// @param input_set The input set.
272 ///
273 /// @throws bad_alloc If an out-of-memory condition occurred while inserting
274 /// an interval. This may occur when the operation is half-completed, which
275 /// may leave the container as a superset of the previous set and a subset of
276 /// the union.
277 template <Is_boundary_set_over_traits_unqualified<Set_traits_t> Input_set_t>
278 void inplace_union(Input_set_t &&input_set) {
279 inplace_op<Binary_operation::op_union>(
280 std::forward<Input_set_t>(input_set));
281 }
282
283 /// Subtract the given interval.
284 ///
285 /// This may split an interval into two parts, shorten an existing interval in
286 /// one end, remove a one-element interval, or, if the element was not in this
287 /// container, do nothing.
288 ///
289 /// This may truncate and/or split intervals that overlap partially with
290 /// the subtracted interval, and remove intervals that overlap completely with
291 /// the subtracted interval.
292 ///
293 /// @param start The left boundary of the interval, inclusive.
294 ///
295 /// @param exclusive_end The right boundary of the interval, exclusive.
296 ///
297 /// @throws bad_alloc If an out-of-memory condition occurred while splitting
298 /// an interval. This leaves the container unmodified.
300 const Element_t &exclusive_end) {
301 Iterator_t hint = this->begin();
303 Hint_guaranteed::yes>(hint, start, exclusive_end);
304 }
305
306 /// Subtract the given interval, reading and updating the given cursor.
307 ///
308 /// This may truncate and/or split intervals that overlap partially with the
309 /// subtracted interval, and remove intervals that overlap completely with the
310 /// subtracted interval.
311 ///
312 /// @param[in,out] cursor Hint for the insertion position. If it refers to
313 /// `lower_bound(start)`, this function finds the insertion position in O(1);
314 /// if it refers to a boundary less than `lower_bound(start)`, it may reduce
315 /// the search space when finding the insertion position; otherwise, the hint
316 /// is ignored and the entire set has to be searched. It will be updated to
317 /// `upper_bound(exclusive_end)`, which makes it good to reuse for future
318 /// calls to this function, with intervals following this one.
319 ///
320 /// @param start The left boundary of the interval, inclusive.
321 ///
322 /// @param exclusive_end The right boundary of the interval, exclusive.
323 ///
324 /// @throws bad_alloc If an out-of-memory condition occurred while splitting
325 /// an interval. This leaves the container unmodified.
327 const Element_t &exclusive_end) {
329 Hint_guaranteed::no>(cursor, start,
330 exclusive_end);
331 }
332
333 /// In-place subtract intervals of the given container from this
334 /// container.
335 ///
336 /// This may truncate and/or split intervals that overlap partially with
337 /// subtracted intervals, and remove intervals that overlap completely with
338 /// subtracted intervals.
339 ///
340 /// Algorithm and complexity: @see inplace_union.
341 ///
342 /// @param input_set The input set.
343 ///
344 /// @throws bad_alloc If an out-of-memory condition occurred while splitting
345 /// an interval. This may occur when the operation is half-completed,
346 /// which may leave the container as a subset of the previous set and a
347 /// superset of the difference.
348 template <Is_boundary_set_over_traits_unqualified<Set_traits_t> Input_set_t>
349 void inplace_subtract(Input_set_t &&input_set) {
350 inplace_op<Binary_operation::op_subtraction>(
351 std::forward<Input_set_t>(input_set));
352 }
353
354 /// In-place intersect this container with the given interval.
355 ///
356 /// This may truncate intervals that overlap partially with the given
357 /// interval, and remove intervals that are disjoint from the given interval.
358 ///
359 /// @param start The left boundary of the interval, inclusive.
360 ///
361 /// @param exclusive_end The right boundary of the interval, exclusive.
362 ///
363 /// Since this cannot increase the number of intervals, it never throws.
365 const Element_t &exclusive_end) noexcept {
366 if (Set_traits_t::lt(exclusive_end, Set_traits_t::max_exclusive()))
367 inplace_subtract(exclusive_end, Set_traits_t::max_exclusive());
368 if (Set_traits_t::gt(start, Set_traits_t::min()))
369 inplace_subtract(Set_traits_t::min(), start);
370 }
371
372 /// In-place intersect this container with intervals of the given container.
373 ///
374 /// This may truncate intervals that overlap partially with one interval from
375 /// the given set, split intervals that overlap partially with more
376 /// than one interval from the given set, and remove intervals that are
377 /// disjoint from the given interval set.
378 ///
379 /// Algorithm and complexity: @see inplace_union.
380 ///
381 /// @param input_set The input set.
382 ///
383 /// @throws bad_alloc If an out-of-memory condition occurred while splitting
384 /// an interval. This may occur when the operation is half-completed, which
385 /// may leave the container as a subset of the previous set and a superset of
386 /// the intersection.
387 template <Is_boundary_set_over_traits_unqualified<Set_traits_t> Input_set_t>
388 void inplace_intersect(Input_set_t &&input_set) {
389 inplace_op<Binary_operation::op_intersection>(
390 std::forward<Input_set_t>(input_set));
391 }
392
393 /// Return iterator to the leftmost boundary at or after `hint` that is
394 /// greater than the given element.
395 ///
396 /// Complexity:
397 /// - O(1) if the return element is end() or `hint`;
398 /// - the complexity of Storage_t::upper_bound, otherwise.
399 ///
400 /// (We override the Boundary_set_interface member because this is
401 /// faster.)
402 [[nodiscard]] static auto upper_bound_impl(
403 mysql::meta::Is_same_ignore_const<This_t> auto &self, const auto &hint,
404 const Element_t &element) noexcept {
405 return Storage_t::upper_bound_dispatch(self.storage(), hint, element);
406 }
407
408 /// Return iterator to the leftmost boundary at or after `hint` that is
409 /// greater than or equal to the given element.
410 ///
411 /// Complexity:
412 /// - O(1) if the return element is end() or `hint`;
413 /// - the complexity of Storage_t::upper_bound, otherwise.
414 ///
415 /// (We override the Boundary_set_interface member because this is
416 /// faster.)
417 [[nodiscard]] static auto lower_bound_impl(
418 mysql::meta::Is_same_ignore_const<This_t> auto &self, const auto &hint,
419 const Element_t &element) noexcept {
420 return Storage_t::lower_bound_dispatch(self.storage(), hint, element);
421 }
422
423 private:
424 /// Indicates to a function that it may steal elements from a source object,
425 /// rather than allocate new elements.
426 // NOLINTNEXTLINE(performance-enum-size): silence clang-tidy's pointless hint
427 enum class Can_donate { no, yes };
428
429 /// Worker for either in-place union or in-place subtraction, adding or
430 /// removing all intervals from another Boundary_set.
431 ///
432 /// Algorithm and complexity: @see inplace_union.
433 ///
434 /// @tparam operation The set operation to compute.
435 ///
436 /// @param source The container whose elements will be added or removed in
437 /// this container.
438 ///
439 /// @throws bad_alloc This may occur when the operation is half-completed, so
440 /// that a subset of `source` has been added or removed from this set.
441 template <Binary_operation operation,
443 void inplace_op(Source_t &&source) {
444 if (mysql::sets::detail::handle_inplace_op_trivial_cases<operation>(
445 *this, std::forward<Source_t>(source)))
446 return;
447
448 // For containers with random access iterators, insertion/deletion in the
449 // middle is typically worst-case linear in the container size. So a
450 // sequence of N insertions/deletions may be O(N*size). This is worst-case
451 // quadratic, when both N and size are linear in this->size() +
452 // source.size(). To avoid performance bottlenecks, we fall back to a
453 // full-copy from a Union_view or Intersection_view, which is guaranteed to
454 // be linear.
455 //
456 // Some cases are not really quadratic: An upper bound on the number of
457 // insertions/deletions during which any intervals are moved, is the number
458 // of intervals of source that are to the left of this->end(). And an upper
459 // bound on the number of intervals that are moved each time, is the number
460 // of intervals of this that are to the right of source.begin(). So, as long
461 // as the product of those two numbers is not greater than this->size(),
462 // in-place is probably faster.
463 auto &self = *this;
464 auto prefer_full_copy = [&self, &source] {
465 // If the storage allows fast insertion, we never prefer to make a full
466 // copy.
467 if (has_fast_insertion) return false;
468 // Iterator to next element in 'source' after the last one that will be
469 // inserted before any element in 'self'.
470 auto insert_end = source.upper_bound(self.back());
471 // Maximum number of elements in 'source' that may be inserted in 'self'.
472 auto max_elements_inserted = std::distance(source.begin(), insert_end);
473 // First element of 'self' that may be moved during an insertion of an
474 // element from 'source'.
475 auto first_element_moved = self.upper_bound(source.front());
476 // The maximum number of elements of 'self' that may be moved during an
477 // insertion of an element of 'source'.
478 auto max_elements_moved_per_insertion =
479 std::distance(first_element_moved, self.end());
480 // If both 'self' and 'source' have their elements uniformly distributed
481 // between 'first_element_moved' and 'self.end()', then it is expected
482 // that fewer and fewer elements will be moved in each iteration; the
483 // number of elements to move decreases linearly so the expected average
484 // number of elements to move is only half the maximum.
485 auto expected_elements_moved_per_insertion =
486 max_elements_moved_per_insertion >> 1;
487 // If the worst-case expected number of moved elements exceeds the size
488 // of the container, we prefer to make a full copy.
489 return max_elements_inserted * expected_elements_moved_per_insertion >
490 self.ssize();
491 };
492
493 if (prefer_full_copy()) {
494 // Todo: This is sometimes faster than the in-place operation, even on
495 // containers with has_fast_insertion==true. See intervals_perf-t.cc. The
496 // reason is that the for loop below always iterates over the full
497 // `source` set, whereas Union_view is able to take long steps. This could
498 // be optimized similar to Union_view, i.e., `inplace_union_or_subtract`
499 // could advance `it` to the upper bound
500 This_t container(make_binary_operation_view<operation>(*this, source));
501 this->assign(std::move(container));
502 } else {
503 // If the operation is 'intersection', subtract the complement instead.
504 // (It's always true that "A intersect B" == "A - complement(B)".)
505 // Therefore the type of `read_source` needs to be either
506 // `Complement_view<Source_t> &` or `Source_t &`; we use a lambda with
507 // deduced return type to declare the type of `read_source` conditionally.
508 [[maybe_unused]] auto complement = make_complement_view(source);
509 auto &read_source = [&]() -> auto & {
510 if constexpr (operation == Binary_operation::op_intersection) {
511 return complement;
512 } else {
513 return source;
514 }
515 }
516 ();
517 constexpr Binary_operation impl_operation =
520 : operation);
521 constexpr bool types_allow_donation =
523 bool can_donate = false;
524 if constexpr (types_allow_donation) {
525 can_donate = (this->get_allocator() == source.get_allocator());
526 }
527
528 // Process one interval at a time from source. We don't use `range for`
529 // because inplace_union_or_subtract may steal the current element, so we
530 // must increment the iterator before invoking the function.
531 auto cursor = this->begin();
532 auto interval_set_view = Interval_set_view(read_source);
533 auto it = interval_set_view.begin();
534 while (it != interval_set_view.end()) {
535 auto iv = *it;
536 ++it;
537 if (can_donate) {
538 // If new elements are needed, donate from source.
539 // Guard by `if constexpr (types_allow_donation)` since the call with
540 // `Can_donate::yes` may not compile otherwise.
541 if constexpr (types_allow_donation) {
544 cursor, iv.start(), iv.exclusive_end(), &source);
545 }
546 } else {
547 // If new elements are needed, allocate.
550 cursor, iv.start(), iv.exclusive_end(), &source);
551 }
552
553 if constexpr (operation != Binary_operation::op_union) {
554 // As an optimization, if we reach the end of this container while
555 // removing intervals, just return.
556 if (cursor == this->end()) return;
557 }
558 }
559 }
560 }
561
562 /// Either add or remove the interval, reading and updating the given cursor.
563 ///
564 /// Complexity:
565 ///
566 /// - list: O(number_of_removed_intervals + std::distance(cursor,
567 /// first_removed_interval).
568 ///
569 /// - set: O(1) if it doesn't change the number of intervals; O(log(size()) +
570 /// number_of_removed_intervals) otherwise.
571 ///
572 /// - Ordered_vector: constant if it doesn't change the number of intervals;
573 /// linear in the distance from the first removed element to end() otherwise
574 ///
575 /// @tparam operation The operation: op_union or op_subtraction
576 ///
577 /// @tparam hint_guaranteed If yes, raise an assertion if @c hint does not
578 /// satisfy the requirements. If no, ignore @c hint if it does not satisfy the
579 /// requirements. See below.
580 ///
581 /// @tparam can_donate If no, allocate new elements when needed; if yes, steal
582 /// elements from `source` when needed.
583 ///
584 /// @param[in,out] cursor Hint for the insertion position. If it refers to
585 /// `upper_bound(start)`, this function finds the insertion position in O(1).
586 /// If it refers to a boundary less than `upper_bound(start)`, it may reduce
587 /// the search space when finding the insertion position. If it refers to a
588 /// boundary greater than `upper_bound(start)`, the behavior depends on @c
589 /// hint_guaranteed : If @c hint_guaranteed is yes, the behavior is undefined
590 /// (an assertion is raised in debug mode); otherwise, the hint is ignored and
591 /// the entire set has to be searched. @c cursor will be updated to
592 /// `upper_bound(exclusive_end)`, which makes it good to reuse in future calls
593 /// to this function, with intervals following this one.
594 ///
595 /// @param start The left boundary of the interval, inclusive.
596 ///
597 /// @param exclusive_end The right boundary of the interval, exclusive.
598 ///
599 /// @param source Container from which elements may be stolen.
600 ///
601 /// @throws bad_alloc This leaves the container unmodified.
602 template <
603 Binary_operation operation, Hint_guaranteed hint_guaranteed,
604 Can_donate can_donate = Can_donate::no,
607 const Element_t &exclusive_end,
608 Source_t *source = nullptr) {
609 assert(Set_traits_t::lt(start, exclusive_end));
610 if constexpr (hint_guaranteed == Hint_guaranteed::yes) {
611 assert(cursor == this->begin() ||
612 Set_traits_t::lt(*std::ranges::prev(cursor), start));
613 } else {
614 if (cursor != this->begin() &&
615 Set_traits_t::ge(*std::ranges::prev(cursor), start)) {
616 cursor = this->begin();
617 }
618 }
619 Iterator_t left = this->lower_bound(cursor, start);
620 Iterator_t right = this->upper_bound(left, exclusive_end);
621 constexpr bool is_union = operation == Binary_operation::op_union;
622 // The following comments are written for the case operation==op_union. They
623 // apply analogously for the case operation==op_subtraction, if we
624 // interchange the words "interval" and "gap", and keep in mind that the
625 // interval is to be erased.
626 //
627 // The diagrams illustrate each case, using [ for the start points left and
628 // start, and ] for the endpoints right/exclusive_end. Question marks
629 // indicate a range where an unknown number of boundaries occur; an odd
630 // number of question marks represents any odd number of boundaries and an
631 // even number of question marks represents any even number of boundraies.
632 // In all cases, `right` is strictly greater than `exclusive_end`, and
633 // `left` is greater than or equal to `start`.
634 if (left.is_endpoint() == is_union) {
635 // The beginning of the interval touches or intersects an interval that
636 // extends to the left of it, which ends at `left`. So `left` must be
637 // removed and `start` must not occur in the resulting set.
638 if (right.is_endpoint() == is_union) {
639 // The end of the interval touches or intersects an interval that
640 // extends to the right of it, which ends at `right`. So `exclusive_end`
641 // must not occur in the resulting set, and boundaries from `left`
642 // (inclusive), to `right` (exclusive), must be removed.
643 // new boundaries (start, exclusive_end): [ ]
644 // existing boundaries (left, right): ] ? ? ? ]
645 // result: ]
646 cursor = storage().erase(left, right);
647 } else {
648 // The end of the interval does not touch or intersect any interval that
649 // extends to the right of it. So `exclusive_end` will be an endpoint in
650 // the output, and `right` is preserved. We overwrite `left` and erase
651 // any boundaries covered by the interval.
652 // new boundaries (start, exclusive_end): [ ]
653 // existing boundaries (left, right): ] ? ? ? ? [
654 // result: ] [
655 cursor = storage().erase(std::next(left), right);
656 cursor =
657 storage().update_point(std::ranges::prev(cursor), exclusive_end);
658 }
659 } else {
660 // The beginning of the interval does not touch or intersect any interval
661 // that extends to the left of it (but may touch the beginning of an
662 // interval). So `start` must be a boundary in the resulting set.
663 if (right.is_endpoint() == is_union) {
664 // The end of the interval touches or intersects the beginning of a
665 // following interval, which ends at `right`. So `exclusive_end` will
666 // not occur in the resulting set. Boundaries between `left` (exclusive)
667 // and `right` (exclusive) are removed, and `left` is replaced by
668 // `start`.
669 // new boundaries (start, exclusive_end): [ ]
670 // existing boundaries (left, right): [ ? ? ? ? ]
671 // result: [ ]
672 cursor = storage().erase(std::next(left), right);
673 cursor = storage().update_point(std::ranges::prev(cursor), start);
674 } else {
675 // The end of the interval does not touch or intersect any interval that
676 // extends to the right of it. So `exclusive_end` must be a boundary in
677 // the output.
678 if (left != right) {
679 // There are intervals between `left` and `right`. So they need to be
680 // removed. Reuse one of them to store the interval.
681 // new boundaries (start, exclusive_end): [ ]
682 // existing boundaries (left, right): [ ? ? ? [
683 // result: [ ] [
684 cursor = storage().erase(std::next(left, 2), right);
685 cursor = storage().update_point(std::ranges::prev(cursor, 2), start);
686 cursor = storage().update_point(cursor, exclusive_end);
687 } else {
688 // There are no intervals between `left` and `last`. In other words,
689 // the interval is fully contained in a gap. So we insert it there.
690 // new boundaries (start, exclusive_end): [ ]
691 // existing boundaries (left==right): [
692 // result: [ ] [
693 if constexpr (can_donate == Can_donate::yes) {
694 // Steal, don't allocate
695 cursor = storage().steal_and_insert(left, start, exclusive_end,
696 source->storage());
697 } else {
698 // Allocate, don't steal
699 cursor = storage().insert(left, start, exclusive_end);
700 }
701 }
702 }
703 }
704 }
705}; // class Boundary_container
706
707} // namespace mysql::sets::throwing
708
709// addtogroup GroupLibsMysqlSets
710/// @}
711
712#endif // ifndef MYSQL_SETS_THROWING_BOUNDARY_CONTAINER_H
Experimental API header.
Experimental API header.
Experimental API header.
Experimental API header.
Experimental API header.
Experimental API header.
Experimental API header.
Experimental API header.
Experimental API header.
Experimental API header.
Polymorphism-free memory resource class with custom allocator and deallocator functions.
Definition: memory_resource.h:88
CRTP base class (mixin) to define a wrapper around a container.
Definition: basic_container_wrapper.h:59
auto get_allocator() const noexcept
Return the allocator used by the wrapped object.
Definition: basic_container_wrapper.h:159
constexpr decltype(auto) back() const
Return the last element. Enabled if we have bidirectional iterators.
Definition: collection_interface.h:161
CRTP base class/mixin, used to implement Boundary Sets that are container wrappers.
Definition: boundary_set_interface.h:128
Definition: basic_set_container_wrapper.h:47
void assign(Source_t &&source)
Enable move-assign from any Basic_set_container_wrapper for a compatible set type (not necessarily fo...
Definition: basic_set_container_wrapper.h:67
View that provides and Interval set from an underlying Boundary set.
Definition: interval_set_interface.h:132
constexpr Iterator_t lower_bound(const Iterator_t &hint, const Element_t &element)
Return the lower bound for element, using an iterator hint known to be less than or equal to the corr...
Definition: upper_lower_bound_interface.h:219
constexpr Iterator_t upper_bound(const Iterator_t &hint, const Element_t &element)
Return the upper bound for element, using an iterator hint known to be less than or equal to the corr...
Definition: upper_lower_bound_interface.h:284
Iterator_tp Iterator_t
Definition: boundary_set_interface.h:75
Set_traits_tp Set_traits_t
Definition: boundary_set_interface.h:78
typename Set_traits_tp::Element_t Element_t
Definition: boundary_set_interface.h:79
Const_iterator_tp Const_iterator_t
Definition: boundary_set_interface.h:76
Container that stores boundaries.
Definition: boundary_container.h:65
void inplace_union(const Element_t &start, const Element_t &exclusive_end)
Insert the given interval (inplace union).
Definition: boundary_container.h:210
Boundary_container(const Is_boundary_set_over_traits< Set_traits_t > auto &source)
Construct from any other Boundary_set over the same Set traits.
Definition: boundary_container.h:109
void inplace_op(Source_t &&source)
Worker for either in-place union or in-place subtraction, adding or removing all intervals from anoth...
Definition: boundary_container.h:443
Boundary_container(const Is_boundary_set_over_traits< Set_traits_t > auto &source, const Memory_resource_t &memory_resource)
Construct from any other Boundary_set over the same Set traits.
Definition: boundary_container.h:100
typename Storage_t::Allocator_t Allocator_t
Definition: boundary_container.h:76
void inplace_intersect(const Element_t &start, const Element_t &exclusive_end) noexcept
In-place intersect this container with the given interval.
Definition: boundary_container.h:364
Storage_t & storage() noexcept
Return a const reference to the underlying storage.
Definition: boundary_container.h:161
void inplace_subtract(Input_set_t &&input_set)
In-place subtract intervals of the given container from this container.
Definition: boundary_container.h:349
static auto lower_bound_impl(mysql::meta::Is_same_ignore_const< This_t > auto &self, const auto &hint, const Element_t &element) noexcept
Return iterator to the leftmost boundary at or after hint that is greater than or equal to the given ...
Definition: boundary_container.h:417
Boundary_container() noexcept=default
Default constructor.
static auto upper_bound_impl(mysql::meta::Is_same_ignore_const< This_t > auto &self, const auto &hint, const Element_t &element) noexcept
Return iterator to the leftmost boundary at or after hint that is greater than the given element.
Definition: boundary_container.h:402
void inplace_subtract(Iterator_t &cursor, const Element_t &start, const Element_t &exclusive_end)
Subtract the given interval, reading and updating the given cursor.
Definition: boundary_container.h:326
Storage_tp Storage_t
Definition: boundary_container.h:67
Can_donate
Indicates to a function that it may steal elements from a source object, rather than allocate new ele...
Definition: boundary_container.h:427
void remove(const Element_t &element)
Remove the given element (inplace subtraction).
Definition: boundary_container.h:187
Boundary_container< Storage_t > This_t
Definition: boundary_container.h:77
void inplace_union(Input_set_t &&input_set)
In-place insert the intervals of the given set into this container (inplace union).
Definition: boundary_container.h:278
void insert(const Element_t &element)
Insert the given element (inplace union).
Definition: boundary_container.h:173
Hint_guaranteed
Whether a hint parameter is guaranteed to satisfy the precondition that of being a lower bound.
Definition: boundary_container.h:195
void inplace_subtract(const Element_t &start, const Element_t &exclusive_end)
Subtract the given interval.
Definition: boundary_container.h:299
void inplace_intersect(Input_set_t &&input_set)
In-place intersect this container with intervals of the given container.
Definition: boundary_container.h:388
const Storage_t & storage() const noexcept
Return a const reference to the underlying storage.
Definition: boundary_container.h:156
void assign(Source_t &&source)
Definition: boundary_container.h:151
static constexpr bool has_fast_insertion
Definition: boundary_container.h:80
This_t & operator=(const Is_boundary_set_over_traits< Set_traits_t > auto &source)
Assign from any other Boundary_set over the same Set traits.
Definition: boundary_container.h:139
Boundary_container(const First_iterator_t &first, const std::sentinel_for< First_iterator_t > auto &last)
Construct from iterators.
Definition: boundary_container.h:130
void inplace_union_or_subtract(Iterator_t &cursor, const Element_t &start, const Element_t &exclusive_end, Source_t *source=nullptr)
Either add or remove the interval, reading and updating the given cursor.
Definition: boundary_container.h:606
Boundary_container(const First_iterator_t &first, const std::sentinel_for< First_iterator_t > auto &last, const Memory_resource_t &memory_resource)
Construct from iterators.
Definition: boundary_container.h:119
void inplace_union(Iterator_t &cursor, const Element_t &start, const Element_t &exclusive_end)
Insert the given interval (inplace union), reading and updating the given cursor.
Definition: boundary_container.h:237
True if the iterator is declared to meet LegacyBidirectionalIterator requirements.
Definition: meta.h:83
true if Type1 and Type2 are equal, const-ness ignored
Definition: is_same_ignore_const.h:39
True if move-semantics has been declared as enabled for inplace_union/inplace_intersect/inplace_subtr...
Definition: meta.h:114
True if move-semantics has been declared as enabled for full-set-copy operations for the given operan...
Definition: meta.h:90
Definition: boundary_set_meta.h:239
True if Test is an interval set over the given Set traits.
Definition: boundary_set_meta.h:217
Experimental API header.
static void start(mysql_harness::PluginFuncEnv *env)
Definition: http_auth_backend_plugin.cc:180
Experimental API header.
Experimental API header.
Class that wraps resources in a polymorphic manner.
Definition: atomics_array.h:39
bool distance(const dd::Spatial_reference_system *srs, const Geometry *g1, const Geometry *g2, double *distance, bool *is_null) noexcept
Computes the distance between two geometries.
Definition: distance.cc:40
void right(std::string *to_trim)
Definition: trim.h:41
void left(std::string *to_trim)
Definition: trim.h:35
decltype(auto) get_memory_resource_or_default(const auto &object)
Return object.get_memory_resource(), if that function exists.
Definition: memory_resource.h:145
std::remove_cvref_t< decltype(std::declval< const Range_t >().begin())> Range_const_iterator_type
Gives the const_iterator type, deduced from the begin() const member.
Definition: meta.h:47
std::remove_cvref_t< decltype(std::declval< Range_t >().begin())> Range_iterator_type
Gives the iterator type, deduced from the begin() member.
Definition: meta.h:42
Definition: aliases.h:73
Binary_operation
Identifies the type of a binary operation.
Definition: binary_operation.h:37
auto make_complement_view(const Source_t &source)
Return the Complement_view over the argument.
Definition: base_complement_view.h:53
noexcept
The return type for any call_and_catch(f, args...) call where f(args...) returns Type.
Definition: call_and_catch.h:76
Definition: instrumented_condition_variable.h:32
repeated Source source
Definition: replication_asynchronous_connection_failover.proto:42
Experimental API header.