added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / core / realm / column_table.hpp
1 /*************************************************************************
2  *
3  * Copyright 2016 Realm Inc.
4  *
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
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  *
17  **************************************************************************/
18
19 #ifndef REALM_COLUMN_TABLE_HPP
20 #define REALM_COLUMN_TABLE_HPP
21
22 #include <vector>
23 #include <mutex>
24
25 #include <realm/util/features.h>
26 #include <memory>
27 #include <realm/column.hpp>
28 #include <realm/table.hpp>
29
30 namespace realm {
31
32
33 /// Base class for any type of column that can contain subtables.
34 // FIXME: Don't derive from IntegerColumn, but define a BpTree<ref_type> specialization.
35 class SubtableColumnBase : public IntegerColumn, public Table::Parent {
36 public:
37     void discard_child_accessors() noexcept;
38
39     ~SubtableColumnBase() noexcept override;
40
41     static ref_type create(Allocator&, size_t size = 0);
42
43     TableRef get_subtable_accessor(size_t) const noexcept override;
44
45     void insert_rows(size_t, size_t, size_t, bool) override;
46     void erase_rows(size_t, size_t, size_t, bool) override;
47     void move_last_row_over(size_t, size_t, bool) override;
48     void clear(size_t, bool) override;
49     void swap_rows(size_t, size_t) override;
50     void discard_subtable_accessor(size_t) noexcept override;
51     void update_from_parent(size_t) noexcept override;
52     void adj_acc_insert_rows(size_t, size_t) noexcept override;
53     void adj_acc_erase_row(size_t) noexcept override;
54     void adj_acc_move_over(size_t, size_t) noexcept override;
55     void adj_acc_clear_root_table() noexcept override;
56     void adj_acc_swap_rows(size_t, size_t) noexcept override;
57     void adj_acc_move_row(size_t, size_t) noexcept override;
58     void mark(int) noexcept override;
59     bool supports_search_index() const noexcept override
60     {
61         return false;
62     }
63     StringIndex* create_search_index() override
64     {
65         return nullptr;
66     }
67     bool is_null(size_t ndx) const noexcept override
68     {
69         return get_as_ref(ndx) == 0;
70     }
71
72     void verify() const override;
73     void verify(const Table&, size_t) const override;
74
75 protected:
76     /// A pointer to the table that this column is part of. For a free-standing
77     /// column, this pointer is null.
78     Table* const m_table;
79
80     struct SubtableMap {
81         bool empty() const noexcept
82         {
83             return m_entries.empty();
84         }
85         Table* find(size_t subtable_ndx) const noexcept;
86         void add(size_t subtable_ndx, Table*);
87         // Returns true if, and only if at least one entry was detached and
88         // removed from the map.
89         bool detach_and_remove_all() noexcept;
90         // Returns true if, and only if the entry was found and removed, and it
91         // was the last entry in the map.
92         bool detach_and_remove(size_t subtable_ndx) noexcept;
93         // Returns true if, and only if the entry was found and removed, and it
94         // was the last entry in the map.
95         bool remove(Table*) noexcept;
96         void update_from_parent(size_t old_baseline) const noexcept;
97         template <bool fix_ndx_in_parent>
98         void adj_insert_rows(size_t row_ndx, size_t num_rows_inserted) noexcept;
99         // Returns true if, and only if an entry was found and removed, and it
100         // was the last entry in the map.
101         template <bool fix_ndx_in_parent>
102         bool adj_erase_rows(size_t row_ndx, size_t num_rows_erased) noexcept;
103         // Returns true if, and only if an entry was found and removed, and it
104         // was the last entry in the map.
105         template <bool fix_ndx_in_parent>
106         bool adj_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept;
107         template <bool fix_ndx_in_parent>
108         void adj_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept;
109         template <bool fix_ndx_in_parent>
110         void adj_move_row(size_t from_ndx, size_t to_ndx) noexcept;
111         void adj_set_null(size_t row_ndx) noexcept;
112
113         void update_accessors(const size_t* col_path_begin, const size_t* col_path_end,
114                               _impl::TableFriend::AccessorUpdater&);
115         void recursive_mark() noexcept;
116         void refresh_accessor_tree();
117         void verify(const SubtableColumn& parent);
118
119     private:
120         struct SubtableEntry {
121             size_t m_subtable_ndx;
122             Table* m_table;
123         };
124         typedef std::vector<SubtableEntry> entries;
125         entries m_entries;
126     };
127
128     /// Contains all existing accessors that are attached to a subtable in this
129     /// column. It can map a row index into a pointer to the corresponding
130     /// accessor when it exists.
131     ///
132     /// There is an invariant in force: Either `m_table` is null, or there is an
133     /// additional referece count on `*m_table` when, and only when the map is
134     /// non-empty.
135     mutable SubtableMap m_subtable_map;
136     mutable std::recursive_mutex m_subtable_map_lock;
137
138     SubtableColumnBase(Allocator&, ref_type, Table*, size_t column_ndx);
139
140     /// Get a TableRef to the accessor of the specified subtable. The
141     /// accessor will be created if it does not already exist.
142     ///
143     /// NOTE: This method must be used only for subtables with
144     /// independent specs, i.e. for elements of a MixedColumn.
145     TableRef get_subtable_tableref(size_t subtable_ndx);
146
147     // Overriding method in ArrayParent
148     void update_child_ref(size_t, ref_type) override;
149
150     // Overriding method in ArrayParent
151     ref_type get_child_ref(size_t) const noexcept override;
152
153     // Overriding method in Table::Parent
154     Table* get_parent_table(size_t*) noexcept override;
155
156     // Overriding method in Table::Parent
157     void child_accessor_destroyed(Table*) noexcept override;
158
159     // Overriding method in Table::Parent
160     std::recursive_mutex* get_accessor_management_lock() noexcept override
161     { return &m_subtable_map_lock; }
162
163     /// Assumes that the two tables have the same spec.
164     static bool compare_subtable_rows(const Table&, const Table&);
165
166     /// Construct a copy of the columns array of the specified table
167     /// and return just the ref to that array.
168     ///
169     /// In the clone, no string column will be of the enumeration
170     /// type.
171     ref_type clone_table_columns(const Table*);
172
173     size_t* record_subtable_path(size_t* begin, size_t* end) noexcept override;
174
175     void update_table_accessors(const size_t* col_path_begin, const size_t* col_path_end,
176                                 _impl::TableFriend::AccessorUpdater&);
177
178     /// \param row_ndx Must be `realm::npos` if appending.
179     /// \param value The value to place in any newly created rows.
180     /// \param num_rows The number of rows to insert.
181     void do_insert(size_t row_ndx, int_fast64_t value, size_t num_rows);
182
183     std::pair<ref_type, size_t> get_to_dot_parent(size_t ndx_in_parent) const override;
184
185     friend class Table;
186 };
187
188
189 class SubtableColumn : public SubtableColumnBase {
190 public:
191     using value_type = ConstTableRef;
192     /// Create a subtable column accessor and attach it to a
193     /// preexisting underlying structure of arrays.
194     ///
195     /// \param alloc The allocator to provide new memory.
196     ///
197     /// \param ref The memory reference of the underlying subtable that
198     /// we are creating an accessor for.
199     ///
200     /// \param table If this column is used as part of a table you must
201     /// pass a pointer to that table. Otherwise you must pass null.
202     ///
203     /// \param column_ndx If this column is used as part of a table
204     /// you must pass the logical index of the column within that
205     /// table. Otherwise you should pass zero.
206     SubtableColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx);
207
208     ~SubtableColumn() noexcept override
209     {
210     }
211
212     // Overriding method in Table::Parent
213     Spec* get_subtable_spec() noexcept override;
214
215     size_t get_subtable_size(size_t ndx) const noexcept;
216
217     /// Get a TableRef to the accessor of the specified subtable. The
218     /// accessor will be created if it does not already exist.
219     TableRef get_subtable_tableref(size_t subtable_ndx);
220
221     ConstTableRef get_subtable_tableref(size_t subtable_ndx) const;
222
223     /// This is to be used by the query system that does not need to
224     /// modify the subtable. Will return a ref object containing a
225     /// nullptr if there is no table object yet.
226     ConstTableRef get(size_t subtable_ndx) const
227     {
228         int64_t ref = IntegerColumn::get(subtable_ndx);
229         if (ref)
230             return get_subtable_tableref(subtable_ndx);
231         else
232             return {};
233     }
234
235     // When passing a table to add() or insert() it is assumed that
236     // the table spec is compatible with this column. The number of
237     // columns must be the same, and the corresponding columns must
238     // have the same data type (as returned by
239     // Table::get_column_type()).
240
241     void add(const Table* value = nullptr);
242     void insert(size_t ndx, const Table* value = nullptr);
243     void set(size_t ndx, const Table*);
244     void clear_table(size_t ndx);
245     void set_null(size_t ndx) override;
246
247     using SubtableColumnBase::insert;
248
249     void erase_rows(size_t, size_t, size_t, bool) override;
250     void move_last_row_over(size_t, size_t, bool) override;
251
252     /// Compare two subtable columns for equality.
253     bool compare_table(const SubtableColumn&) const;
254
255     void refresh_accessor_tree(size_t, const Spec&) override;
256     void refresh_subtable_map();
257
258 #ifdef REALM_DEBUG
259     void verify(const Table&, size_t) const override;
260     void do_dump_node_structure(std::ostream&, int) const override;
261     void to_dot(std::ostream&, StringData title) const override;
262 #endif
263
264 private:
265     mutable size_t m_subspec_ndx; // Unknown if equal to `npos`
266
267     size_t get_subspec_ndx() const noexcept;
268
269     void destroy_subtable(size_t ndx) noexcept;
270
271     void do_discard_child_accessors() noexcept override;
272 };
273
274
275 // Implementation
276
277 // Overriding virtual method of Column.
278 inline void SubtableColumnBase::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool)
279 {
280     REALM_ASSERT_DEBUG(prior_num_rows == size());
281     REALM_ASSERT(row_ndx <= prior_num_rows);
282
283     size_t row_ndx_2 = (row_ndx == prior_num_rows ? realm::npos : row_ndx);
284     int_fast64_t value = 0;
285     do_insert(row_ndx_2, value, num_rows_to_insert); // Throws
286 }
287
288 // Overriding virtual method of Column.
289 inline void SubtableColumnBase::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows,
290                                            bool broken_reciprocal_backlinks)
291 {
292     IntegerColumn::erase_rows(row_ndx, num_rows_to_erase, prior_num_rows, broken_reciprocal_backlinks); // Throws
293
294     std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
295     const bool fix_ndx_in_parent = true;
296     bool last_entry_removed = m_subtable_map.adj_erase_rows<fix_ndx_in_parent>(row_ndx, num_rows_to_erase);
297     typedef _impl::TableFriend tf;
298     if (last_entry_removed)
299         tf::unbind_ptr(*m_table);
300 }
301
302 // Overriding virtual method of Column.
303 inline void SubtableColumnBase::move_last_row_over(size_t row_ndx, size_t prior_num_rows,
304                                                    bool broken_reciprocal_backlinks)
305 {
306     IntegerColumn::move_last_row_over(row_ndx, prior_num_rows, broken_reciprocal_backlinks); // Throws
307
308     std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
309     const bool fix_ndx_in_parent = true;
310     size_t last_row_ndx = prior_num_rows - 1;
311     bool last_entry_removed = m_subtable_map.adj_move_over<fix_ndx_in_parent>(last_row_ndx, row_ndx);
312     typedef _impl::TableFriend tf;
313     if (last_entry_removed)
314         tf::unbind_ptr(*m_table);
315 }
316
317 inline void SubtableColumnBase::clear(size_t, bool)
318 {
319     discard_child_accessors();
320     clear_without_updating_index(); // Throws
321     // FIXME: This one is needed because
322     // IntegerColumn::clear_without_updating_index() forgets about the
323     // leaf type. A better solution should probably be sought after.
324     get_root_array()->set_type(Array::type_HasRefs);
325 }
326
327 inline void SubtableColumnBase::swap_rows(size_t row_ndx_1, size_t row_ndx_2)
328 {
329     IntegerColumn::swap_rows(row_ndx_1, row_ndx_2); // Throws
330
331     std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
332     const bool fix_ndx_in_parent = true;
333     m_subtable_map.adj_swap_rows<fix_ndx_in_parent>(row_ndx_1, row_ndx_2);
334 }
335
336 inline void SubtableColumnBase::mark(int type) noexcept
337 {
338     if (type & mark_Recursive) {
339         std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
340         m_subtable_map.recursive_mark();
341     }
342 }
343
344 inline void SubtableColumnBase::adj_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept
345 {
346     // This function must assume no more than minimal consistency of the
347     // accessor hierarchy. This means in particular that it cannot access the
348     // underlying node structure. See AccessorConsistencyLevels.
349
350     std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
351     const bool fix_ndx_in_parent = false;
352     m_subtable_map.adj_insert_rows<fix_ndx_in_parent>(row_ndx, num_rows);
353 }
354
355 inline void SubtableColumnBase::adj_acc_erase_row(size_t row_ndx) noexcept
356 {
357     // This function must assume no more than minimal consistency of the
358     // accessor hierarchy. This means in particular that it cannot access the
359     // underlying node structure. See AccessorConsistencyLevels.
360
361     std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
362     const bool fix_ndx_in_parent = false;
363     size_t num_rows_erased = 1;
364     bool last_entry_removed = m_subtable_map.adj_erase_rows<fix_ndx_in_parent>(row_ndx, num_rows_erased);
365     typedef _impl::TableFriend tf;
366     if (last_entry_removed)
367         tf::unbind_ptr(*m_table);
368 }
369
370 inline void SubtableColumnBase::adj_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept
371 {
372     // This function must assume no more than minimal consistency of the
373     // accessor hierarchy. This means in particular that it cannot access the
374     // underlying node structure. See AccessorConsistencyLevels.
375
376     std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
377     const bool fix_ndx_in_parent = false;
378     bool last_entry_removed = m_subtable_map.adj_move_over<fix_ndx_in_parent>(from_row_ndx, to_row_ndx);
379     typedef _impl::TableFriend tf;
380     if (last_entry_removed)
381         tf::unbind_ptr(*m_table);
382 }
383
384 inline void SubtableColumnBase::adj_acc_clear_root_table() noexcept
385 {
386     // This function must assume no more than minimal consistency of the
387     // accessor hierarchy. This means in particular that it cannot access the
388     // underlying node structure. See AccessorConsistencyLevels.
389
390     IntegerColumn::adj_acc_clear_root_table();
391     discard_child_accessors();
392 }
393
394 inline void SubtableColumnBase::adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept
395 {
396     std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
397     const bool fix_ndx_in_parent = false;
398     m_subtable_map.adj_swap_rows<fix_ndx_in_parent>(row_ndx_1, row_ndx_2);
399 }
400
401 inline void SubtableColumnBase::adj_acc_move_row(size_t from_ndx, size_t to_ndx) noexcept
402 {
403     std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
404     const bool fix_ndx_in_parent = false;
405     m_subtable_map.adj_move_row<fix_ndx_in_parent>(from_ndx, to_ndx);
406 }
407
408 inline TableRef SubtableColumnBase::get_subtable_accessor(size_t row_ndx) const noexcept
409 {
410     // This function must assume no more than minimal consistency of the
411     // accessor hierarchy. This means in particular that it cannot access the
412     // underlying node structure. See AccessorConsistencyLevels.
413     std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
414     TableRef subtable(m_subtable_map.find(row_ndx));
415     return subtable;
416 }
417
418 inline void SubtableColumnBase::discard_subtable_accessor(size_t row_ndx) noexcept
419 {
420     // This function must assume no more than minimal consistency of the
421     // accessor hierarchy. This means in particular that it cannot access the
422     // underlying node structure. See AccessorConsistencyLevels.
423
424     std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
425     bool last_entry_removed = m_subtable_map.detach_and_remove(row_ndx);
426     typedef _impl::TableFriend tf;
427     if (last_entry_removed)
428         tf::unbind_ptr(*m_table);
429 }
430
431 inline void SubtableColumnBase::SubtableMap::add(size_t subtable_ndx, Table* table)
432 {
433     SubtableEntry e;
434     e.m_subtable_ndx = subtable_ndx;
435     e.m_table = table;
436     m_entries.push_back(e);
437 }
438
439 template <bool fix_ndx_in_parent>
440 void SubtableColumnBase::SubtableMap::adj_insert_rows(size_t row_ndx, size_t num_rows_inserted) noexcept
441 {
442     for (auto& entry : m_entries) {
443         if (entry.m_subtable_ndx >= row_ndx) {
444             entry.m_subtable_ndx += num_rows_inserted;
445             typedef _impl::TableFriend tf;
446             if (fix_ndx_in_parent)
447                 tf::set_ndx_in_parent(*(entry.m_table), entry.m_subtable_ndx);
448         }
449     }
450 }
451
452 template <bool fix_ndx_in_parent>
453 bool SubtableColumnBase::SubtableMap::adj_erase_rows(size_t row_ndx, size_t num_rows_erased) noexcept
454 {
455     if (m_entries.empty())
456         return false;
457     typedef _impl::TableFriend tf;
458     auto end = m_entries.end();
459     auto i = m_entries.begin();
460     do {
461         if (i->m_subtable_ndx >= row_ndx + num_rows_erased) {
462             i->m_subtable_ndx -= num_rows_erased;
463             if (fix_ndx_in_parent)
464                 tf::set_ndx_in_parent(*(i->m_table), i->m_subtable_ndx);
465         }
466         else if (i->m_subtable_ndx >= row_ndx) {
467             // Must hold a counted reference while detaching
468             TableRef table(i->m_table);
469             tf::detach(*table);
470             // Move last over
471             *i = *--end;
472             continue;
473         }
474         ++i;
475     } while (i != end);
476     m_entries.erase(end, m_entries.end());
477     return m_entries.empty();
478 }
479
480
481 template <bool fix_ndx_in_parent>
482 bool SubtableColumnBase::SubtableMap::adj_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept
483 {
484     typedef _impl::TableFriend tf;
485
486     size_t i = 0, n = m_entries.size();
487     // We return true if, and only if we remove the last entry in the map.  We
488     // need special handling for the case, where the set of entries are already
489     // empty, otherwise the final return statement would return true in this
490     // case, even though we didn't actually remove an entry.
491     if (n == 0)
492         return false;
493
494     while (i < n) {
495         SubtableEntry& e = m_entries[i];
496         if (REALM_UNLIKELY(e.m_subtable_ndx == to_row_ndx)) {
497             // Must hold a counted reference while detaching
498             TableRef table(e.m_table);
499             tf::detach(*table);
500             // Delete entry by moving last over (faster and avoids invalidating
501             // iterators)
502             e = m_entries[--n];
503             m_entries.pop_back();
504         }
505         else {
506             if (REALM_UNLIKELY(e.m_subtable_ndx == from_row_ndx)) {
507                 e.m_subtable_ndx = to_row_ndx;
508                 if (fix_ndx_in_parent)
509                     tf::set_ndx_in_parent(*(e.m_table), e.m_subtable_ndx);
510             }
511             ++i;
512         }
513     }
514     return m_entries.empty();
515 }
516
517 template <bool fix_ndx_in_parent>
518 void SubtableColumnBase::SubtableMap::adj_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept
519 {
520     using tf = _impl::TableFriend;
521     for (auto& entry : m_entries) {
522         if (REALM_UNLIKELY(entry.m_subtable_ndx == row_ndx_1)) {
523             entry.m_subtable_ndx = row_ndx_2;
524             if (fix_ndx_in_parent)
525                 tf::set_ndx_in_parent(*(entry.m_table), entry.m_subtable_ndx);
526         }
527         else if (REALM_UNLIKELY(entry.m_subtable_ndx == row_ndx_2)) {
528             entry.m_subtable_ndx = row_ndx_1;
529             if (fix_ndx_in_parent)
530                 tf::set_ndx_in_parent(*(entry.m_table), entry.m_subtable_ndx);
531         }
532     }
533 }
534
535
536 template <bool fix_ndx_in_parent>
537 void SubtableColumnBase::SubtableMap::adj_move_row(size_t from_ndx, size_t to_ndx) noexcept
538 {
539     using tf = _impl::TableFriend;
540     for (auto& entry : m_entries) {
541         if (entry.m_subtable_ndx == from_ndx) {
542             entry.m_subtable_ndx = to_ndx;
543             if (fix_ndx_in_parent)
544                 tf::set_ndx_in_parent(*(entry.m_table), entry.m_subtable_ndx);
545         }
546         else {
547             if (from_ndx < to_ndx) {
548                 // shift the range (from, to] down one
549                 if (entry.m_subtable_ndx <= to_ndx && entry.m_subtable_ndx > from_ndx) {
550                     entry.m_subtable_ndx--;
551                     if (fix_ndx_in_parent) {
552                         tf::set_ndx_in_parent(*(entry.m_table), entry.m_subtable_ndx);
553                     }
554                 }
555             } else if (from_ndx > to_ndx) {
556                 // shift the range (from, to] up one
557                 if (entry.m_subtable_ndx >= to_ndx && entry.m_subtable_ndx < from_ndx) {
558                     entry.m_subtable_ndx++;
559                     if (fix_ndx_in_parent) {
560                         tf::set_ndx_in_parent(*(entry.m_table), entry.m_subtable_ndx);
561                     }
562                 }
563             }
564         }
565     }
566 }
567
568 inline void SubtableColumnBase::SubtableMap::adj_set_null(size_t row_ndx) noexcept
569 {
570     Table* table = find(row_ndx);
571     if (table)
572         _impl::TableFriend::refresh_accessor_tree(*table);
573 }
574
575 inline SubtableColumnBase::SubtableColumnBase(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx)
576     : IntegerColumn(alloc, ref, column_ndx) // Throws
577     , m_table(table)
578 {
579 }
580
581 inline void SubtableColumnBase::update_child_ref(size_t child_ndx, ref_type new_ref)
582 {
583     set_as_ref(child_ndx, new_ref);
584 }
585
586 inline ref_type SubtableColumnBase::get_child_ref(size_t child_ndx) const noexcept
587 {
588     return get_as_ref(child_ndx);
589 }
590
591 inline void SubtableColumnBase::discard_child_accessors() noexcept
592 {
593     std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
594     bool last_entry_removed = m_subtable_map.detach_and_remove_all();
595     if (last_entry_removed && m_table)
596         _impl::TableFriend::unbind_ptr(*m_table);
597 }
598
599 inline SubtableColumnBase::~SubtableColumnBase() noexcept
600 {
601     discard_child_accessors();
602 }
603
604 inline bool SubtableColumnBase::compare_subtable_rows(const Table& a, const Table& b)
605 {
606     return _impl::TableFriend::compare_rows(a, b);
607 }
608
609 inline ref_type SubtableColumnBase::clone_table_columns(const Table* t)
610 {
611     return _impl::TableFriend::clone_columns(*t, get_root_array()->get_alloc());
612 }
613
614 inline ref_type SubtableColumnBase::create(Allocator& alloc, size_t size)
615 {
616     return IntegerColumn::create(alloc, Array::type_HasRefs, size); // Throws
617 }
618
619 inline size_t* SubtableColumnBase::record_subtable_path(size_t* begin, size_t* end) noexcept
620 {
621     if (end == begin)
622         return 0; // Error, not enough space in buffer
623     *begin++ = get_column_index();
624     if (end == begin)
625         return 0; // Error, not enough space in buffer
626     return _impl::TableFriend::record_subtable_path(*m_table, begin, end);
627 }
628
629 inline void SubtableColumnBase::update_table_accessors(const size_t* col_path_begin, const size_t* col_path_end,
630                                                        _impl::TableFriend::AccessorUpdater& updater)
631 {
632     // This function must assume no more than minimal consistency of the
633     // accessor hierarchy. This means in particular that it cannot access the
634     // underlying node structure. See AccessorConsistencyLevels.
635
636     m_subtable_map.update_accessors(col_path_begin, col_path_end, updater); // Throws
637 }
638
639 inline void SubtableColumnBase::do_insert(size_t row_ndx, int_fast64_t value, size_t num_rows)
640 {
641     IntegerColumn::insert_without_updating_index(row_ndx, value, num_rows); // Throws
642     bool is_append = row_ndx == realm::npos;
643     if (!is_append) {
644         const bool fix_ndx_in_parent = true;
645         m_subtable_map.adj_insert_rows<fix_ndx_in_parent>(row_ndx, num_rows);
646     }
647 }
648
649
650 inline SubtableColumn::SubtableColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx)
651     : SubtableColumnBase(alloc, ref, table, column_ndx)
652     , m_subspec_ndx(realm::npos)
653 {
654 }
655
656 inline ConstTableRef SubtableColumn::get_subtable_tableref(size_t subtable_ndx) const
657 {
658     return const_cast<SubtableColumn*>(this)->get_subtable_tableref(subtable_ndx);
659 }
660
661 inline void SubtableColumn::refresh_accessor_tree(size_t col_ndx, const Spec& spec)
662 {
663     SubtableColumnBase::refresh_accessor_tree(col_ndx, spec); // Throws
664     m_subspec_ndx = spec.get_subspec_ndx(col_ndx);
665     std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
666     m_subtable_map.refresh_accessor_tree(); // Throws
667 }
668
669 inline void SubtableColumn::refresh_subtable_map()
670 {
671     std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
672     m_subtable_map.refresh_accessor_tree(); // Throws
673 }
674
675 inline size_t SubtableColumn::get_subspec_ndx() const noexcept
676 {
677     if (REALM_UNLIKELY(m_subspec_ndx == realm::npos)) {
678         typedef _impl::TableFriend tf;
679         m_subspec_ndx = tf::get_spec(*m_table).get_subspec_ndx(get_column_index());
680     }
681     return m_subspec_ndx;
682 }
683
684 inline Spec* SubtableColumn::get_subtable_spec() noexcept
685 {
686     typedef _impl::TableFriend tf;
687     return tf::get_spec(*m_table).get_subtable_spec(get_column_index());
688 }
689
690
691 } // namespace realm
692
693 #endif // REALM_COLUMN_TABLE_HPP