BDE 4.39.x Production Release
Loading...
Searching...
No Matches
ball_categorymanager_radixtree.h
Go to the documentation of this file.
1/// @file ball_categorymanager_radixtree.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// ball_categorymanager_radixtree.h -*-C++-*-
8#ifndef INCLUDED_BALL_CATEGORYMANAGER_RADIXTREE
9#define INCLUDED_BALL_CATEGORYMANAGER_RADIXTREE
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup ball_categorymanager_radixtree ball_categorymanager_radixtree
15/// @brief Provide a space-efficient associative container for string keys.
16/// @addtogroup bal
17/// @{
18/// @addtogroup ball
19/// @{
20/// @addtogroup ball_categorymanager_radixtree
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#ball_categorymanager_radixtree-purpose"> Purpose</a>
25/// * <a href="#ball_categorymanager_radixtree-classes"> Classes </a>
26/// * <a href="#ball_categorymanager_radixtree-description"> Description </a>
27/// * <a href="#ball_categorymanager_radixtree-features"> Features </a>
28/// * <a href="#ball_categorymanager_radixtree-usage"> Usage </a>
29/// * <a href="#ball_categorymanager_radixtree-example-1-basic-usage"> Example 1: Basic Usage </a>
30///
31/// # Purpose {#ball_categorymanager_radixtree-purpose}
32/// Provide a space-efficient associative container for string keys.
33///
34/// # Classes {#ball_categorymanager_radixtree-classes}
35///
36/// - ball::CategoryManager_RadixTree: space-efficient string-key assoc container
37///
38/// # Description {#ball_categorymanager_radixtree-description}
39/// This component implements `ball::CategoryManager_RadixTree`, a
40/// space-efficient associative container that stores key-value pairs where keys
41/// are strings (or string-like types). A radix tree (also known as a
42/// compressed trie or prefix tree) achieves space efficiency by sharing common
43/// prefixes among keys, making it particularly suitable for storing large sets
44/// of strings with common prefixes.
45///
46/// ## Features {#ball_categorymanager_radixtree-features}
47///
48///
49/// The `ball::CategoryManager_RadixTree` provides the following features:
50///
51/// * Space-efficient storage of string keys with common prefixes
52/// * O(k) insertion, lookup, and removal where k is the key length
53/// * Support for custom value types
54/// * Visitor pattern for traversing all key-value pairs
55/// * Full allocator support for value-semantic behavior
56///
57/// ## Usage {#ball_categorymanager_radixtree-usage}
58///
59///
60/// This section illustrates intended use of this component.
61///
62/// ### Example 1: Basic Usage {#ball_categorymanager_radixtree-example-1-basic-usage}
63///
64///
65/// Suppose we want to store a mapping of words to their definitions. Using
66/// `ball::CategoryManager_RadixTree` allows us to efficiently store many words
67/// that share common prefixes.
68///
69/// First, we create a radix tree:
70/// @code
71/// ball::CategoryManager_RadixTree<bsl::string> dictionary;
72/// @endcode
73/// Then, we insert some words and their definitions:
74/// @code
75/// dictionary.emplace("car", "a road vehicle with four wheels");
76/// dictionary.emplace("card", "a piece of stiff paper");
77/// dictionary.emplace("care", "the provision of what is needed");
78/// dictionary.emplace("cat", "a small domesticated carnivorous mammal");
79/// @endcode
80/// Next, we can look up definitions:
81/// @code
82/// bsl::optional<bsl::reference_wrapper<bsl::string> > definition =
83/// dictionary.find("car");
84/// assert(definition.has_value());
85/// assert(definition->get() == "a road vehicle with four wheels");
86/// @endcode
87/// We can also check if a key exists:
88/// @code
89/// assert(dictionary.contains("card"));
90/// assert(!dictionary.contains("dog"));
91/// @endcode
92/// We can find the longest prefix of a key that exists in the tree:
93/// @code
94/// ball::CategoryManager_RadixTree<bsl::string>::OptValueRef optValue;
95/// bsl::string_view prefix = dictionary.findLongestCommonPrefix(&optValue,
96/// "cards");
97/// assert(prefix == "card");
98/// assert(optValue.has_value() && optValue->get() == "a piece of stiff paper");
99/// prefix = dictionary.findLongestCommonPrefix(&optValue, "carpet");
100/// assert(prefix == "car");
101/// assert(optValue.has_value()
102/// && optValue->get() == "a road vehicle with four wheels");
103/// prefix = dictionary.findLongestCommonPrefix(&optValue, "dog");
104/// assert(prefix == "");
105/// assert(!optValue.has_value());
106/// @endcode
107/// We can run a functor for all entries:
108/// @code
109/// struct Printer {
110/// void operator()(const bsl::string_view& key,
111/// const bsl::string& value) const {
112/// bsl::cout << key << ": " << value << bsl::endl;
113/// }
114/// };
115/// dictionary.forEach(Printer());
116/// @endcode
117/// We can run a functor for all entries with a given prefix:
118/// @code
119/// struct PrefixCollector {
120/// bsl::vector<bsl::string> *d_vec_p;
121/// PrefixCollector(bsl::vector<bsl::string> *vec) : d_vec_p(vec) {}
122/// void operator()(const bsl::string_view& key,
123/// const bsl::string& value) const {
124/// d_vec_p->push_back(bsl::string(key));
125/// }
126/// };
127/// bsl::vector<bsl::string> found;
128/// PrefixCollector collector(&found);
129/// dictionary.forEachPrefix("car", collector);
130/// assert(found.size() == 3);
131/// bool foundCar = false;
132/// bool foundCard = false;
133/// bool foundCare = false;
134/// for (bsl::size_t i = 0; i < found.size(); ++i) {
135/// if (found[i] == "car") {
136/// foundCar = true;
137/// } else if (found[i] == "card") {
138/// foundCard = true;
139/// } else if (found[i] == "care") {
140/// foundCare = true;
141/// }
142/// }
143/// assert(foundCar);
144/// assert(foundCard);
145/// assert(foundCare);
146/// @endcode
147/// We can also mutate values for all entries with a given prefix:
148/// @code
149/// struct SuffixAppender {
150/// void operator()(const bsl::string_view&, bsl::string& value) const {
151/// value += "!";
152/// }
153/// };
154/// dictionary.forEachPrefix("car", SuffixAppender());
155/// assert(dictionary.find("car")->get() ==
156/// "a road vehicle with four wheels!");
157/// assert(dictionary.find("card")->get() == "a piece of stiff paper!");
158/// assert(dictionary.find("care")->get() ==
159/// "the provision of what is needed!");
160/// @endcode
161/// Finally, we can remove entries:
162/// @code
163/// dictionary.erase("card"); assert(!dictionary.contains("card"));
164/// @endcode
165/// @}
166/** @} */
167/** @} */
168
169/** @addtogroup bal
170 * @{
171 */
172/** @addtogroup ball
173 * @{
174 */
175/** @addtogroup ball_categorymanager_radixtree
176 * @{
177 */
178
179#include <balscm_version.h>
180
182#include <bslalg_swaputil.h>
183
184#include <bslma_allocator.h>
185#include <bslma_bslallocator.h>
186
187#include <bslmf_movableref.h>
188
189#include <bsls_assert.h>
191#include <bsls_keyword.h>
192
193#include <bsl_cstddef.h>
194#include <bsl_functional.h>
195#include <bsl_iostream.h>
196#include <bsl_map.h>
197#include <bsl_optional.h>
198#include <bsl_string.h>
199#include <bsl_string_view.h>
200#include <bsl_utility.h>
201#include <bsl_vector.h>
202
203#if BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
204// clang-format off
205// Include version that can be compiled with C++03
206// Generated on Fri Nov 28 18:11:57 2025
207// Command line: sim_cpp11_features.pl ball_categorymanager_radixtree.h
208
209# define COMPILING_BALL_CATEGORYMANAGER_RADIXTREE_H
211# undef COMPILING_BALL_CATEGORYMANAGER_RADIXTREE_H
212
213// clang-format on
214#else
215
216
217namespace ball {
218
219 // ====================================
220 // class CategoryManager_RadixTree_Node
221 // ====================================
222
223/// This class template represents a node in the radix tree. Each node stores
224/// a prefix string, an optional value, and child nodes mapped with their
225/// prefix-part starting character. As the second underscore in the class name
226/// indicates, this is a component-local class not intended for use outside of
227///
228/// See @ref ball_categorymanager_radixtree
229// the radix tree implementation.
230template <class t_VALUE>
232 public:
233 // PUBLIC TYPES
234
235 /// Child nodes mapped by the starting their starting character of their
236 /// prefix-part.
238
239 private:
240 // PRIVATE TYPES
242
243 private:
244 // DATA
245 bsl::string d_prefix; // prefix-part of this node
246 ValueProxy d_value; // optional value
247 Children d_children; // child nodes mapped by prefix part 1st character
248
249 public:
250 // PUBLIC TYPES
252
253 // CREATORS
254
255 /// Create a `CategoryManager_RadixTree_Node` object with the specified
256 /// `prefix` and no value. Optionally specify an `allocator` (e.g., the
257 /// address of a `bslma::Allocator` object) to supply memory; otherwise,
258 /// the default allocator is used.
261 const allocator_type& allocator = allocator_type());
262
263 /// Create a `CategoryManager_RadixTree_Node` object having the same value
264 /// as the specified `original` object. Use the default allocator to
265 /// supply memory for this object.
267 const CategoryManager_RadixTree_Node& original);
268
269 /// Create a `CategoryManager_RadixTree_Node` object having the same value
270 /// as the specified `original` object, and use the specified `allocator`
271 /// to supply memory for this new object.
273 const CategoryManager_RadixTree_Node& original,
274 const allocator_type& allocator);
275
276 /// Create a `CategoryManager_RadixTree_Node` object having the same value
277 /// as the specified `original` object by moving (in amortized constant
278 /// time) the contents of `original` to the newly-created object. The
279 /// allocator associated with `original` is propagated for use in the
280 /// newly-created object. `original` is left in a valid but unspecified
281 /// state.
285
286 /// Create a `CategoryManager_RadixTree_Node` object having the same value
287 /// as the specified `original` object, and use the specified `allocator`
288 /// to supply memory for this new object. The contents of `original` are
289 /// moved (in amortized constant time) to the newly-created object if
290 /// `allocator == original.get_allocator()`, and are move-inserted (in
291 /// linear time) using `allocator` otherwise. `original` is left in a
292 /// valid but unspecified state.
295 const allocator_type& allocator);
296
297 // MANIPULATORS
298
299 /// Assign to this object the value of the specified `rhs` object, and
300 /// return a reference providing modifiable access to this object.
303
304 /// Assign to this object the value of the specified `rhs` object, and
305 /// return a reference providing modifiable access to this object. The
306 /// contents of `rhs` are moved (in amortized constant time) to this object
307 /// if `get_allocator() == rhs.get_allocator()`; otherwise, all elements in
308 /// this object are either destroyed or move-assigned to, and each
309 /// additional element in `rhs` is move-inserted into this object. `rhs`
310 /// is left in a valid but unspecified state.
313
314 /// Return a reference providing modifiable access to the child nodes of
315 /// this node.
317
318 /// Return a reference providing modifiable access to the prefix string of this node.
319 ///
320 /// \note Note that only a part of the prefix is stored in a node;
321 /// the full key is obtained by concatenating the prefixes of all nodes.
323
324 /// Efficiently exchange the value of this object with the value of the
325 /// specified `other` object. This method provides the no-throw
326 /// exception-safety guarantee if the two objects were created with the
327 /// same allocator; otherwise, it provides the strong guarantee.
329
330 /// Return a reference providing modifiable access to the optional value of
331 /// this node.
333
334 // ACCESSORS
335
336 /// Return a reference providing non-modifiable access to the child nodes
337 /// of this node.
338 const Children& children() const;
339
340 /// Return a reference providing non-modifiable access to the prefix string
341 /// of this node.
342 const bsl::string& prefix() const;
343
344 /// Return a reference providing non-modifiable access to the optional
345 /// value of this node.
347
348 // Aspects
349
350 /// Return the allocator used by this object to supply memory.
352};
353
354// FREE OPERATORS
355
356/// Return `true` if the specified `lhs` and `rhs` nodes have the same value,
357/// and `false` otherwise. Two nodes have the same value if they have the same
358/// prefix, the same value (or both have no value), and the same children.
359template <class t_VALUE>
362
363/// Return `true` if the specified `lhs` and `rhs` nodes do not have the same
364/// value, and `false` otherwise.
365template <class t_VALUE>
368
369 // ==============================================
370 // class CategoryManager_RadixTree_ChildNodeGuard
371 // ==============================================
372
373/// RAII guard to remove a child node when an exception is thrown during value
374/// emplacement in a `CategoryManager_RadixTree_Node`. This guard ensures
375/// exception safety by automatically removing a newly created child node from
376/// its parent if the value construction throws an exception.
377///
378/// See @ref ball_categorymanager_radixtree
379template <class t_VALUE>
381 // TYPES
382 public:
384 typedef typename Node::Children::iterator ChildIterator;
385
386 private:
387 // DATA
388 Node *d_parent_p;
389 ChildIterator d_child;
390 bool d_released;
391
392 private:
393 // NOT IMPLEMENTED
400
401 public:
402 // CREATORS
403
404 /// Create a guard managing the specified `child` iterator in the
405 /// specified `parent` node. The guard will erase the child from the
406 /// parent upon destruction unless `release()` is called.
408 ChildIterator child);
409
410 /// Destroy this guard. If `release()` has not been called, erase the
411 /// child node from the parent (specified at construction).
413
414 // MANIPULATORS
415
416 /// Release this guard, preventing the child node from being erased upon
417 /// destruction. This method should be called after the value has been
418 /// successfully emplaced in the child node.
419 void release();
420};
421
422 // ===============================
423 // class CategoryManager_RadixTree
424 // ===============================
425
426/// This class template implements a space-efficient associative container that
427/// maps string keys to values of the specified `t_VALUE` type. The container
428/// uses a radix tree (compressed trie) data structure, which shares common
429/// prefixes among keys. The container provides O(k) insertion, lookup, and
430/// removal operations, where k is the key length.
431///
432/// See @ref ball_categorymanager_radixtree
433template <class t_VALUE>
435 public:
436 // PUBLIC TYPES
438 typedef t_VALUE value_type;
439 typedef bsl::size_t size_type;
440
441 /// Type for mutable access to the optional value. Used also as return
442 /// type for mutable finders where empty optional signifies "not found".
444
445 /// Type for immutable access to the optional value. Used also as return
446 /// type for immutable finders where empty optional signifies "not found".
448
449 /// The return type of adding a value to the tree with `emplace()`. It is
450 /// *not* the usual `insert` return type, because this data structure does
451 /// not provide an iterator. The `.first` boolean is `true` if an element
452 /// was inserted, and the `.second` data member is a reference to the
453 /// (possibly newly created) value. Notice that if the value existed
454 /// (`.first == false`) the reference wrapper will still give access to the
455 /// value of that node; the `.second` is always a valid mutable reference
456 /// to the value belonging to the key used in the `emplace` call.
458
459 private:
460 // PRIVATE TYPES
463
464 // PRIVATE MANIPULATORS
465#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
466 /// Recursively insert a value into the subtree rooted at the specified
467 /// `node` with the specified `remainingKey` and `args`. Return a pair
468 /// consisting of a reference to the inserted or existing value and a
469 /// boolean indicating whether insertion took place (`true` if inserted,
470 /// `false` if already existed). Notice that the returned reference is
471 /// always valid and refers to the value associated with `remainingKey`.
472 template <class... Args>
473 EmplaceResult emplaceImp(Node *node,
474 const bsl::string_view& remainingKey,
475 Args&&... args);
476#endif
477
478 /// Recursively erase all children of the specified `node` (but not the
479 /// node itself). Return the number of entries removed.
480 size_type eraseAllChildren(Node *node);
481
482 /// Recursively erase the entry with the specified `remainingKey` from the
483 /// subtree rooted at the specified `node`. Return `true` if the entry was
484 /// removed, and `false` otherwise. Deleting an entry may not delete the
485 /// node corresponding to `remainingKey` if it has children. However if
486 /// the node is deleted, this method will also recursively delete
487 /// all now-unused (no value) nodes that lead to it. Notice that this
488 /// method erases one entry only, meaning that it does not erase entries
489 /// with keys that have `remainingKey` as a prefix. If a node exists with
490 /// `remainingKey` but has no value (it is not an entry) this method will
491 /// do nothing and return `false`.
492 bool eraseImp(Node *node, const bsl::string_view& remainingKey);
493
494 /// Recursively erase all entries with keys matching the specified
495 /// `remainingPrefix` from the subtree rooted at the specified `node`.
496 /// Return the number of entries removed. While deleting the entries all
497 /// nodes that become unused (no value and no children) are also deleted.
498 size_type erasePrefixImp(Node *node,
499 const bsl::string_view& remainingPrefix);
500
501 /// Clean up a child node after an erase operation. If the child at the
502 /// specified `it` in the specified `node`'s children map (accessed via
503 /// the specified `firstChar`) has no value and no children, remove it.
504 /// If the child has no value but exactly one grandchild, merge the child
505 /// with its grandchild to maintain a compact tree structure.
506 void cleanupChildAfterErase(
507 Node *node,
508 typename Node::Children::iterator it,
509 char firstChar);
510
511 private:
512 // PRIVATE CLASS METHODS
513
514 /// Recursively visit all key-value pairs in the subtree rooted at the
515 /// specified `node` with the specified `keyPrefix`, invoking the specified `functor` for each pair.
516 ///
517 /// \note Note that this variant does not allow
518 /// modifying the value by the functor. Return the number of times the
519 /// functor is called.
520 template <class t_FUNCTOR>
521 static void forEachImp(const Node *node,
522 const bsl::string_view& keyPrefix,
523 const t_FUNCTOR& functor);
524
525 /// Recursively visit all key-value pairs in the subtree rooted at the
526 /// specified `node` with the specified `keyPrefix`, invoking the specified `functor` for each pair.
527 ///
528 /// \note Note that this "manipulator" variant allows
529 /// modifying the value by the functor.
530 template <class t_FUNCTOR>
531 static void forEachImp(Node *node,
532 const bsl::string_view& keyPrefix,
533 const t_FUNCTOR& functor);
534
535 /// Recursively visit all key-value pairs in the subtree rooted at the
536 /// specified 'node' with the specified 'key', invoking the specified
537 /// 'functor' for each pair. This manipulator version allows modifying the
538 /// value by the functor. Return the number of times the functor was
539 /// called.
540 template <class t_FUNCTOR>
541 static size_type forEachPrefixImp(Node *node,
542 const bsl::string_view& key,
543 const t_FUNCTOR& functor);
544
545 /// Recursively visit all key-value pairs in the subtree rooted at the
546 /// specified 'node' with the specified 'key', invoking the specified
547 /// 'functor' for each pair. This accessor version does not allow modifying
548 /// the value by the functor. Return the number of times the functor was
549 /// called.
550 template <class t_FUNCTOR>
551 static size_type forEachPrefixImp(const Node *node,
552 const bsl::string_view& key,
553 const t_FUNCTOR& functor);
554
555 /// Recursively print the subtree rooted at the specified `node` with the
556 /// specified `keyPrefix` to the specified `stream`, using the specified
557 /// `currLevel` for indentation and `spacesPerLevel` for spacing control,
558 /// and when necessary the specified `depth` to indicate the depth on the
559 /// tree (in single line printing). This method is intended for debugging.
560 static void printNodeImp(bsl::ostream& stream,
561 int depth,
562 const Node *node,
563 const bsl::string& keyPrefix,
564 int currLevel,
565 int spacesPerLevel);
566
567 private:
568 // DATA
569 Node d_root; // root node of the tree
570 size_type d_size; // number of entries in the tree
571
572 // FRIENDS
573 template <class t_TYPE>
576
577 template <class t_TYPE>
580
581 public:
582 // CREATORS
583
584 /// Create an empty `CategoryManager_RadixTree`. Optionally specify
585 /// an `allocator` (e.g., the address of a `bslma::Allocator` object)
586 /// to supply memory; otherwise, the default allocator is used.
588 explicit CategoryManager_RadixTree(const allocator_type& allocator);
589
590 /// Create a `CategoryManager_RadixTree` having the same value as the
591 /// specified `original` object. Optionally specify an `allocator`
592 /// (e.g., the address of a `bslma::Allocator` object) to supply
593 /// memory; otherwise, the default allocator is used.
595 const CategoryManager_RadixTree& original,
596 const allocator_type& allocator = allocator_type());
597
598 /// Create a `CategoryManager_RadixTree` having the same value as the
599 /// specified `original` object by moving (in amortized constant time) the
600 /// contents of `original` to the newly-created object. The allocator
601 /// associated with `original` is propagated for use in the
602 /// newly-created object. `original` is left in a valid but
603 /// unspecified state.
607
608 /// Create a `CategoryManager_RadixTree` having the same value as the
609 /// specified `original` object that uses the specified `allocator` to
610 /// supply memory. The contents of `original` are moved (in amortized
611 /// constant time) to the newly-created object if
612 /// `allocator == original.get_allocator()`, and are move-inserted (in
613 /// linear time) using `allocator` otherwise. `original` is left in a
614 /// valid but unspecified state.
617 const allocator_type& allocator);
618
619 /// Destroy this object.
621
622 // MANIPULATORS
623
624 /// Assign to this object the value of the specified `rhs` object, and
625 /// return a reference providing modifiable access to this object.
627
628 /// Assign to this object the value of the specified `rhs` object, and
629 /// return a reference providing modifiable access to this object. The
630 /// contents of `rhs` are moved (in amortized constant time) to this object
631 /// if `get_allocator() == rhs.get_allocator()`; otherwise, all elements in
632 /// this object are either destroyed or move-assigned to, and each
633 /// additional element in `rhs` is move-inserted into this object. `rhs`
634 /// is left in a valid but unspecified state.
637
638#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
639 /// Insert into this tree an entry with the specified `key` and a newly
640 /// created `t_VALUE` object, constructed by forwarding `get_allocator()`
641 /// (if required) and the specified (variable number of) `args` to the
642 /// corresponding constructor of `t_VALUE`. Return a pair consisting of a
643 /// reference to the value associated with `key` (whether newly inserted or
644 /// already existing) and a boolean indicating whether insertion took place
645 /// (`true` if the key was not already present, `false` otherwise). This
646 /// method requires that `t_VALUE` be `emplace-constructible` from `args`.
647 template <class... Args>
649 Args&&... args);
650#endif
651
652 /// Remove all entries from this tree. After this call `empty()` will
653 /// return `true`. After this call the tree will have 0 nodes.
654 void clear();
655
656 /// Remove from this tree the entry with the specified `key`. Return
657 /// `true` if the entry was removed (key existed), and `false` otherwise.
658 bool erase(const bsl::string_view& key);
659
660 /// Remove all children of the entry matching the specified `prefix`, but
661 /// not the entry with the prefix itself. If unused (no value, no
662 /// children) nodes remain remove those, too. If no entry exists for
663 /// `prefix` remove nothing. Return the number of entries removed.
665
666 /// Remove from this tree all entries with keys that have the specified
667 /// `prefix`, including the entry for the `prefix` itself if it exists. Return the number of entries removed.
668 ///
669 /// \note Note that this method removes
670 /// all nodes whose keys start with `prefix`, not just those that have a
671 /// value.
673
674 /// Return an optional containing a reference to the modifiable value
675 /// associated with the specified `key`, or an empty optional if the key is
676 /// not found. The returned reference remains valid until the tree is
677 /// modified.
679
680 /// Return the longest prefix of the specified `key` that has an associated
681 /// value in this tree, or an empty string view if no such prefix exists.
682 /// If the optionally specified `value` is not null, load into `*value` a
683 /// reference to the value associated with the returned prefix.
684 ///
685 /// \note Note that an empty return value can mean either that no matching prefix exists,
686 /// or that the empty string itself is the longest matching prefix (when
687 /// the tree contains a value for the empty key). Also note that the
688 /// returned reference (if set) remains valid until the tree is modified.
690 const bsl::string_view& key);
691
692 /// Call the specified `functor` for each key-value pair in this tree.
693 /// The `functor` should be a callable object that accepts two parameters:
694 /// `const bsl::string_view&` for the key and `t_VALUE&` for the value.
695 /// The order of visitation is unspecified. Noteice that the functor is
696 /// able to modify the value.
697 template <class t_FUNCTOR>
698 void forEach(const t_FUNCTOR& functor);
699
700 /// Call the specified `functor` for each key-value pair whose key starts
701 /// with the specified `prefix`. The `functor` should be a callable object
702 /// that accepts two parameters: `const bsl::string_view&` for the key and
703 /// `t_VALUE&` for the value. Return the number of times the functor was
704 /// called. The order of visitation is unspecified. Noteice that the
705 /// functor is able to modify the value.
706 template <class t_FUNCTOR>
708 const t_FUNCTOR& functor);
709
710 /// Efficiently exchange the value of this object with the value of the
711 /// specified `other` object. This method provides the no-throw exception-safety guarantee.
712 ///
713 /// \pre The behavior is undefined unless this
714 /// object was created with the same allocator as `other`.
716
717 // ACCESSORS
718
719 /// Return `true` if this tree contains an entry for the specified `key`,
720 /// and `false` otherwise.
721 bool contains(const bsl::string_view& key) const;
722
723 /// Return the total number of nodes in this tree, including internal nodes without values.
724 ///
725 /// \note Note that this method has O(n) complexity where
726 /// n is the number of nodes, and is intended for use in testing to verify
727 /// tree structure invariants. In user code use `size()` that tells the
728 /// actual number of entries with values.
730
731 /// Return `true` if this tree contains no entries, and `false` otherwise.
732 bool empty() const;
733
734 /// Return an optional containing a reference to the non-modifiable value
735 /// associated with the specified `key`, or an empty optional if the key is
736 /// not found. The returned reference remains valid until the tree is
737 /// modified.
739
740 /// Return the longest prefix of the specified `key` that has an associated
741 /// value in this tree, or an empty string view if no such prefix exists.
742 /// If the optionally specified `value` is not null, load into `*value` a
743 /// reference to the value associated with the returned prefix.
744 ///
745 /// \note Note that an empty return value can mean either that no matching prefix exists,
746 /// or that the empty string itself is the longest matching prefix (when
747 /// the tree contains a value for the empty key). Also note that the
748 /// returned reference (if set) remains valid until the tree is modified.
750 OptValueCRef *value,
751 const bsl::string_view& key) const;
752
753 /// Call the specified `functor` for each key-value pair in this tree.
754 /// The `functor` should be a callable object that accepts two parameters:
755 /// `const bsl::string_view&` for the key and `const t_VALUE&` for the
756 /// value. The order of visitation is unspecified. See also the
757 /// manipulator variation that allows the functor to modify the value.
758 template <class t_FUNCTOR>
759 void forEach(const t_FUNCTOR& functor) const;
760
761 /// Call the specified `functor` for each key-value pair whose key starts
762 /// with the specified `prefix`. The `functor` should be a callable object
763 /// that accepts two parameters: `const bsl::string_view&` for the key and
764 /// `const t_VALUE&` for the value. Return the number of times the functor
765 /// was called. The order of visitation is unspecified. See also the
766 /// manipulator variation that allows the functor to modify the value.
767 template <class t_FUNCTOR>
769 const t_FUNCTOR& functor) const;
770
771 /// Write the value of this object to the specified output `stream` in a
772 /// human-readable format, and return a non-`const` reference to
773 /// `stream`. Optionally specify an initial indentation `level`, whose
774 /// absolute value is incremented recursively for nested objects. If
775 /// `level` is specified, optionally specify `spacesPerLevel`, whose
776 /// absolute value indicates the number of spaces per indentation level
777 /// for this and all of its nested objects. If `level` is negative,
778 /// suppress indentation of the first line. If `spacesPerLevel` is
779 /// negative, format the entire output on one line, suppressing all but
780 /// the initial indentation (as governed by `level`). If `stream` is not valid on entry, this operation has no effect.
781 ///
782 /// \note Note that the
783 /// format is not fully specified, and may change without notice.
784 bsl::ostream& printNodes(bsl::ostream& stream,
785 int level = 0,
786 int spacesPerLevel = 4) const;
787
788 /// Return the number of entries in this tree.
790
791 // Aspects
792
793 /// Return the allocator used by this object to supply memory.
794 ///
795 /// \note Note that if no allocator was supplied at construction the default allocator in
796 /// effect at construction is used.
798};
799
800// FREE OPERATORS
801
802/// Return `true` if the specified `lhs` and `rhs` objects have the same
803/// value, and `false` otherwise. Two `CategoryManager_RadixTree` objects
804/// have the same value if they have the same number of entries and each key
805/// in `lhs` maps to the same value as in `rhs`.
806template <class t_VALUE>
807bool operator==(const CategoryManager_RadixTree<t_VALUE>& lhs,
809
810/// Return `true` if the specified `lhs` and `rhs` objects do not have the
811/// same value, and `false` otherwise. Two `CategoryManager_RadixTree`
812/// objects do not have the same value if they differ in their number of
813/// entries or if any key maps to different values in the two objects.
814template <class t_VALUE>
815bool operator!=(const CategoryManager_RadixTree<t_VALUE>& lhs,
817
818// FREE FUNCTIONS
819
820/// Exchange the values of the specified `a` and `b` objects. This function
821/// provides the no-throw exception-safety guarantee if the two objects were
822/// created with the same allocator and the basic guarantee otherwise.
823template <class t_VALUE>
826
827// ============================================================================
828// INLINE FUNCTION DEFINITIONS
829// ============================================================================
830
831 // ----------------------------------------------
832 // class CategoryManager_RadixTree_ChildNodeGuard
833 // ----------------------------------------------
834
835// CREATORS
836template <class t_VALUE>
837inline
840 ChildIterator child)
841: d_parent_p(parent)
842, d_child(child)
843, d_released(false)
844{
845 BSLS_ASSERT(parent);
846 BSLS_ASSERT(child != parent->children().end());
847}
848
849template <class t_VALUE>
850inline
853{
854 if (!d_released) {
855 d_parent_p->children().erase(d_child);
856 }
857}
858
859// MANIPULATORS
860template <class t_VALUE>
861inline
866
867 // ------------------------------------
868 // class CategoryManager_RadixTree_Node
869 // ------------------------------------
870
871// CREATORS
872template <class t_VALUE>
873inline
875 const bsl::string_view& prefix,
876 const allocator_type& allocator)
877: d_prefix(prefix, allocator)
878, d_value(allocator)
879, d_children(allocator)
880{
881}
882
883template <class t_VALUE>
884inline
886 const CategoryManager_RadixTree_Node& original)
887: d_prefix(original.d_prefix)
888, d_value(original.d_value, allocator_type())
889, d_children(original.d_children)
890{
891}
892
893template <class t_VALUE>
894inline
896 const CategoryManager_RadixTree_Node& original,
897 const allocator_type& allocator)
898: d_prefix(original.d_prefix, allocator)
899, d_value(original.d_value, allocator)
900, d_children(original.d_children, allocator)
901{
902}
903
904template <class t_VALUE>
905inline
919
920template <class t_VALUE>
921inline
924 const allocator_type& allocator)
925: d_prefix(bslmf::MovableRefUtil::move(
926 bslmf::MovableRefUtil::access(original).d_prefix),
927 allocator)
928, d_value(bslmf::MovableRefUtil::move(
929 bslmf::MovableRefUtil::access(original).d_value),
930 allocator)
931, d_children(bslmf::MovableRefUtil::move(
932 bslmf::MovableRefUtil::access(original).d_children),
933 allocator)
934{
935}
936
937// MANIPULATORS
938template <class t_VALUE>
939inline
943{
944 if (this != &rhs) {
945 // Copy-and-swap for strong exception safety
946 CategoryManager_RadixTree_Node temp(rhs, get_allocator());
947 swap(temp);
948 }
949 return *this;
950}
951
952template <class t_VALUE>
953inline
957{
958 CategoryManager_RadixTree_Node& lvalue = rhs;
959 if (this != &lvalue) {
960 if (get_allocator() == lvalue.get_allocator()) {
961 // Same allocator - can swap efficiently
962 swap(lvalue);
963 }
964 else {
965 // Different allocators - must deep copy, use copy-and-swap
966 CategoryManager_RadixTree_Node temp(lvalue, get_allocator());
967 swap(temp);
968 }
969 }
970 return *this;
971}
972
973template <class t_VALUE>
974inline
977{
978 return d_children;
979}
980
981template <class t_VALUE>
982inline
987
988template <class t_VALUE>
989void
992{
993 BSLS_ASSERT(get_allocator() == other.get_allocator());
994
995 bslalg::SwapUtil::swap(&d_prefix, &other.d_prefix);
996 bslalg::SwapUtil::swap(&d_value.object(), &other.d_value.object());
997 bslalg::SwapUtil::swap(&d_children, &other.d_children);
998}
999
1000template <class t_VALUE>
1001inline
1006
1007// ACCESSORS
1008template <class t_VALUE>
1009inline
1012{
1013 return d_children;
1014}
1015
1016template <class t_VALUE>
1017inline
1019{
1020 return d_prefix;
1021}
1022
1023template <class t_VALUE>
1024inline
1027{
1028 return d_value.object();
1029}
1030
1031 // Aspects
1032
1033template <class t_VALUE>
1034inline
1040
1041 // -------------------------------
1042 // class CategoryManager_RadixTree
1043 // -------------------------------
1044
1045// PRIVATE MANIPULATORS
1046#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
1047template <class t_VALUE>
1048template <class... Args>
1051 Node *node,
1052 const bsl::string_view& remainingKey,
1053 Args&&... args)
1054{
1055 BSLS_ASSERT(node);
1056
1057 if (remainingKey.empty()) {
1058 if (node->value().has_value()) {
1059 return EmplaceResult(false, node->value().value()); // RETURN
1060 }
1061 node->value().emplace(std::forward<Args>(args)...);
1062 return EmplaceResult(true, node->value().value()); // RETURN
1063 }
1064
1065 const char firstChar = remainingKey[0];
1066 typename Node::Children::iterator it = node->children().find(firstChar);
1067
1068 if (it == node->children().end()) {
1069 // No child with this first character, create new node
1070 typename Node::Children::iterator iter =
1071 node->children().emplace(firstChar, remainingKey).first;
1072 ChildNodeGuard guard(node, iter);
1073 iter->second.value().emplace(std::forward<Args>(args)...);
1074 guard.release();
1075 return EmplaceResult(true, iter->second.value().value()); // RETURN
1076 }
1077
1078 Node& child = it->second;
1079 const bsl::string_view childPrefix = child.prefix();
1080
1081 // Find common prefix length
1082 size_type minLen = bsl::min(remainingKey.size(), childPrefix.size());
1083 const bsl::string_view::const_iterator mismatchPos =
1084 bsl::mismatch(remainingKey.begin(),
1085 remainingKey.begin() + minLen,
1086 childPrefix.begin())
1087 .first;
1088 const size_type commonLen = mismatchPos - remainingKey.begin();
1089
1090 if (commonLen == childPrefix.size()) {
1091 // Key shares full prefix with child, recurse into child
1092 return emplaceImp(&child,
1093 remainingKey.substr(commonLen),
1094 std::forward<Args>(args)...); // RETURN
1095 }
1096
1097 // Need to split the child node
1098 // Build the split structure first, without modifying the tree
1099 Node splitNode(childPrefix.substr(0, commonLen), get_allocator());
1100
1101 // Create a copy of the child node with adjusted prefix
1102 // Copy constructor (will throw if allocation fails)
1103 // We are allocating it with the object-allocator so we can move it into
1104 // the tree without the need to actually deep-copy.
1105 Node childCopy(child, get_allocator());
1106 childCopy.prefix() = childPrefix.substr(commonLen);
1107
1108 // Insert the child copy into split node (may throw)
1109 splitNode.children().emplace(childCopy.prefix()[0],
1110 bslmf::MovableRefUtil::move(childCopy));
1111
1112 if (commonLen == remainingKey.size()) {
1113 // The split point is exactly our key - emplace value into split node
1114 splitNode.value().emplace(std::forward<Args>(args)...);
1115
1116 // All operations succeeded - commit by moving split structure into the
1117 // tree
1118 it->second = bslmf::MovableRefUtil::move(splitNode);
1119 return EmplaceResult(true,
1120 it->second.value().value()); // RETURN
1121 }
1122
1123 // Insert remaining key under split node (may throw)
1124 const bsl::string_view newKey = remainingKey.substr(commonLen);
1125 const typename Node::Children::iterator newIter =
1126 splitNode.children().emplace(newKey[0], newKey).first;
1127 newIter->second.value().emplace(std::forward<Args>(args)...);
1128
1129 // All operations succeeded - commit by moving split structure into tree
1130 it->second = bslmf::MovableRefUtil::move(splitNode);
1131 return EmplaceResult(true, newIter->second.value().value());
1132}
1133#endif
1134
1135template <class t_VALUE>
1137CategoryManager_RadixTree<t_VALUE>::eraseAllChildren(
1138 typename CategoryManager_RadixTree<t_VALUE>::Node *node)
1139{
1140 BSLS_ASSERT(node);
1141
1142 size_type count = 0;
1143
1144 typedef typename Node::Children::iterator Iter;
1145 for (Iter it = node->children().begin();
1146 it != node->children().end(); ) {
1147 count += eraseAllChildren(&it->second);
1148 if (it->second.value().has_value()) {
1149 it->second.value().reset();
1150 --d_size;
1151 ++count;
1152 }
1153 it = node->children().erase(it);
1154 }
1155
1156 return count;
1157}
1158
1159template <class t_VALUE>
1160void CategoryManager_RadixTree<t_VALUE>::cleanupChildAfterErase(
1161 Node *node,
1162 typename Node::Children::iterator it,
1163 char firstChar)
1164{
1165 BSLS_ASSERT(node);
1166
1167 Node& child = it->second;
1168
1169 if (!child.value().has_value() && child.children().empty()) {
1170 // Remove child completely
1171 node->children().erase(it);
1172 }
1173 else if (!child.value().has_value() && child.children().size() == 1) {
1174 // Merge child with its single grandchild
1175 // Build merged node without modifying original, then commit atomically
1176 const typename Node::Children::iterator grandIt =
1177 child.children().begin();
1178 Node& grandchild = grandIt->second;
1179
1180 // Build new prefix (may throw - but original tree unchanged)
1181 bsl::string mergedPrefix(child.prefix() + grandchild.prefix(),
1182 get_allocator());
1183
1184 // Create merged node (may throw - but original tree unchanged)
1185 Node mergedNode(mergedPrefix, get_allocator());
1186 mergedNode.value() = bslmf::MovableRefUtil::move(grandchild.value());
1187 mergedNode.children() =
1188 bslmf::MovableRefUtil::move(grandchild.children());
1189
1190 // All operations succeeded - commit by replacing child in tree
1191 node->children().erase(it);
1192 node->children().emplace(firstChar,
1193 bslmf::MovableRefUtil::move(mergedNode));
1194 }
1195}
1196
1197template <class t_VALUE>
1198bool CategoryManager_RadixTree<t_VALUE>::eraseImp(
1199 Node *node,
1200 const bsl::string_view& remainingKey)
1201{
1202 BSLS_ASSERT(node);
1203
1204 if (remainingKey.empty()) {
1205 if (!node->value().has_value()) {
1206 return false; // RETURN
1207 }
1208 node->value().reset();
1209 return true; // RETURN
1210 }
1211
1212 const char firstChar = remainingKey[0];
1213 typename Node::Children::iterator it = node->children().find(firstChar);
1214
1215 if (it == node->children().end()) {
1216 return false; // RETURN
1217 }
1218
1219 Node& child = it->second;
1220 bsl::string_view childPrefix = child.prefix();
1221
1222 if (!remainingKey.starts_with(childPrefix)) {
1223 return false; // RETURN
1224 }
1225
1226 // Recurse into child
1227 const bool erased = eraseImp(&child,
1228 remainingKey.substr(childPrefix.size()));
1229
1230 if (!erased) {
1231 return false; // RETURN
1232 }
1233
1234 // Post-order cleanup: merge or remove child if needed
1235 cleanupChildAfterErase(node, it, firstChar);
1236
1237 return true;
1238}
1239
1240template <class t_VALUE>
1242CategoryManager_RadixTree<t_VALUE>::erasePrefixImp(
1243 Node *node,
1244 const bsl::string_view& remainingPrefix)
1245{
1246 BSLS_ASSERT(node);
1247
1248 if (remainingPrefix.empty()) {
1249 // Found the prefix node - recursively count and remove this subtree
1250 size_type count = 0;
1251
1252 // Count and remove value at this node
1253 if (node->value().has_value()) {
1254 node->value().reset();
1255 --d_size;
1256 ++count;
1257 }
1258
1259 // Recursively count and remove all children
1260 typedef typename Node::Children::iterator Iter;
1261 for (Iter it = node->children().begin();
1262 it != node->children().end();
1263 ++it) {
1264 count += erasePrefixImp(&it->second, "");
1265 }
1266
1267 node->children().clear();
1268
1269 return count; // RETURN
1270 }
1271
1272 const char firstChar = remainingPrefix[0];
1273 const typename Node::Children::iterator it =
1274 node->children().find(firstChar);
1275
1276 if (it == node->children().end()) {
1277 return 0; // RETURN
1278 }
1279
1280 Node& child = it->second;
1281 const bsl::string_view childPrefix = child.prefix();
1282
1283 if (remainingPrefix.starts_with(childPrefix)) {
1284 // Prefix matches this child's prefix completely, recurse
1285 const size_type count =
1286 erasePrefixImp(&child,
1287 remainingPrefix.substr(childPrefix.size()));
1288
1289 // After recursion, clean up child if needed
1290 cleanupChildAfterErase(node, it, firstChar);
1291
1292 return count; // RETURN
1293 }
1294 else if (childPrefix.starts_with(remainingPrefix)) {
1295 // Child prefix starts with remaining prefix - remove entire child
1296 const size_type count = erasePrefixImp(&child, "");
1297 node->children().erase(it);
1298 return count; // RETURN
1299 }
1300
1301 return 0;
1302}
1303
1304// PRIVATE CLASS METHODS
1305template <class t_VALUE>
1306template <class t_FUNCTOR>
1307void
1308CategoryManager_RadixTree<t_VALUE>::forEachImp(
1309 const Node *node,
1310 const bsl::string_view& keyPrefix,
1311 const t_FUNCTOR& functor)
1312{
1313 // Design Note: No return value with a count because it would always be
1314 // `size()` as we always run on all nodes with values.
1315
1316 BSLS_ASSERT(node);
1317
1318 const bsl::string fullKey = keyPrefix + node->prefix();
1319
1320 // Call on `node` itself
1321 if (node->value().has_value()) {
1322 functor(fullKey, node->value().value());
1323 }
1324
1325 // Recursively handle the children
1326 typedef typename Node::Children::const_iterator ConstIter;
1327 for (ConstIter it = node->children().begin();
1328 it != node->children().end();
1329 ++it) {
1330 forEachImp(&it->second, fullKey, functor);
1331 }
1332}
1333
1334template <class t_VALUE>
1335template <class t_FUNCTOR>
1336void
1337CategoryManager_RadixTree<t_VALUE>::forEachImp(
1338 Node *node,
1339 const bsl::string_view& keyPrefix,
1340 const t_FUNCTOR& functor)
1341{
1342 // Design Note: No return value with a count because it would always be
1343 // `size()` as we always run on all nodes with values.
1344
1345 BSLS_ASSERT(node);
1346
1347 const bsl::string fullKey = keyPrefix + node->prefix();
1348
1349 // Call on `node` itself
1350 if (node->value().has_value()) {
1351 functor(fullKey, node->value().value());
1352 }
1353
1354 // Recursively handle the children
1355 typedef typename Node::Children::iterator Iter;
1356 for (Iter it = node->children().begin();
1357 it != node->children().end();
1358 ++it) {
1359 forEachImp(&it->second, fullKey, functor);
1360 }
1361}
1362
1363template <class t_VALUE>
1364template <class t_FUNCTOR>
1366CategoryManager_RadixTree<t_VALUE>::forEachPrefixImp(
1367 Node *node,
1368 const bsl::string_view& key,
1369 const t_FUNCTOR& functor)
1370{
1371 BSLS_ASSERT(node);
1372
1373 size_type count = 0;
1374
1375 // Call on `node` itself
1376 if (node->value()) {
1377 functor(key, *node->value());
1378 ++count;
1379 }
1380
1381 // Recursively handle the children
1382 typedef typename Node::Children::iterator Iter;
1383 for (Iter it = node->children().begin();
1384 it != node->children().end();
1385 ++it) {
1386 const bsl::string nextKey = key + it->second.prefix();
1387 count += forEachPrefixImp(&it->second, nextKey, functor);
1388 }
1389
1390 return count;
1391}
1392
1393template <class t_VALUE>
1394template <class t_FUNCTOR>
1396CategoryManager_RadixTree<t_VALUE>::forEachPrefixImp(
1397 const Node *node,
1398 const bsl::string_view& key,
1399 const t_FUNCTOR& functor)
1400{
1401 BSLS_ASSERT(node);
1402
1403 size_type count = 0;
1404
1405 // Call on `node` itself
1406 if (node->value()) {
1407 functor(key, *node->value());
1408 ++count;
1409 }
1410
1411 // Recursively handle the children
1412 typedef typename Node::Children::const_iterator Iter;
1413 for (Iter it = node->children().begin();
1414 it != node->children().end();
1415 ++it) {
1416 const bsl::string nextKey = key + it->second.prefix();
1417 count += forEachPrefixImp(&it->second, nextKey, functor);
1418 }
1419
1420 return count;
1421}
1422
1423template <class t_VALUE>
1424void
1425CategoryManager_RadixTree<t_VALUE>::printNodeImp(
1426 bsl::ostream& stream,
1427 int depth,
1428 const Node *node,
1429 const bsl::string& keyPrefix,
1430 int currLevel,
1431 int spacesPerLevel)
1432{
1433 BSLS_ASSERT(node);
1434
1435 const bool noFirstLineIndent = (currLevel < 0);
1436 const bool singleLineMode = (spacesPerLevel < 0);
1437
1438 const int absLevel = noFirstLineIndent ? -currLevel : currLevel;
1439 const int absSpacesPerLevel = singleLineMode
1440 ? -spacesPerLevel
1441 : spacesPerLevel;
1442
1443 if (!noFirstLineIndent) {
1444 stream << bsl::string(absLevel * absSpacesPerLevel, ' ');
1445 }
1446
1447 if (singleLineMode) {
1448 stream << '{' << depth << "} ";
1449 }
1450
1451 stream << '"' << keyPrefix << '"';
1452 if (node->value().has_value()) {
1453 stream << ": ";
1454 stream << node->value().value();
1455 }
1456 else {
1457 stream << ": **NO-VALUE**";
1458 }
1459
1460 stream << (singleLineMode ? ' ' : '\n');
1461
1462 const int nextLevel = absLevel + 1;
1463
1464 typedef typename Node::Children::const_iterator Iter;
1465 for (Iter it = node->children().begin();
1466 it != node->children().end();
1467 ++it) {
1468 printNodeImp(stream,
1469 depth + 1,
1470 &it->second,
1471 keyPrefix + it->second.prefix(),
1472 singleLineMode ? -nextLevel : nextLevel,
1473 spacesPerLevel);
1474 }
1475}
1476
1477// CREATORS
1478template <class t_VALUE>
1479inline
1481: d_root("")
1482, d_size(0)
1483{
1484 // Notice that the root has the empty key, so if we add a value with an
1485 // empty key it will not make a child node, it'll be added to `d_root`.
1486}
1487
1488template <class t_VALUE>
1489inline
1491 const allocator_type& allocator)
1492: d_root("", allocator)
1493, d_size(0)
1494{
1495 // Notice that the root has the empty key, so if we add a value with an
1496 // empty key it will not make a child node, it'll be added to `d_root`.
1497}
1498
1499template <class t_VALUE>
1500inline
1502 const CategoryManager_RadixTree& original,
1503 const allocator_type& allocator)
1504: d_root(original.d_root, allocator)
1505, d_size(original.d_size)
1506{
1507}
1508
1509template <class t_VALUE>
1510inline
1521
1522template <class t_VALUE>
1523inline
1526 const allocator_type& allocator)
1527: d_root(bslmf::MovableRefUtil::move(
1528 bslmf::MovableRefUtil::access(original).d_root),
1529 allocator)
1530, d_size(bslmf::MovableRefUtil::move(
1531 bslmf::MovableRefUtil::access(original).d_size))
1532{
1533 if (bslmf::MovableRefUtil::access(original).get_allocator() == allocator) {
1534 bslmf::MovableRefUtil::access(original).d_size = 0;
1535 }
1536}
1537
1538// MANIPULATORS
1539template <class t_VALUE>
1540inline
1543 const CategoryManager_RadixTree& rhs)
1544{
1545 if (this != &rhs) {
1546 CategoryManager_RadixTree temp(rhs, get_allocator());
1547 swap(temp);
1548 }
1549 return *this;
1550}
1551
1552template <class t_VALUE>
1553inline
1557{
1558 CategoryManager_RadixTree& lvalue = rhs;
1559 if (this != &lvalue) {
1560 if (get_allocator() == lvalue.get_allocator()) {
1562 swap(temp);
1563 }
1564 else {
1566 get_allocator());
1567 swap(temp);
1568 }
1569 }
1570 return *this;
1571}
1572
1573#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
1574template <class t_VALUE>
1575template <class... Args>
1578 Args&&... args)
1579{
1580 const EmplaceResult result = emplaceImp(&d_root,
1581 key,
1582 std::forward<Args>(args)...);
1583 if (result.first) {
1584 ++d_size;
1585 }
1586 return result;
1587}
1588#endif
1589
1590template <class t_VALUE>
1591inline
1593{
1594 d_root.children().clear();
1595 d_root.value().reset();
1596 d_size = 0;
1597}
1598
1599template <class t_VALUE>
1601{
1602 const bool erased = eraseImp(&d_root, key);
1603 if (erased) {
1604 --d_size;
1605 }
1606 return erased;
1607}
1608
1609template <class t_VALUE>
1612 const bsl::string_view& prefix)
1613{
1614 Node *node = &d_root;
1615 size_type pos = 0;
1616
1617 while (pos < prefix.size()) {
1618 typedef typename Node::Children::iterator Iter;
1619
1620 Iter it = node->children().find(prefix[pos]);
1621 if (it == node->children().end()) {
1622 return 0; // RETURN
1623 }
1624
1625 Node &child = it->second;
1626 const bsl::string_view childPrefix = child.prefix();
1627 if (prefix.substr(pos, childPrefix.size()) == childPrefix) {
1628 node = &child;
1629 pos += childPrefix.size();
1630 } else {
1631 return 0; // RETURN
1632 }
1633 }
1634 // Now 'node' is the prefix node. Erase all its children, but not itself.
1635 return eraseAllChildren(node);
1636}
1637
1638template <class t_VALUE>
1641{
1642 // Empty prefix is a prefix of all nodes, so we just quickly clear it out
1643 if (prefix.empty()) {
1644 const size_type oldSize = d_size;
1645 clear();
1646 return oldSize; // RETURN
1647 }
1648
1649 return erasePrefixImp(&d_root, prefix);
1650}
1651
1652template <class t_VALUE>
1655{
1656 // Iterative implementation to avoid stack overflow
1657 Node *currentNode = &d_root;
1658 bsl::string_view remainingKey = key;
1659
1660 while (true) {
1661 if (remainingKey.empty()) {
1662 return currentNode->value().has_value()
1664 bsl::ref(currentNode->value().value()))
1665 : bsl::nullopt; // RETURN
1666 }
1667
1668 const typename Node::Children::iterator it =
1669 currentNode->children().find(remainingKey[0]);
1670
1671 if (it == currentNode->children().end()) {
1672 return bsl::nullopt; // RETURN
1673 }
1674
1675 Node& child = it->second;
1676 const bsl::string_view childPrefix = child.prefix();
1677
1678 // Check if key matches child prefix
1679 if (!remainingKey.starts_with(childPrefix)) {
1680 return bsl::nullopt; // RETURN
1681 }
1682
1683 remainingKey.remove_prefix(childPrefix.size());
1684 currentNode = &child;
1685 }
1686}
1687
1688template <class t_VALUE>
1689inline
1692 OptValueRef *value,
1693 const bsl::string_view& key)
1694{
1695 OptValueCRef constValue;
1696 const CategoryManager_RadixTree<t_VALUE>* constThis =
1697 const_cast<const CategoryManager_RadixTree*>(this);
1698 const bsl::string_view result =
1699 constThis->findLongestCommonPrefix(&constValue, key);
1700
1701 if (value) {
1702 if (constValue.has_value()) {
1703 *value = bsl::ref(const_cast<t_VALUE&>(constValue.value().get()));
1704 } else {
1705 *value = bsl::nullopt;
1706 }
1707 }
1708 return result;
1709}
1710
1711template <class t_VALUE>
1712template <class t_FUNCTOR>
1714{
1715 if (d_size == 0) {
1716 return; // RETURN
1717 }
1718
1719 forEachImp(&d_root, "", functor);
1720}
1721
1722template <class t_VALUE>
1723template <class t_FUNCTOR>
1726 const bsl::string_view& prefix,
1727 const t_FUNCTOR& functor)
1728{
1729 Node *node = &d_root;
1730 bsl::string keySoFar;
1731 size_type pos = 0;
1732 while (pos < prefix.size()) {
1733 bool found = false;
1734 typedef typename Node::Children::iterator Iter;
1735 for (Iter it = node->children().begin();
1736 it != node->children().end();
1737 ++it) {
1738 const Node& child = it->second;
1739 const bsl::string& childPrefix = child.prefix();
1740 size_type i = 0;
1741 while (i < childPrefix.size()
1742 && pos + i < prefix.size()
1743 && prefix[pos + i] == childPrefix[i]) {
1744 ++i;
1745 }
1746 if (i == childPrefix.size()) {
1747 // Full child prefix match, keep descending
1748 node = &it->second;
1749 keySoFar.append(childPrefix);
1750 pos += i;
1751 found = true;
1752 break; // BREAK
1753 } else if (i == prefix.size() - pos) {
1754 // Prefix matches the start of childPrefix, descend into child
1755 node = &it->second;
1756 keySoFar.append(childPrefix.substr(0, i));
1757 pos += i;
1758 found = true;
1759 // Now, apply functor to all descendants of this child
1760 return forEachPrefixImp(node, prefix, functor);
1761 }
1762 }
1763 if (!found) {
1764 node = 0;
1765 break; // BREAK
1766 }
1767 }
1768 if (!node) {
1769 return 0; // RETURN
1770 }
1771 return forEachPrefixImp(node, prefix.substr(0, keySoFar.size()), functor);
1772}
1773
1774template <class t_VALUE>
1775inline
1777{
1778 BSLS_ASSERT(get_allocator() == other.get_allocator());
1779
1780 bslalg::SwapUtil::swap(&d_root, &other.d_root);
1781 bslalg::SwapUtil::swap(&d_size, &other.d_size);
1782}
1783
1784// ACCESSORS
1785template <class t_VALUE>
1786inline
1788 const bsl::string_view& key) const
1789{
1790 // Iterative implementation to avoid stack overflow
1791 const Node *currentNode = &d_root;
1792 bsl::string_view remainingKey = key;
1793
1794 while (true) {
1795 if (remainingKey.empty()) {
1796 return currentNode->value().has_value(); // RETURN
1797 }
1798
1799 const typename Node::Children::const_iterator it =
1800 currentNode->children().find(remainingKey[0]);
1801
1802 if (it == currentNode->children().end()) {
1803 return false; // RETURN
1804 }
1805
1806 const Node& child = it->second;
1807 const bsl::string_view childPrefix = child.prefix();
1808
1809 // Check if key matches child prefix
1810 if (!remainingKey.starts_with(childPrefix)) {
1811 return false; // RETURN
1812 }
1813
1814 remainingKey.remove_prefix(childPrefix.size());
1815 currentNode = &child;
1816 }
1817}
1818
1819template <class t_VALUE>
1822{
1823 size_type count = 1; // count root node
1824
1825 // Recursively count all child nodes
1826 typedef typename Node::Children::const_iterator Iter;
1827 for (Iter it = d_root.children().begin();
1828 it != d_root.children().end();
1829 ++it) {
1830 // Count this child node
1831 ++count;
1832
1833 // Recursively count all descendants of this child
1835 stack.push_back(&it->second);
1836
1837 while (!stack.empty()) {
1838 const Node* node = stack.back();
1839 stack.pop_back();
1840
1841 for (Iter childIt = node->children().begin();
1842 childIt != node->children().end();
1843 ++childIt) {
1844 ++count;
1845 stack.push_back(&childIt->second);
1846 }
1847 }
1848 }
1849
1850 return count;
1851}
1852
1853template <class t_VALUE>
1854inline
1856{
1857 return 0 == d_size;
1858}
1859
1860template <class t_VALUE>
1863{
1864 // Iterative implementation to avoid stack overflow
1865 const Node *currentNode = &d_root;
1866 bsl::string_view remainingKey = key;
1867
1868 while (true) {
1869 if (remainingKey.empty()) {
1870 return currentNode->value().has_value()
1872 bsl::cref(currentNode->value().value()))
1873 : bsl::nullopt; // RETURN
1874 }
1875
1876 const typename Node::Children::const_iterator it =
1877 currentNode->children().find(remainingKey[0]);
1878
1879 if (it == currentNode->children().end()) {
1880 return bsl::nullopt; // RETURN
1881 }
1882
1883 const Node& child = it->second;
1884 const bsl::string_view childPrefix = child.prefix();
1885
1886 // Check if key matches child prefix
1887 if (!remainingKey.starts_with(childPrefix)) {
1888 return bsl::nullopt; // RETURN
1889 }
1890
1891 remainingKey.remove_prefix(childPrefix.size());
1892 currentNode = &child;
1893 }
1894}
1895
1896template <class t_VALUE>
1897inline
1899 OptValueCRef *value,
1900 const bsl::string_view& key) const
1901
1902{
1903 const Node *node = &d_root;
1904 size_type matched = 0;
1905 size_type pos = 0;
1906 size_type lastMatchedLength = 0;
1907 OptValueCRef lastValueRef = bsl::nullopt;
1908
1909 // Handle the case where the root node has a value (empty-string key)
1910 if (node->value().has_value()) {
1911 lastMatchedLength = 0;
1912 lastValueRef = bsl::cref(node->value().value());
1913 }
1914
1915 while (pos < key.size()) {
1916 typedef typename Node::Children::const_iterator Iter;
1917 const Iter it = node->children().find(key[pos]);
1918 if (it == node->children().end()) {
1919 break; // BREAK
1920 }
1921 const Node& child = it->second;
1922 const bsl::string& childPrefix = child.prefix();
1923 size_type i = 0;
1924 while (i < childPrefix.size()
1925 && pos + i < key.size()
1926 && key[pos + i] == childPrefix[i]) {
1927 ++i;
1928 }
1929 // Defensive: if no progress is made, break to avoid infinite loop
1930 if (i == 0) {
1931 // No characters matched in prefix, cannot advance
1932 break; // BREAK
1933 }
1934 if (i < childPrefix.size()) {
1935 // Partial match, stop here
1936 break; // BREAK
1937 }
1938 // Full prefix match
1939 matched += i;
1940 pos += i;
1941 node = &child;
1942 if (node->value().has_value()) {
1943 lastMatchedLength = matched;
1944 lastValueRef = bsl::cref(node->value().value());
1945 }
1946 }
1947 if (value) {
1948 *value = lastValueRef;
1949 }
1950 return key.substr(0, lastMatchedLength);
1951}
1952
1953template <class t_VALUE>
1954template <class t_FUNCTOR>
1955void
1956CategoryManager_RadixTree<t_VALUE>::forEach(const t_FUNCTOR& functor) const
1957{
1958 if (d_size == 0) {
1959 return; // RETURN
1960 }
1961
1962 forEachImp(&d_root, "", functor);
1963}
1964
1965template <class t_VALUE>
1966template <class t_FUNCTOR>
1969 const bsl::string_view& prefix,
1970 const t_FUNCTOR& functor) const
1971{
1972 const Node *node = &d_root;
1973 bsl::string keySoFar;
1974 size_type pos = 0;
1975
1976 while (pos < prefix.size()) {
1977 bool found = false;
1978 typedef typename Node::Children::const_iterator Iter;
1979 for (Iter it = node->children().begin();
1980 it != node->children().end();
1981 ++it) {
1982 const Node& child = it->second;
1983 const bsl::string& childPrefix = child.prefix();
1984 size_type i = 0;
1985 while (i < childPrefix.size()
1986 && pos + i < prefix.size()
1987 && prefix[pos + i] == childPrefix[i]) {
1988 ++i;
1989 }
1990 if (i == childPrefix.size()) {
1991 // Full child prefix match, keep descending
1992 node = &it->second;
1993 keySoFar.append(childPrefix);
1994 pos += i;
1995 found = true;
1996 break; // BREAK
1997 } else if (i == prefix.size() - pos) {
1998 // Prefix matches the start of childPrefix, descend into child
1999 node = &it->second;
2000 keySoFar.append(childPrefix.substr(0, i));
2001 pos += i;
2002 found = true;
2003 // Now, apply functor to all descendants of this child
2004 return forEachPrefixImp(node, prefix, functor);
2005 }
2006 }
2007 if (!found) {
2008 node = 0;
2009 break; // BREAK
2010 }
2011 }
2012 if (!node) {
2013 return 0; // RETURN
2014 }
2015 return forEachPrefixImp(node, prefix.substr(0, keySoFar.size()), functor);
2016}
2017
2018template <class t_VALUE>
2020 bsl::ostream& stream,
2021 int level,
2022 int spacesPerLevel) const
2023{
2024 printNodeImp(stream, 0, &d_root, "", level, spacesPerLevel);
2025 return stream;
2026}
2027
2028template <class t_VALUE>
2029inline
2032{
2033 return d_size;
2034}
2035
2036 // Aspects
2037
2038template <class t_VALUE>
2039inline
2045
2046} // close package namespace
2047
2048 // ------------------------------------
2049 // class CategoryManager_RadixTree_Node
2050 // ------------------------------------
2051
2052// FREE OPERATORS
2053template <class t_VALUE>
2054bool ball::operator==(const CategoryManager_RadixTree_Node<t_VALUE>& lhs,
2055 const CategoryManager_RadixTree_Node<t_VALUE>& rhs)
2056{
2057 return lhs.prefix() == rhs.prefix()
2058 && lhs.value() == rhs.value()
2059 && lhs.children() == rhs.children();
2060}
2061
2062template <class t_VALUE>
2063inline
2064bool ball::operator!=(const CategoryManager_RadixTree_Node<t_VALUE>& lhs,
2065 const CategoryManager_RadixTree_Node<t_VALUE>& rhs)
2066{
2067 return !(lhs == rhs);
2068}
2069
2070 // -------------------------------
2071 // class CategoryManager_RadixTree
2072 // -------------------------------
2073
2074// FREE OPERATORS
2075
2076template <class t_VALUE>
2077bool ball::operator==(const CategoryManager_RadixTree<t_VALUE>& lhs,
2078 const CategoryManager_RadixTree<t_VALUE>& rhs)
2079{
2080 if (lhs.d_size != rhs.d_size) {
2081 return false; // RETURN
2082 }
2083
2084 // Since the radix tree is always in its most compact form, two equal
2085 // trees will have identical structure. Use direct node comparison.
2086 return lhs.d_root == rhs.d_root;
2087}
2088
2089template <class t_VALUE>
2090inline
2091bool ball::operator!=(const CategoryManager_RadixTree<t_VALUE>& lhs,
2092 const CategoryManager_RadixTree<t_VALUE>& rhs)
2093{
2094 return !(lhs == rhs);
2095}
2096
2097// FREE FUNCTIONS
2098template <class t_VALUE>
2099inline
2100void ball::swap(CategoryManager_RadixTree<t_VALUE>& a,
2101 CategoryManager_RadixTree<t_VALUE>& b)
2102{
2103 bslalg::SwapUtil::swap(&a.d_root, &b.d_root);
2104 bslalg::SwapUtil::swap(&a.d_size, &b.d_size);
2105}
2106
2107
2108#endif // End C++11 code
2109
2110#endif // INCLUDED_BALL_CATEGORYMANAGER_RADIXTREE_H
2111
2112// ----------------------------------------------------------------------------
2113// Copyright 2025 Bloomberg Finance L.P.
2114//
2115// Licensed under the Apache License, Version 2.0 (the "License");
2116// you may not use this file except in compliance with the License.
2117// You may obtain a copy of the License at
2118//
2119// http://www.apache.org/licenses/LICENSE-2.0
2120//
2121// Unless required by applicable law or agreed to in writing, software
2122// distributed under the License is distributed on an "AS IS" BASIS,
2123// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2124// See the License for the specific language governing permissions and
2125// limitations under the License.
2126// ----------------------------- END-OF-FILE ----------------------------------
2127
2128/** @} */
2129/** @} */
2130/** @} */
Definition ball_categorymanager_radixtree.h:380
void release()
Definition ball_categorymanager_radixtree.h:862
~CategoryManager_RadixTree_ChildNodeGuard()
Definition ball_categorymanager_radixtree.h:852
Node::Children::iterator ChildIterator
Definition ball_categorymanager_radixtree.h:384
CategoryManager_RadixTree_Node< t_VALUE > Node
Definition ball_categorymanager_radixtree.h:383
Definition ball_categorymanager_radixtree.h:231
CategoryManager_RadixTree_Node(bslmf::MovableRef< CategoryManager_RadixTree_Node > original, const allocator_type &allocator)
Definition ball_categorymanager_radixtree.h:922
void swap(CategoryManager_RadixTree_Node &other)
Definition ball_categorymanager_radixtree.h:990
bsl::allocator allocator_type
Definition ball_categorymanager_radixtree.h:251
CategoryManager_RadixTree_Node(const CategoryManager_RadixTree_Node &original, const allocator_type &allocator)
Definition ball_categorymanager_radixtree.h:895
CategoryManager_RadixTree_Node(bslmf::MovableRef< CategoryManager_RadixTree_Node > original) BSLS_KEYWORD_NOEXCEPT
Definition ball_categorymanager_radixtree.h:906
CategoryManager_RadixTree_Node & operator=(bslmf::MovableRef< CategoryManager_RadixTree_Node > rhs)
Definition ball_categorymanager_radixtree.h:955
Children & children()
Definition ball_categorymanager_radixtree.h:976
bsl::optional< t_VALUE > & value()
Definition ball_categorymanager_radixtree.h:1002
CategoryManager_RadixTree_Node(const CategoryManager_RadixTree_Node &original)
Definition ball_categorymanager_radixtree.h:885
const Children & children() const
Definition ball_categorymanager_radixtree.h:1011
bsl::map< char, CategoryManager_RadixTree_Node > Children
Definition ball_categorymanager_radixtree.h:237
allocator_type get_allocator() const
Return the allocator used by this object to supply memory.
Definition ball_categorymanager_radixtree.h:1036
const bsl::string & prefix() const
Definition ball_categorymanager_radixtree.h:1018
CategoryManager_RadixTree_Node(const bsl::string_view &prefix, const allocator_type &allocator=allocator_type())
Definition ball_categorymanager_radixtree.h:874
const bsl::optional< t_VALUE > & value() const
Definition ball_categorymanager_radixtree.h:1026
bsl::string & prefix()
Definition ball_categorymanager_radixtree.h:983
CategoryManager_RadixTree_Node & operator=(const CategoryManager_RadixTree_Node &rhs)
Definition ball_categorymanager_radixtree.h:941
Definition ball_categorymanager_radixtree.h:434
size_type eraseChildrenOfPrefix(const bsl::string_view &prefix)
Definition ball_categorymanager_radixtree.h:1611
void forEach(const t_FUNCTOR &functor) const
Definition ball_categorymanager_radixtree.h:1956
~CategoryManager_RadixTree()=default
Destroy this object.
OptValueRef find(const bsl::string_view &key)
Definition ball_categorymanager_radixtree.h:1654
bsl::optional< bsl::reference_wrapper< t_VALUE > > OptValueRef
Definition ball_categorymanager_radixtree.h:443
bsl::ostream & printNodes(bsl::ostream &stream, int level=0, int spacesPerLevel=4) const
Definition ball_categorymanager_radixtree.h:2019
bool empty() const
Return true if this tree contains no entries, and false otherwise.
Definition ball_categorymanager_radixtree.h:1855
OptValueCRef find(const bsl::string_view &key) const
Definition ball_categorymanager_radixtree.h:1862
bsl::size_t size_type
Definition ball_categorymanager_radixtree.h:439
bsl::optional< bsl::reference_wrapper< const t_VALUE > > OptValueCRef
Definition ball_categorymanager_radixtree.h:447
bsl::pair< bool, bsl::reference_wrapper< t_VALUE > > EmplaceResult
Definition ball_categorymanager_radixtree.h:457
void swap(CategoryManager_RadixTree &other)
Definition ball_categorymanager_radixtree.h:1776
t_VALUE value_type
Definition ball_categorymanager_radixtree.h:438
friend void swap(CategoryManager_RadixTree< t_TYPE > &a, CategoryManager_RadixTree< t_TYPE > &b)
CategoryManager_RadixTree(const CategoryManager_RadixTree &original, const allocator_type &allocator=allocator_type())
Definition ball_categorymanager_radixtree.h:1501
CategoryManager_RadixTree(bslmf::MovableRef< CategoryManager_RadixTree > original, const allocator_type &allocator)
Definition ball_categorymanager_radixtree.h:1524
allocator_type get_allocator() const
Definition ball_categorymanager_radixtree.h:2041
void forEach(const t_FUNCTOR &functor)
Definition ball_categorymanager_radixtree.h:1713
void clear()
Definition ball_categorymanager_radixtree.h:1592
friend bool operator==(const CategoryManager_RadixTree< t_TYPE > &, const CategoryManager_RadixTree< t_TYPE > &)
CategoryManager_RadixTree & operator=(const CategoryManager_RadixTree &rhs)
Definition ball_categorymanager_radixtree.h:1542
bsl::string_view findLongestCommonPrefix(OptValueCRef *value, const bsl::string_view &key) const
Definition ball_categorymanager_radixtree.h:1898
size_type forEachPrefix(const bsl::string_view &prefix, const t_FUNCTOR &functor)
Definition ball_categorymanager_radixtree.h:1725
EmplaceResult emplace(const bsl::string_view &key, Args &&... args)
Definition ball_categorymanager_radixtree.h:1577
size_type forEachPrefix(const bsl::string_view &prefix, const t_FUNCTOR &functor) const
Definition ball_categorymanager_radixtree.h:1968
bsl::allocator allocator_type
Definition ball_categorymanager_radixtree.h:437
size_type erasePrefix(const bsl::string_view &prefix)
Definition ball_categorymanager_radixtree.h:1640
CategoryManager_RadixTree(bslmf::MovableRef< CategoryManager_RadixTree > original) BSLS_KEYWORD_NOEXCEPT
Definition ball_categorymanager_radixtree.h:1511
size_type countNodes() const
Definition ball_categorymanager_radixtree.h:1821
bsl::string_view findLongestCommonPrefix(OptValueRef *value, const bsl::string_view &key)
Definition ball_categorymanager_radixtree.h:1691
CategoryManager_RadixTree()
Definition ball_categorymanager_radixtree.h:1480
CategoryManager_RadixTree & operator=(bslmf::MovableRef< CategoryManager_RadixTree > rhs)
Definition ball_categorymanager_radixtree.h:1555
bool erase(const bsl::string_view &key)
Definition ball_categorymanager_radixtree.h:1600
CategoryManager_RadixTree(const allocator_type &allocator)
Definition ball_categorymanager_radixtree.h:1490
bool contains(const bsl::string_view &key) const
Definition ball_categorymanager_radixtree.h:1787
size_type size() const
Return the number of entries in this tree.
Definition ball_categorymanager_radixtree.h:2031
Definition bslma_bslallocator.h:588
Definition bslstl_stringview.h:471
BSLS_KEYWORD_CONSTEXPR_CPP14 size_type find(basic_string_view subview, size_type position=0) const BSLS_KEYWORD_NOEXCEPT
Definition bslstl_stringview.h:2284
BSLS_KEYWORD_CONSTEXPR_CPP17 bool starts_with(basic_string_view subview) const BSLS_KEYWORD_NOEXCEPT
Definition bslstl_stringview.h:2215
BSLS_KEYWORD_CONSTEXPR_CPP14 basic_string_view substr(size_type position=0, size_type numChars=npos) const
Definition bslstl_stringview.h:2027
BSLS_KEYWORD_CONSTEXPR size_type size() const BSLS_KEYWORD_NOEXCEPT
Return the length of this view.
Definition bslstl_stringview.h:1904
const value_type * const_iterator
Definition bslstl_stringview.h:481
BSLS_KEYWORD_CONSTEXPR const_iterator begin() const BSLS_KEYWORD_NOEXCEPT
Definition bslstl_stringview.h:1830
BSLS_KEYWORD_CONSTEXPR_CPP14 void remove_prefix(size_type numChars)
Definition bslstl_stringview.h:1800
BSLS_KEYWORD_CONSTEXPR bool empty() const BSLS_KEYWORD_NOEXCEPT
Return true if this view has length 0, and false otherwise.
Definition bslstl_stringview.h:1931
Definition bslstl_string.h:1252
basic_string substr(size_type position=0, size_type numChars=npos) const
Definition bslstl_string.h:8013
size_type size() const BSLS_KEYWORD_NOEXCEPT
Definition bslstl_string.h:7292
allocator_type get_allocator() const BSLS_KEYWORD_NOEXCEPT
Return the allocator used by this string to supply memory.
Definition bslstl_string.h:7423
basic_string & append(const basic_string &suffix)
Definition bslstl_string.h:6188
Definition bslstl_map.h:653
allocator_type get_allocator() const BSLS_KEYWORD_NOEXCEPT
Definition bslstl_map.h:3949
iterator end() BSLS_KEYWORD_NOEXCEPT
Definition bslstl_map.h:3308
iterator find(const key_type &key)
Definition bslstl_map.h:1885
iterator begin() BSLS_KEYWORD_NOEXCEPT
Definition bslstl_map.h:3300
void clear() BSLS_KEYWORD_NOEXCEPT
Definition bslstl_map.h:3927
Definition bslstl_optional.h:2043
Definition bslstl_pair.h:1280
reference back()
Definition bslstl_vector.h:2932
bool empty() const BSLS_KEYWORD_NOEXCEPT
Return true if this vector has size 0, and false otherwise.
Definition bslstl_vector.h:3034
Definition bslstl_vector.h:1120
void push_back(const VALUE_TYPE &value)
Definition bslstl_vector.h:4343
void pop_back()
Definition bslstl_vector.h:4375
Definition bslalg_constructorproxy.h:376
OBJECT_TYPE & object() BSLS_KEYWORD_NOEXCEPT
Return a reference to the modifiable object held by this proxy.
Definition bslalg_constructorproxy.h:1197
static void swap(T *a, T *b)
Definition bslalg_swaputil.h:182
Definition bslmf_movableref.h:752
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1976
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
#define BSLS_KEYWORD_DELETED
Definition bsls_keyword.h:651
#define BSLS_KEYWORD_NOEXCEPT
Definition bsls_keyword.h:674
Definition ball_administration.h:214
void swap(CategoryManager_RadixTree< t_VALUE > &a, CategoryManager_RadixTree< t_VALUE > &b)
bool operator!=(const Attribute &lhs, const Attribute &rhs)
bool operator==(const Attribute &lhs, const Attribute &rhs)
const nullopt_t nullopt
reference_wrapper< const T > cref(const T &object)
reference_wrapper< T > ref(T &object)
Return a reference wrapper that represents the specified object.
ALLOCATOR const STRING_VIEW_LIKE_TYPE & rhs
Definition bslstl_string.h:3918
ALLOCATOR & lhs
Definition bslstl_string.h:3917
basic_string< char > string
Definition bslstl_string.h:844
Definition bdlbb_blob.h:579
static MovableRef< t_TYPE > move(t_TYPE &reference) BSLS_KEYWORD_NOEXCEPT
Definition bslmf_movableref.h:1067
static t_TYPE & access(t_TYPE &ref) BSLS_KEYWORD_NOEXCEPT
Definition bslmf_movableref.h:1039