1 /*************************************************************************
3 * Copyright 2016 Realm Inc.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 **************************************************************************/
19 #ifndef REALM_COLUMN_HPP
20 #define REALM_COLUMN_HPP
22 #include <cstdint> // unint8_t etc
23 #include <cstdlib> // size_t
27 #include <realm/array_integer.hpp>
28 #include <realm/column_type.hpp>
29 #include <realm/column_fwd.hpp>
30 #include <realm/spec.hpp>
31 #include <realm/impl/output_stream.hpp>
32 #include <realm/query_conditions.hpp>
33 #include <realm/bptree.hpp>
34 #include <realm/index_string.hpp>
35 #include <realm/impl/destroy_guard.hpp>
36 #include <realm/exceptions.hpp>
37 #include <realm/table_ref.hpp>
50 struct ImplicitNull<util::Optional<T>> {
51 static constexpr bool value = true;
55 struct ImplicitNull<int64_t> {
56 static constexpr bool value = false;
60 struct ImplicitNull<float> {
61 static constexpr bool value = true;
65 struct ImplicitNull<double> {
66 static constexpr bool value = true;
69 // FIXME: Add specialization for ImplicitNull for float, double, StringData, BinaryData.
71 template <class T, class R, Action action, class Condition, class ColType>
72 R aggregate(const ColType& column, T target, size_t start, size_t end, size_t limit, size_t* return_ndx);
75 // Iterator with random access for Columns
76 template <typename ColumnDataType>
77 class ColumnRandIterator : public std::iterator<std::random_access_iterator_tag, ColumnDataType, ptrdiff_t, size_t> {
79 ColumnRandIterator(const Column<ColumnDataType>* src_col, size_t ndx = 0);
80 bool operator==(const ColumnRandIterator<ColumnDataType>& rhs) const;
81 bool operator!=(const ColumnRandIterator<ColumnDataType>& rhs) const;
82 bool operator<(const ColumnRandIterator<ColumnDataType>& rhs) const;
83 bool operator>(const ColumnRandIterator<ColumnDataType>& rhs) const;
84 bool operator<=(const ColumnRandIterator<ColumnDataType>& rhs) const;
85 bool operator>=(const ColumnRandIterator<ColumnDataType>& rhs) const;
86 ColumnRandIterator<ColumnDataType>& operator+=(ptrdiff_t movement);
87 ColumnRandIterator<ColumnDataType>& operator-=(ptrdiff_t movement);
88 ColumnRandIterator<ColumnDataType>& operator++();
89 ColumnRandIterator<ColumnDataType>& operator--();
90 ColumnRandIterator<ColumnDataType> operator++(int);
91 ColumnRandIterator<ColumnDataType> operator--(int);
92 ColumnRandIterator<ColumnDataType> operator+(ptrdiff_t movement);
93 ColumnRandIterator<ColumnDataType> operator-(ptrdiff_t movement);
94 ptrdiff_t operator-(const ColumnRandIterator<ColumnDataType>& right) const;
95 const ColumnDataType operator*() const;
96 const ColumnDataType operator->() const;
97 const ColumnDataType operator[](ptrdiff_t offset) const;
98 size_t get_col_ndx() const;
102 const Column<ColumnDataType>* m_col;
105 /// Base class for all column types.
108 /// Get the number of entries in this column. This operation is relatively
110 virtual size_t size() const noexcept = 0;
112 /// \throw LogicError Thrown if this column is not string valued.
113 virtual void set_string(size_t row_ndx, StringData value);
115 /// Whether or not this column is nullable.
116 virtual bool is_nullable() const noexcept;
118 /// Whether or not the value at \a row_ndx is NULL. If the column is not
119 /// nullable, always returns false.
120 virtual bool is_null(size_t row_ndx) const noexcept;
122 /// Sets the value at \a row_ndx to be NULL.
123 /// \throw LogicError Thrown if this column is not nullable.
124 virtual void set_null(size_t row_ndx);
126 /// Inserts the specified number of elements into this column
127 /// starting at the specified row index. The new elements will have the
128 /// default value for the column type.
130 /// \param row_ndx The row to start insertion at. If the row_ndx is less
131 /// than prior_num_rows then previous rows from row_ndx onwards will be
132 /// moved ahead by num_rows_to_insert.
134 /// \param num_rows_to_insert The number of rows to insert. There is no
135 /// restriction on this value.
137 /// \param prior_num_rows The number of elements in this column prior to the
140 /// \param nullable Specifies whether or not this column is nullable. This
141 /// function may assert if nullable does not agree with \a is_nullable()
142 virtual void insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool nullable) = 0;
144 /// Removes the specified number of consecutive elements from
145 /// this column, starting at the specified row index.
147 /// \param row_ndx The row to start removal at (inclusive). This must be
148 /// less than prior_num_rows.
150 /// \param num_rows_to_erase The number of rows to erase.
151 /// The row_ndx + num_rows_to_erase must be less than prior_num_rows.
153 /// \param prior_num_rows The number of elements in this column prior to the
156 /// \param broken_reciprocal_backlinks If true, link columns must assume
157 /// that reciprocal backlinks have already been removed. Non-link columns
158 /// should ignore this argument.
159 virtual void erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows,
160 bool broken_reciprocal_backlinks) = 0;
162 /// Removes the element at the specified row index by
163 /// moving the element at the last row index over it. This reduces the
164 /// number of elements by one.
166 /// \param row_ndx The row to erase. Must be less than prior_num_rows.
168 /// \param prior_num_rows The number of elements in this column prior to the
171 /// \param broken_reciprocal_backlinks If true, link columns must assume
172 /// that reciprocal backlinks have already been removed. Non-link columns
173 /// should ignore this argument.
174 virtual void move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool broken_reciprocal_backlinks) = 0;
176 /// Remove all elements from this column.
178 /// \param num_rows The total number of rows in this column.
180 /// \param broken_reciprocal_backlinks If true, link columns must assume
181 /// that reciprocal backlinks have already been removed. Non-link columns
182 /// should ignore this argument.
183 virtual void clear(size_t num_rows, bool broken_reciprocal_backlinks) = 0;
185 /// \brief Swap the elements at the specified indices.
187 /// Behaviour is undefined if:
188 /// - \a row_ndx_1 or \a row_ndx_2 point to an invalid element (out-of
190 /// - \a row_ndx_1 and \a row_ndx_2 point to the same value
191 virtual void swap_rows(size_t row_ndx_1, size_t row_ndx_2) = 0;
193 virtual void destroy() noexcept = 0;
194 void move_assign(ColumnBase& col) noexcept;
196 virtual ~ColumnBase() noexcept
200 // Disable copying, this is not supported.
201 ColumnBase& operator=(const ColumnBase&) = delete;
202 ColumnBase(const ColumnBase&) = delete;
204 // Getter function for index. For integer index, the caller must supply a
205 // buffer that we can store the extracted value in (it may be bitpacked, so
206 // we cannot return a pointer in to the Array as we do with String index).
207 virtual StringData get_index_data(size_t, StringIndex::StringConversionBuffer& buffer) const noexcept = 0;
210 virtual bool supports_search_index() const noexcept;
211 virtual bool has_search_index() const noexcept;
212 virtual StringIndex* create_search_index();
213 virtual void destroy_search_index() noexcept;
214 virtual const StringIndex* get_search_index() const noexcept;
215 virtual StringIndex* get_search_index() noexcept;
216 virtual void set_search_index_ref(ref_type, ArrayParent*, size_t ndx_in_parent);
218 virtual Allocator& get_alloc() const noexcept = 0;
220 /// Returns the 'ref' of the root array.
221 virtual ref_type get_ref() const noexcept = 0;
222 virtual MemRef get_mem() const noexcept = 0;
224 virtual void replace_root_array(std::unique_ptr<Array> leaf) = 0;
225 virtual MemRef clone_deep(Allocator& alloc) const = 0;
226 virtual void detach(void) = 0;
227 virtual bool is_attached(void) const noexcept = 0;
229 static size_t get_size_from_type_and_ref(ColumnType, ref_type, Allocator&, bool) noexcept;
231 // These assume that the right column compile-time type has been
233 static size_t get_size_from_ref(ref_type root_ref, Allocator&);
234 static size_t get_size_from_ref(ref_type spec_ref, ref_type columns_ref, Allocator&);
236 /// Write a slice of this column to the specified output stream.
237 virtual ref_type write(size_t slice_offset, size_t slice_size, size_t table_size, _impl::OutputStream&) const = 0;
239 /// Get this column's logical index within the containing table, or npos
240 /// for free-standing or non-top-level columns.
241 size_t get_column_index() const noexcept
246 virtual void set_parent(ArrayParent*, size_t ndx_in_parent) noexcept = 0;
247 virtual size_t get_ndx_in_parent() const noexcept = 0;
248 virtual void set_ndx_in_parent(size_t ndx_in_parent) noexcept = 0;
250 /// Called to update refs and memory pointers of this column accessor and
251 /// all its nested accessors, but only in cases where the logical contents
252 /// in strictly unchanged. Group::commit(), and
253 /// SharedGroup::commit_and_continue_as_read()() are examples of such
254 /// cases. In both those cases, the purpose is to keep user visible
255 /// accessors in a valid state across a commit.
256 virtual void update_from_parent(size_t old_baseline) noexcept = 0;
260 /// cascade_break_backlinks_to() is called iteratively for each column by
261 /// Table::cascade_break_backlinks_to() with the same arguments as are
262 /// passed to Table::cascade_break_backlinks_to(). Link columns must
263 /// override it. The same is true for cascade_break_backlinks_to_all_rows(),
264 /// except that it is called from
265 /// Table::cascade_break_backlinks_to_all_rows(), and that it expects
266 /// Table::cascade_break_backlinks_to_all_rows() to pass the number of rows
267 /// in the table as \a num_rows.
269 virtual void cascade_break_backlinks_to(size_t row_ndx, CascadeState&);
270 virtual void cascade_break_backlinks_to_all_rows(size_t num_rows, CascadeState&);
274 void discard_child_accessors() noexcept;
276 /// For columns that are able to contain subtables, this function returns
277 /// the pointer to the subtable accessor at the specified row index if it
278 /// exists, otherwise it returns null. For other column types, this function
280 virtual TableRef get_subtable_accessor(size_t row_ndx) const noexcept;
282 /// Detach and remove the subtable accessor at the specified row if it
283 /// exists. For column types that are unable to contain subtable, this
284 /// function does nothing.
285 virtual void discard_subtable_accessor(size_t row_ndx) noexcept;
287 virtual void adj_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept;
288 virtual void adj_acc_erase_row(size_t row_ndx) noexcept;
289 /// See Table::adj_acc_move_over()
290 virtual void adj_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept;
291 virtual void adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept;
292 virtual void adj_acc_move_row(size_t from_ndx, size_t to_ndx) noexcept;
293 virtual void adj_acc_merge_rows(size_t old_row_ndx, size_t new_row_ndx) noexcept;
294 virtual void adj_acc_clear_root_table() noexcept;
297 mark_Recursive = 0x01,
298 mark_LinkTargets = 0x02,
299 mark_LinkOrigins = 0x04,
302 virtual void mark(int type) noexcept;
304 virtual void bump_link_origin_table_version() noexcept;
306 virtual int compare_values(size_t row1, size_t row2) const noexcept = 0;
308 /// Refresh the dirty part of the accessor subtree rooted at this column
311 /// The following conditions are necessary and sufficient for the proper
312 /// operation of this function:
314 /// - The parent table accessor (excluding its column accessors) is in a
315 /// valid state (already refreshed).
317 /// - Every subtable accessor in the subtree is marked dirty if it needs to
318 /// be refreshed, or if it has a descendant accessor that needs to be
321 /// - This column accessor, as well as all its descendant accessors, are in
322 /// structural correspondence with the underlying node hierarchy whose
323 /// root ref is stored in the parent (`Table::m_columns`) (see
324 /// AccessorConsistencyLevels).
326 /// - The 'index in parent' property of the cached root array
327 /// (`root->m_ndx_in_parent`) is valid.
328 virtual void refresh_accessor_tree(size_t new_col_ndx, const Spec&);
330 virtual void verify() const = 0;
331 virtual void verify(const Table&, size_t col_ndx) const;
332 virtual void to_dot(std::ostream&, StringData title = StringData()) const = 0;
333 virtual void do_dump_node_structure(std::ostream&, int level) const = 0;
336 void dump_node_structure() const; // To std::cerr (for GDB)
337 void bptree_to_dot(const Array* root, std::ostream& out) const;
341 using SliceHandler = BpTreeBase::SliceHandler;
343 ColumnBase(size_t column_ndx = npos)
344 : m_column_ndx(column_ndx)
347 ColumnBase(ColumnBase&&) = default;
349 // Must not assume more than minimal consistency (see
350 // AccessorConsistencyLevels).
351 virtual void do_discard_child_accessors() noexcept
356 /// \tparam L Any type with an appropriate `value_type`, %size(),
357 /// and %get() members.
358 template <class L, class T>
359 size_t lower_bound(const L& list, T value) const noexcept;
361 template <class L, class T>
362 size_t upper_bound(const L& list, T value) const noexcept;
367 class CreateHandler {
369 virtual ref_type create_leaf(size_t size) = 0;
370 ~CreateHandler() noexcept
375 static ref_type create(Allocator&, size_t size, CreateHandler&);
378 virtual void leaf_to_dot(MemRef, ArrayParent*, size_t ndx_in_parent, std::ostream&) const = 0;
380 template <class Column>
381 static int compare_values(const Column* column, size_t row1, size_t row2) noexcept;
384 size_t m_column_ndx = npos;
386 static ref_type build(size_t* rest_size_ptr, size_t fixed_height, Allocator&, CreateHandler&);
390 // FIXME: Temporary class until all column types have been migrated to use BpTree interface
391 class ColumnBaseSimple : public ColumnBase {
394 /// Returns the array node at the root of this column, but note
395 /// that there is no guarantee that this node is an inner B+-tree
396 /// node or a leaf. This is the case for a MixedColumn in
398 Array* get_root_array() noexcept
400 return m_array.get();
402 const Array* get_root_array() const noexcept
404 return m_array.get();
408 Allocator& get_alloc() const noexcept final
410 return m_array->get_alloc();
412 void destroy() noexcept override
415 m_array->destroy_deep();
417 ref_type get_ref() const noexcept final
419 return m_array->get_ref();
421 MemRef get_mem() const noexcept final
423 return m_array->get_mem();
425 void detach() noexcept final
429 bool is_attached() const noexcept final
431 return m_array->is_attached();
433 void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept final
435 m_array->set_parent(parent, ndx_in_parent);
437 size_t get_ndx_in_parent() const noexcept final
439 return m_array->get_ndx_in_parent();
441 void set_ndx_in_parent(size_t ndx_in_parent) noexcept override
443 m_array->set_ndx_in_parent(ndx_in_parent);
445 void update_from_parent(size_t old_baseline) noexcept override
447 m_array->update_from_parent(old_baseline);
449 MemRef clone_deep(Allocator& alloc) const override
451 return m_array->clone_deep(alloc);
455 ColumnBaseSimple(size_t column_ndx)
456 : ColumnBase(column_ndx)
459 ColumnBaseSimple(Array* root)
463 std::unique_ptr<Array> m_array;
465 void replace_root_array(std::unique_ptr<Array> new_root) final;
466 bool root_is_leaf() const noexcept
468 return !m_array->is_inner_bptree_node();
471 /// Introduce a new root node which increments the height of the
473 void introduce_new_root(ref_type new_sibling_ref, TreeInsertBase& state, bool is_append);
475 static ref_type write(const Array* root, size_t slice_offset, size_t slice_size, size_t table_size, SliceHandler&,
476 _impl::OutputStream&);
478 #if defined(REALM_DEBUG)
479 void tree_to_dot(std::ostream&) const;
483 class ColumnBaseWithIndex : public ColumnBase {
485 ~ColumnBaseWithIndex() noexcept override
488 void set_ndx_in_parent(size_t ndx) noexcept override;
489 void update_from_parent(size_t old_baseline) noexcept override;
490 void refresh_accessor_tree(size_t, const Spec&) override;
491 void move_assign(ColumnBaseWithIndex& col) noexcept;
492 void destroy() noexcept override;
494 virtual bool supports_search_index() const noexcept override
498 bool has_search_index() const noexcept final
500 return bool(m_search_index);
502 StringIndex* get_search_index() noexcept final
504 return m_search_index.get();
506 const StringIndex* get_search_index() const noexcept final
508 return m_search_index.get();
510 void destroy_search_index() noexcept override;
511 void set_search_index_ref(ref_type ref, ArrayParent* parent, size_t ndx_in_parent) final;
512 StringIndex* create_search_index() override = 0;
515 using ColumnBase::ColumnBase;
516 ColumnBaseWithIndex(ColumnBaseWithIndex&&) = default;
517 std::unique_ptr<StringIndex> m_search_index;
521 /// A column (Column) is a single B+-tree, and the root of
522 /// the column is the root of the B+-tree. All leaf nodes are arrays.
524 class Column : public ColumnBaseWithIndex {
526 using value_type = T;
527 using LeafInfo = typename BpTree<T>::LeafInfo;
528 using LeafType = typename BpTree<T>::LeafType;
530 static constexpr bool nullable = ImplicitNull<T>::value;
532 struct unattached_root_tag {
535 explicit Column() noexcept
536 : ColumnBaseWithIndex(npos)
537 , m_tree(Allocator::get_default())
540 REALM_DEPRECATED("Initialize with ref instead") explicit Column(std::unique_ptr<Array> root) noexcept;
541 Column(Allocator&, ref_type, size_t column_ndx = npos);
542 Column(unattached_root_tag, Allocator&);
543 Column(Column&&) noexcept = default;
544 ~Column() noexcept override;
546 void init_from_parent();
547 void init_from_ref(Allocator&, ref_type);
548 void init_from_mem(Allocator&, MemRef);
550 void destroy() noexcept override;
551 Allocator& get_alloc() const noexcept final;
552 ref_type get_ref() const noexcept final;
553 MemRef get_mem() const noexcept final;
554 void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept override;
555 size_t get_ndx_in_parent() const noexcept final;
556 void set_ndx_in_parent(size_t ndx) noexcept final;
557 void update_from_parent(size_t old_baseline) noexcept override;
558 void refresh_accessor_tree(size_t, const Spec&) override;
559 void detach() noexcept final;
560 bool is_attached() const noexcept final;
561 MemRef clone_deep(Allocator&) const override;
563 void move_assign(Column&);
565 static size_t get_size_from_ref(ref_type root_ref, Allocator& alloc)
567 return ColumnBase::get_size_from_ref(root_ref, alloc);
570 size_t size() const noexcept override;
571 bool is_empty() const noexcept
575 bool is_nullable() const noexcept override;
577 /// Provides access to the leaf that contains the element at the
578 /// specified index. Upon return \a ndx_in_leaf will be set to the
579 /// corresponding index relative to the beginning of the leaf.
581 /// LeafInfo is a struct defined by the underlying BpTree<T>
582 /// data structure, that provides a way for the caller to do
583 /// leaf caching without instantiating too many objects along
586 /// This function cannot be used for modifying operations as it
587 /// does not ensure the presence of an unbroken chain of parent
588 /// accessors. For this reason, the identified leaf should always
589 /// be accessed through the returned const-qualified reference,
590 /// and never directly through the specfied fallback accessor.
591 void get_leaf(size_t ndx, size_t& ndx_in_leaf, LeafInfo& inout_leaf) const noexcept;
593 // Getting and setting values
594 T get(size_t ndx) const noexcept;
595 bool is_null(size_t ndx) const noexcept override;
596 T back() const noexcept;
597 void set(size_t, T value);
598 void set_null(size_t) override;
599 void add(T value = T{});
600 void insert(size_t ndx, T value = T{}, size_t num_rows = 1);
601 void erase(size_t row_ndx);
602 void erase(size_t row_ndx, bool is_last);
603 void move_last_over(size_t row_ndx, size_t last_row_ndx);
607 StringData get_index_data(size_t ndx, StringIndex::StringConversionBuffer& buffer) const noexcept override;
609 // FIXME: Remove these
610 uint64_t get_uint(size_t ndx) const noexcept;
611 ref_type get_as_ref(size_t ndx) const noexcept;
612 void set_uint(size_t ndx, uint64_t value);
613 void set_as_ref(size_t ndx, ref_type value);
616 void adjust(size_t ndx, U diff);
622 void adjust_ge(T limit, U diff);
624 size_t count(T target) const;
626 typename ColumnTypeTraits<T>::sum_type sum(size_t start = 0, size_t end = npos, size_t limit = npos,
627 size_t* return_ndx = nullptr) const;
629 typename ColumnTypeTraits<T>::minmax_type maximum(size_t start = 0, size_t end = npos, size_t limit = npos,
630 size_t* return_ndx = nullptr) const;
632 typename ColumnTypeTraits<T>::minmax_type minimum(size_t start = 0, size_t end = npos, size_t limit = npos,
633 size_t* return_ndx = nullptr) const;
635 double average(size_t start = 0, size_t end = npos, size_t limit = npos, size_t* return_ndx = nullptr) const;
637 size_t find_first(T value, size_t begin = 0, size_t end = npos) const;
638 void find_all(Column<int64_t>& out_indices, T value, size_t begin = 0, size_t end = npos) const;
640 void populate_search_index();
641 StringIndex* create_search_index() override;
642 inline bool supports_search_index() const noexcept override
644 if (realm::is_any<T, float, double>::value)
652 /// Find the lower/upper bound for the specified value assuming
653 /// that the elements are already sorted in ascending order
654 /// according to ordinary integer comparison.
655 size_t lower_bound(T value) const noexcept;
656 size_t upper_bound(T value) const noexcept;
659 using const_iterator = ColumnRandIterator<T>;
661 const_iterator cbegin() const
663 return const_iterator(this, 0); // `this` is const in a const method
665 const_iterator cend() const
667 return const_iterator(this, size());
670 size_t find_gte(T target, size_t start) const;
672 bool compare(const Column&) const noexcept;
673 int compare_values(size_t row1, size_t row2) const noexcept override;
675 static ref_type create(Allocator&, Array::Type leaf_type = Array::type_Normal, size_t size = 0, T value = T{});
677 // Overriding method in ColumnBase
678 ref_type write(size_t, size_t, size_t, _impl::OutputStream&) const override;
680 void insert_rows(size_t, size_t, size_t, bool) override;
681 void erase_rows(size_t, size_t, size_t, bool) override;
682 void move_last_row_over(size_t, size_t, bool) override;
684 /// \brief Swap the elements at the specified indices.
686 /// If this \c Column has a search index defined, it will be updated to
687 /// reflect the changes induced by the swap.
689 /// Behaviour is undefined if:
690 /// - \a row_ndx_1 or \a row_ndx_2 point to an invalid element (out-of
692 /// - \a row_ndx_1 and \a row_ndx_2 point to the same value
693 void swap_rows(size_t, size_t) override;
694 void clear(size_t, bool) override;
696 /// \param row_ndx Must be `realm::npos` if appending.
697 /// \param value The value to store at the specified row.
698 /// \param num_rows The number of rows to insert.
699 void insert_without_updating_index(size_t row_ndx, T value, size_t num_rows);
701 void verify() const override;
702 void to_dot(std::ostream&, StringData title) const override;
703 void do_dump_node_structure(std::ostream&, int) const override;
705 using ColumnBase::verify;
706 void tree_to_dot(std::ostream&) const;
707 MemStats stats() const;
711 /// Returns the array node at the root of this column, but note
712 /// that there is no guarantee that this node is an inner B+-tree
713 /// node or a leaf. This is the case for a MixedColumn in
715 Array* get_root_array() noexcept
717 return &m_tree.root();
719 const Array* get_root_array() const noexcept
721 return &m_tree.root();
726 bool root_is_leaf() const noexcept
728 return m_tree.root_is_leaf();
730 void replace_root_array(std::unique_ptr<Array> leaf) final
732 m_tree.replace_root(std::move(leaf));
735 void set_without_updating_index(size_t row_ndx, T value);
736 void erase_without_updating_index(size_t row_ndx, bool is_last);
737 void move_last_over_without_updating_index(size_t row_ndx, size_t last_row_ndx);
738 void swap_rows_without_updating_index(size_t row_ndx_1, size_t row_ndx_2);
740 /// If any element points to an array node, this function recursively
741 /// destroys that array node. Note that the same is **not** true for
742 /// IntegerColumn::do_erase() and IntegerColumn::do_move_last_over().
744 /// FIXME: Be careful, clear_without_updating_index() currently forgets
745 /// if the leaf type is Array::type_HasRefs.
746 void clear_without_updating_index();
748 void leaf_to_dot(MemRef, ArrayParent*, size_t ndx_in_parent, std::ostream&) const override;
750 static void dump_node_structure(const Array& root, std::ostream&, int level);
752 std::pair<ref_type, size_t> get_to_dot_parent(size_t ndx_in_parent) const;
760 friend class ColumnBase;
761 friend class StringIndex;
765 void do_erase(size_t row_ndx, size_t num_rows_to_erase, bool is_last);
772 inline size_t IntNullColumn::get_size_from_ref(ref_type root_ref, Allocator& alloc)
774 // FIXME: Speed improvement possible by not creating instance, but tricky! This slow method is OK so far
775 // because it's only invoked by Table::get_size_from_ref() which is only used for subtables which we
776 // currently 2016) do not expose publicly.
777 IntNullColumn inc(alloc, root_ref);
782 inline bool ColumnBase::supports_search_index() const noexcept
784 REALM_ASSERT(!has_search_index());
788 inline bool ColumnBase::has_search_index() const noexcept
790 return get_search_index() != nullptr;
793 inline StringIndex* ColumnBase::create_search_index()
798 inline void ColumnBase::destroy_search_index() noexcept
802 inline const StringIndex* ColumnBase::get_search_index() const noexcept
807 inline StringIndex* ColumnBase::get_search_index() noexcept
812 inline void ColumnBase::set_search_index_ref(ref_type, ArrayParent*, size_t)
816 inline void ColumnBase::discard_child_accessors() noexcept
818 do_discard_child_accessors();
821 inline void ColumnBase::discard_subtable_accessor(size_t) noexcept
826 inline void ColumnBase::adj_acc_insert_rows(size_t, size_t) noexcept
831 inline void ColumnBase::adj_acc_erase_row(size_t) noexcept
836 inline void ColumnBase::adj_acc_move_over(size_t, size_t) noexcept
841 inline void ColumnBase::adj_acc_swap_rows(size_t, size_t) noexcept
846 inline void ColumnBase::adj_acc_move_row(size_t, size_t) noexcept
851 inline void ColumnBase::adj_acc_merge_rows(size_t, size_t) noexcept
856 inline void ColumnBase::adj_acc_clear_root_table() noexcept
861 inline void ColumnBase::mark(int) noexcept
866 inline void ColumnBase::bump_link_origin_table_version() noexcept
871 template <class Column>
872 int ColumnBase::compare_values(const Column* column, size_t row1, size_t row2) noexcept
874 // we negate nullability such that the two ternary statements in this method can look identical to reduce
876 bool v1 = !column->is_null(row1);
877 bool v2 = !column->is_null(row2);
880 return v1 == v2 ? 0 : v1 < v2 ? 1 : -1;
882 auto a = column->get(row1);
883 auto b = column->get(row2);
884 return a == b ? 0 : a < b ? 1 : -1;
888 void Column<T>::set_without_updating_index(size_t ndx, T value)
890 m_tree.set(ndx, std::move(value));
894 void Column<T>::set(size_t ndx, T value)
896 REALM_ASSERT_DEBUG(ndx < size());
897 if (has_search_index()) {
898 m_search_index->set(ndx, value);
900 set_without_updating_index(ndx, std::move(value));
904 void Column<T>::set_null(size_t ndx)
906 REALM_ASSERT_DEBUG(ndx < size());
907 if (!is_nullable()) {
908 throw LogicError{LogicError::column_not_nullable};
910 if (has_search_index()) {
911 m_search_index->set(ndx, null{});
913 m_tree.set_null(ndx);
916 // When a value of a signed type is converted to an unsigned type, the C++ standard guarantees that negative values
917 // are converted from the native representation to 2's complement, but the opposite conversion is left as undefined.
918 // realm::util::from_twos_compl() is used here to perform the correct opposite unsigned-to-signed conversion,
919 // which reduces to a no-op when 2's complement is the native representation of negative values.
921 void Column<T>::set_uint(size_t ndx, uint64_t value)
923 set(ndx, util::from_twos_compl<int_fast64_t>(value));
927 void Column<T>::set_as_ref(size_t ndx, ref_type ref)
929 set(ndx, from_ref(ref));
934 void Column<T>::adjust(size_t ndx, U diff)
936 REALM_ASSERT_3(ndx, <, size());
937 m_tree.adjust(ndx, diff);
942 void Column<T>::adjust(U diff)
949 void Column<T>::adjust_ge(T limit, U diff)
951 m_tree.adjust_ge(limit, diff);
955 size_t Column<T>::count(T target) const
957 if (has_search_index()) {
958 return m_search_index->count(target);
960 return to_size_t(aggregate<T, int64_t, act_Count, Equal>(*this, target, 0, size(), npos, nullptr));
964 typename ColumnTypeTraits<T>::sum_type Column<T>::sum(size_t start, size_t end, size_t limit,
965 size_t* return_ndx) const
967 using sum_type = typename ColumnTypeTraits<T>::sum_type;
969 return aggregate<T, sum_type, act_Sum, NotNull>(*this, 0, start, end, limit, return_ndx);
971 return aggregate<T, sum_type, act_Sum, None>(*this, 0, start, end, limit, return_ndx);
975 double Column<T>::average(size_t start, size_t end, size_t limit, size_t* return_ndx) const
977 if (end == size_t(-1))
980 auto s = sum(start, end, limit);
981 size_t cnt = to_size_t(aggregate<T, int64_t, act_Count, NotNull>(*this, 0, start, end, limit, nullptr));
984 double avg = double(s) / (cnt == 0 ? 1 : cnt);
989 typename ColumnTypeTraits<T>::minmax_type Column<T>::minimum(size_t start, size_t end, size_t limit,
990 size_t* return_ndx) const
992 using R = typename ColumnTypeTraits<T>::minmax_type;
993 return aggregate<T, R, act_Min, NotNull>(*this, 0, start, end, limit, return_ndx);
997 typename ColumnTypeTraits<T>::minmax_type Column<T>::maximum(size_t start, size_t end, size_t limit,
998 size_t* return_ndx) const
1000 using R = typename ColumnTypeTraits<T>::minmax_type;
1001 return aggregate<T, R, act_Max, NotNull>(*this, 0, start, end, limit, return_ndx);
1005 void Column<T>::get_leaf(size_t ndx, size_t& ndx_in_leaf, LeafInfo& inout_leaf_info) const noexcept
1007 m_tree.get_leaf(ndx, ndx_in_leaf, inout_leaf_info);
1011 StringData Column<T>::get_index_data(size_t ndx, StringIndex::StringConversionBuffer& buffer) const noexcept
1014 return to_str(x, buffer);
1018 void Column<T>::populate_search_index()
1020 REALM_ASSERT(has_search_index());
1021 // Populate the index
1022 size_t num_rows = size();
1023 for (size_t row_ndx = 0; row_ndx != num_rows; ++row_ndx) {
1024 bool is_append = true;
1025 if (is_null(row_ndx)) {
1026 m_search_index->insert(row_ndx, null{}, 1, is_append); // Throws
1029 T value = get(row_ndx);
1030 m_search_index->insert(row_ndx, value, 1, is_append); // Throws
1036 StringIndex* Column<T>::create_search_index()
1038 if (realm::is_any<T, float, double>::value)
1041 REALM_ASSERT(!has_search_index());
1042 REALM_ASSERT(supports_search_index());
1043 m_search_index.reset(new StringIndex(this, get_alloc())); // Throws
1044 populate_search_index();
1045 return m_search_index.get();
1049 size_t Column<T>::find_first(T value, size_t begin, size_t end) const
1051 REALM_ASSERT_3(begin, <=, size());
1052 REALM_ASSERT(end == npos || (begin <= end && end <= size()));
1054 if (m_search_index && begin == 0 && end == npos)
1055 return m_search_index->find_first(value);
1056 return m_tree.find_first(value, begin, end);
1060 void Column<T>::find_all(IntegerColumn& result, T value, size_t begin, size_t end) const
1062 REALM_ASSERT_3(begin, <=, size());
1063 REALM_ASSERT(end == npos || (begin <= end && end <= size()));
1065 if (m_search_index && begin == 0 && end == npos)
1066 return m_search_index->find_all(result, value);
1067 return m_tree.find_all(result, value, begin, end);
1070 inline size_t ColumnBase::get_size_from_ref(ref_type root_ref, Allocator& alloc)
1072 const char* root_header = alloc.translate(root_ref);
1073 bool root_is_leaf = !Array::get_is_inner_bptree_node_from_header(root_header);
1075 return Array::get_size_from_header(root_header);
1076 return BpTreeNode::get_bptree_size_from_header(root_header);
1079 template <class L, class T>
1080 size_t ColumnBase::lower_bound(const L& list, T value) const noexcept
1083 size_t list_size = list.size();
1084 while (0 < list_size) {
1085 size_t half = list_size / 2;
1086 size_t mid = i + half;
1087 typename L::value_type probe = list.get(mid);
1088 if (probe < value) {
1090 list_size -= half + 1;
1099 template <class L, class T>
1100 size_t ColumnBase::upper_bound(const L& list, T value) const noexcept
1103 size_t list_size = list.size();
1104 while (0 < list_size) {
1105 size_t half = list_size / 2;
1106 size_t mid = i + half;
1107 typename L::value_type probe = list.get(mid);
1108 if (!(value < probe)) {
1110 list_size -= half + 1;
1120 inline ref_type ColumnBase::create(Allocator& alloc, size_t column_size, CreateHandler& handler)
1122 size_t rest_size = column_size;
1123 size_t fixed_height = 0; // Not fixed
1124 return build(&rest_size, fixed_height, alloc, handler);
1128 Column<T>::Column(Allocator& alloc, ref_type ref, size_t column_ndx)
1129 : ColumnBaseWithIndex(column_ndx)
1130 , m_tree(BpTreeBase::unattached_tag{})
1132 // fixme, must m_search_index be copied here?
1133 m_tree.init_from_ref(alloc, ref);
1137 Column<T>::Column(unattached_root_tag, Allocator& alloc)
1138 : ColumnBaseWithIndex(npos)
1144 Column<T>::Column(std::unique_ptr<Array> root) noexcept
1145 : m_tree(std::move(root))
1150 Column<T>::~Column() noexcept
1155 void Column<T>::init_from_parent()
1157 m_tree.init_from_parent();
1161 void Column<T>::init_from_ref(Allocator& alloc, ref_type ref)
1163 m_tree.init_from_ref(alloc, ref);
1167 void Column<T>::init_from_mem(Allocator& alloc, MemRef mem)
1169 m_tree.init_from_mem(alloc, mem);
1173 void Column<T>::destroy() noexcept
1175 ColumnBaseWithIndex::destroy();
1180 void Column<T>::move_assign(Column<T>& col)
1182 ColumnBaseWithIndex::move_assign(col);
1183 m_tree = std::move(col.m_tree);
1187 Allocator& Column<T>::get_alloc() const noexcept
1189 return m_tree.get_alloc();
1193 void Column<T>::set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept
1195 m_tree.set_parent(parent, ndx_in_parent);
1199 size_t Column<T>::get_ndx_in_parent() const noexcept
1201 return m_tree.get_ndx_in_parent();
1205 void Column<T>::set_ndx_in_parent(size_t ndx_in_parent) noexcept
1207 ColumnBaseWithIndex::set_ndx_in_parent(ndx_in_parent);
1208 m_tree.set_ndx_in_parent(ndx_in_parent);
1212 void Column<T>::detach() noexcept
1218 bool Column<T>::is_attached() const noexcept
1220 return m_tree.is_attached();
1224 ref_type Column<T>::get_ref() const noexcept
1226 return get_root_array()->get_ref();
1230 MemRef Column<T>::get_mem() const noexcept
1232 return get_root_array()->get_mem();
1236 void Column<T>::update_from_parent(size_t old_baseline) noexcept
1238 ColumnBaseWithIndex::update_from_parent(old_baseline);
1239 m_tree.update_from_parent(old_baseline);
1243 MemRef Column<T>::clone_deep(Allocator& alloc) const
1245 return m_tree.clone_deep(alloc);
1249 size_t Column<T>::size() const noexcept
1251 return m_tree.size();
1255 bool Column<T>::is_nullable() const noexcept
1261 T Column<T>::get(size_t ndx) const noexcept
1263 return m_tree.get(ndx);
1267 bool Column<T>::is_null(size_t ndx) const noexcept
1269 return nullable && m_tree.is_null(ndx);
1273 T Column<T>::back() const noexcept
1275 return m_tree.back();
1279 ref_type Column<T>::get_as_ref(size_t ndx) const noexcept
1281 return to_ref(get(ndx));
1285 uint64_t Column<T>::get_uint(size_t ndx) const noexcept
1287 static_assert(std::is_convertible<T, uint64_t>::value, "T is not convertible to uint.");
1288 return static_cast<uint64_t>(get(ndx));
1292 void Column<T>::add(T value)
1294 insert(npos, std::move(value));
1298 void Column<T>::insert_without_updating_index(size_t row_ndx, T value, size_t num_rows)
1300 size_t column_size = this->size(); // Slow
1301 bool is_append = row_ndx == column_size || row_ndx == npos;
1302 size_t ndx_or_npos_if_append = is_append ? npos : row_ndx;
1304 m_tree.insert(ndx_or_npos_if_append, std::move(value), num_rows); // Throws
1308 void Column<T>::insert(size_t row_ndx, T value, size_t num_rows)
1310 size_t column_size = this->size(); // Slow
1311 bool is_append = row_ndx == column_size || row_ndx == npos;
1312 size_t ndx_or_npos_if_append = is_append ? npos : row_ndx;
1314 m_tree.insert(ndx_or_npos_if_append, value, num_rows); // Throws
1316 if (has_search_index()) {
1317 row_ndx = is_append ? column_size : row_ndx;
1318 m_search_index->insert(row_ndx, value, num_rows, is_append); // Throws
1323 void Column<T>::erase_without_updating_index(size_t row_ndx, bool is_last)
1325 m_tree.erase(row_ndx, is_last);
1329 void Column<T>::erase(size_t row_ndx)
1331 REALM_ASSERT(size() >= 1);
1332 size_t last_row_ndx = size() - 1; // Note that size() is slow
1333 bool is_last = (row_ndx == last_row_ndx);
1334 erase(row_ndx, is_last); // Throws
1338 void Column<T>::erase(size_t row_ndx, bool is_last)
1340 size_t num_rows_to_erase = 1;
1341 do_erase(row_ndx, num_rows_to_erase, is_last); // Throws
1345 void Column<T>::move_last_over_without_updating_index(size_t row_ndx, size_t last_row_ndx)
1347 m_tree.move_last_over(row_ndx, last_row_ndx);
1351 void Column<T>::move_last_over(size_t row_ndx, size_t last_row_ndx)
1353 REALM_ASSERT_3(row_ndx, <=, last_row_ndx);
1354 REALM_ASSERT_DEBUG(last_row_ndx + 1 == size());
1356 if (has_search_index()) {
1357 // remove the value to be overwritten from index
1358 bool is_last = true; // This tells StringIndex::erase() to not adjust subsequent indexes
1359 m_search_index->erase<StringData>(row_ndx, is_last); // Throws
1361 // update index to point to new location
1362 if (row_ndx != last_row_ndx) {
1363 T moved_value = get(last_row_ndx);
1364 m_search_index->update_ref(moved_value, last_row_ndx, row_ndx); // Throws
1368 move_last_over_without_updating_index(row_ndx, last_row_ndx);
1372 void Column<T>::swap_rows(size_t row_ndx_1, size_t row_ndx_2)
1374 REALM_ASSERT_3(row_ndx_1, <, size());
1375 REALM_ASSERT_3(row_ndx_2, <, size());
1376 REALM_ASSERT_DEBUG(row_ndx_1 != row_ndx_2);
1378 if (has_search_index()) {
1379 T value_1 = get(row_ndx_1);
1380 T value_2 = get(row_ndx_2);
1381 size_t column_size = this->size();
1382 bool row_ndx_1_is_last = row_ndx_1 == column_size - 1;
1383 bool row_ndx_2_is_last = row_ndx_2 == column_size - 1;
1384 m_search_index->erase<StringData>(row_ndx_1, row_ndx_1_is_last);
1385 m_search_index->insert(row_ndx_1, value_2, 1, row_ndx_1_is_last);
1387 m_search_index->erase<StringData>(row_ndx_2, row_ndx_2_is_last);
1388 m_search_index->insert(row_ndx_2, value_1, 1, row_ndx_2_is_last);
1391 swap_rows_without_updating_index(row_ndx_1, row_ndx_2);
1395 void Column<T>::swap_rows_without_updating_index(size_t row_ndx_1, size_t row_ndx_2)
1397 // FIXME: This can be optimized with direct getters and setters.
1398 T value_1 = get(row_ndx_1);
1399 T value_2 = get(row_ndx_2);
1400 m_tree.set(row_ndx_1, value_2);
1401 m_tree.set(row_ndx_2, value_1);
1405 void Column<T>::clear_without_updating_index()
1407 m_tree.clear(); // Throws
1411 void Column<T>::clear()
1413 if (has_search_index()) {
1414 m_search_index->clear();
1416 clear_without_updating_index();
1419 template <class T, class Enable = void>
1420 struct NullOrDefaultValue;
1422 struct NullOrDefaultValue<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
1423 static T null_or_default_value(bool is_null)
1426 return null::get_null_float<T>();
1434 struct NullOrDefaultValue<util::Optional<T>, void> {
1435 static util::Optional<T> null_or_default_value(bool is_null)
1441 return util::some<T>(T{});
1446 struct NullOrDefaultValue<T, typename std::enable_if<!ImplicitNull<T>::value>::type> {
1447 static T null_or_default_value(bool is_null)
1449 REALM_ASSERT(!is_null);
1454 // Implementing pure virtual method of ColumnBase.
1456 void Column<T>::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool insert_nulls)
1458 REALM_ASSERT_DEBUG(prior_num_rows == size());
1459 REALM_ASSERT(row_ndx <= prior_num_rows);
1461 size_t row_ndx_2 = (row_ndx == prior_num_rows ? realm::npos : row_ndx);
1462 T value = NullOrDefaultValue<T>::null_or_default_value(insert_nulls);
1463 insert(row_ndx_2, value, num_rows_to_insert); // Throws
1466 // Implementing pure virtual method of ColumnBase.
1468 void Column<T>::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool)
1470 REALM_ASSERT_DEBUG(prior_num_rows == size());
1471 REALM_ASSERT(num_rows_to_erase <= prior_num_rows);
1472 REALM_ASSERT(row_ndx <= prior_num_rows - num_rows_to_erase);
1474 bool is_last = (row_ndx + num_rows_to_erase == prior_num_rows);
1475 do_erase(row_ndx, num_rows_to_erase, is_last); // Throws
1478 // Implementing pure virtual method of ColumnBase.
1480 void Column<T>::move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool)
1482 REALM_ASSERT_DEBUG(prior_num_rows == size());
1483 REALM_ASSERT(row_ndx < prior_num_rows);
1485 size_t last_row_ndx = prior_num_rows - 1;
1486 move_last_over(row_ndx, last_row_ndx); // Throws
1489 // Implementing pure virtual method of ColumnBase.
1491 void Column<T>::clear(size_t, bool)
1498 size_t Column<T>::lower_bound(T value) const noexcept
1500 if (root_is_leaf()) {
1501 auto root = static_cast<const LeafType*>(get_root_array());
1502 return root->lower_bound(value);
1504 return ColumnBase::lower_bound(*this, value);
1508 size_t Column<T>::upper_bound(T value) const noexcept
1510 if (root_is_leaf()) {
1511 auto root = static_cast<const LeafType*>(get_root_array());
1512 return root->upper_bound(value);
1514 return ColumnBase::upper_bound(*this, value);
1517 // For a *sorted* Column, return first element E for which E >= target or return -1 if none
1519 size_t Column<T>::find_gte(T target, size_t start) const
1521 // fixme: slow reference implementation. See Array::find_gte for faster version
1524 for (idx = start; idx < size(); ++idx) {
1525 if (get(idx) >= target) {
1538 bool Column<T>::compare(const Column<T>& c) const noexcept
1543 for (size_t i = 0; i < n; ++i) {
1544 bool left_is_null = is_null(i);
1545 bool right_is_null = c.is_null(i);
1546 if (left_is_null != right_is_null) {
1549 if (!left_is_null) {
1550 if (get(i) != c.get(i))
1558 int Column<T>::compare_values(size_t row1, size_t row2) const noexcept
1560 return ColumnBase::compare_values(this, row1, row2);
1564 class Column<T>::CreateHandler : public ColumnBase::CreateHandler {
1566 CreateHandler(Array::Type leaf_type, T value, Allocator& alloc)
1569 , m_leaf_type(leaf_type)
1572 ref_type create_leaf(size_t size) override
1574 MemRef mem = BpTree<T>::create_leaf(m_leaf_type, size, m_value, m_alloc); // Throws
1575 return mem.get_ref();
1581 Array::Type m_leaf_type;
1585 ref_type Column<T>::create(Allocator& alloc, Array::Type leaf_type, size_t size, T value)
1587 CreateHandler handler(leaf_type, std::move(value), alloc);
1588 return ColumnBase::create(alloc, size, handler);
1592 ref_type Column<T>::write(size_t slice_offset, size_t slice_size, size_t table_size, _impl::OutputStream& out) const
1594 return m_tree.write(slice_offset, slice_size, table_size, out);
1598 void Column<T>::refresh_accessor_tree(size_t new_col_ndx, const Spec& spec)
1600 m_tree.init_from_parent();
1601 ColumnBaseWithIndex::refresh_accessor_tree(new_col_ndx, spec);
1605 void Column<T>::do_erase(size_t row_ndx, size_t num_rows_to_erase, bool is_last)
1607 if (has_search_index()) {
1608 for (size_t i = num_rows_to_erase; i > 0; --i) {
1609 size_t row_ndx_2 = row_ndx + i - 1;
1610 m_search_index->erase<T>(row_ndx_2, is_last); // Throws
1613 for (size_t i = num_rows_to_erase; i > 0; --i) {
1614 size_t row_ndx_2 = row_ndx + i - 1;
1615 erase_without_updating_index(row_ndx_2, is_last); // Throws
1620 void Column<T>::verify() const
1630 void Column<T>::to_dot(std::ostream& out, StringData title) const
1633 ref_type ref = get_root_array()->get_ref();
1634 out << "subgraph cluster_integer_column" << ref << " {" << std::endl;
1635 out << " label = \"Integer column";
1636 if (title.size() != 0)
1637 out << "\\n'" << title << "'";
1638 out << "\";" << std::endl;
1640 out << "}" << std::endl;
1642 static_cast<void>(out);
1643 static_cast<void>(title);
1648 void Column<T>::leaf_to_dot(MemRef leaf_mem, ArrayParent* parent, size_t ndx_in_parent, std::ostream& out) const
1651 BpTree<T>::leaf_to_dot(leaf_mem, parent, ndx_in_parent, out, get_alloc());
1653 static_cast<void>(leaf_mem);
1654 static_cast<void>(parent);
1655 static_cast<void>(ndx_in_parent);
1656 static_cast<void>(out);
1661 void Column<T>::do_dump_node_structure(std::ostream& out, int level) const
1664 dump_node_structure(*get_root_array(), out, level);
1666 static_cast<void>(out);
1667 static_cast<void>(level);
1674 void Column<T>::tree_to_dot(std::ostream& out) const
1676 ColumnBase::bptree_to_dot(get_root_array(), out);
1681 MemStats Column<T>::stats() const
1684 get_root_array()->stats(mem_stats);
1689 void leaf_dumper(MemRef mem, Allocator& alloc, std::ostream& out, int level);
1693 void Column<T>::dump_node_structure(const Array& root, std::ostream& out, int level)
1695 root.dump_bptree_structure(out, level, &_impl::leaf_dumper);
1701 std::pair<ref_type, size_t> Column<T>::get_to_dot_parent(size_t ndx_in_parent) const
1703 auto root = get_root_array();
1704 if (root->is_inner_bptree_node()) {
1705 std::pair<MemRef, size_t> p = static_cast<const BpTreeNode*>(root)->get_bptree_leaf(ndx_in_parent);
1706 return std::make_pair(p.first.get_ref(), p.second);
1709 return std::make_pair(root->get_ref(), ndx_in_parent);
1713 // LCOV_EXCL_STOP ignore debug functions
1716 template <class ColumnDataType>
1717 ColumnRandIterator<ColumnDataType>::ColumnRandIterator(const Column<ColumnDataType>* src_col, size_t ndx)
1723 template <class ColumnDataType>
1724 bool ColumnRandIterator<ColumnDataType>::operator==(const ColumnRandIterator<ColumnDataType>& rhs) const
1726 return (m_col_ndx == rhs.m_col_ndx);
1729 template <class ColumnDataType>
1730 bool ColumnRandIterator<ColumnDataType>::operator!=(const ColumnRandIterator<ColumnDataType>& rhs) const
1732 return !(*this == rhs);
1735 template <class ColumnDataType>
1736 bool ColumnRandIterator<ColumnDataType>::operator<(const ColumnRandIterator<ColumnDataType>& rhs) const
1738 return m_col_ndx < rhs.m_col_ndx;
1741 template <class ColumnDataType>
1742 bool ColumnRandIterator<ColumnDataType>::operator>(const ColumnRandIterator<ColumnDataType>& rhs) const
1747 template <class ColumnDataType>
1748 bool ColumnRandIterator<ColumnDataType>::operator<=(const ColumnRandIterator<ColumnDataType>& rhs) const
1750 return !(rhs < *this);
1753 template <class ColumnDataType>
1754 bool ColumnRandIterator<ColumnDataType>::operator>=(const ColumnRandIterator<ColumnDataType>& rhs) const
1756 return !(*this < rhs);
1759 template <class ColumnDataType>
1760 ColumnRandIterator<ColumnDataType>& ColumnRandIterator<ColumnDataType>::operator+=(ptrdiff_t movement)
1762 m_col_ndx += movement;
1766 template <class ColumnDataType>
1767 ColumnRandIterator<ColumnDataType>& ColumnRandIterator<ColumnDataType>::operator-=(ptrdiff_t movement)
1769 m_col_ndx -= movement;
1773 template <class ColumnDataType>
1774 ColumnRandIterator<ColumnDataType>& ColumnRandIterator<ColumnDataType>::operator++()
1780 template <class ColumnDataType>
1781 ColumnRandIterator<ColumnDataType>& ColumnRandIterator<ColumnDataType>::operator--()
1787 template <class ColumnDataType>
1788 ColumnRandIterator<ColumnDataType> ColumnRandIterator<ColumnDataType>::operator++(int)
1795 template <class ColumnDataType>
1796 ColumnRandIterator<ColumnDataType> ColumnRandIterator<ColumnDataType>::operator--(int)
1803 template <class ColumnDataType>
1804 ColumnRandIterator<ColumnDataType> ColumnRandIterator<ColumnDataType>::operator+(ptrdiff_t movement)
1806 return ColumnRandIterator(m_col, m_col_ndx + movement);
1809 template <class ColumnDataType>
1810 ColumnRandIterator<ColumnDataType> ColumnRandIterator<ColumnDataType>::operator-(ptrdiff_t movement)
1812 return ColumnRandIterator(m_col, m_col_ndx - movement);
1815 template <class ColumnDataType>
1816 ptrdiff_t ColumnRandIterator<ColumnDataType>::operator-(const ColumnRandIterator<ColumnDataType>& right) const
1818 return m_col_ndx - right.m_col_ndx;
1821 template <class ColumnDataType>
1822 const ColumnDataType ColumnRandIterator<ColumnDataType>::operator*() const
1824 return m_col->get(m_col_ndx);
1827 template <class ColumnDataType>
1828 const ColumnDataType ColumnRandIterator<ColumnDataType>::operator->() const
1830 return m_col->get(m_col_ndx);
1833 template <class ColumnDataType>
1834 const ColumnDataType ColumnRandIterator<ColumnDataType>::operator[](ptrdiff_t offset) const
1836 return m_col->get(m_col_ndx + offset);
1839 template <class ColumnDataType>
1840 size_t ColumnRandIterator<ColumnDataType>::get_col_ndx() const
1845 template <class ColumnDataType>
1846 std::ostream& operator<<(std::ostream& out, const ColumnRandIterator<ColumnDataType>& it)
1848 out << "ColumnRandIterator at index: " << it.get_col_ndx();
1852 } // namespace realm
1854 #endif // REALM_COLUMN_HPP