column.hpp
46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
/*************************************************************************
*
* REALM CONFIDENTIAL
* __________________
*
* [2011] - [2015] Realm Inc
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Realm Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Realm Incorporated
* and its suppliers and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Realm Incorporated.
*
**************************************************************************/
#ifndef REALM_COLUMN_HPP
#define REALM_COLUMN_HPP
#include <stdint.h> // unint8_t etc
#include <cstdlib> // size_t
#include <vector>
#include <memory>
#include <realm/array_integer.hpp>
#include <realm/column_type.hpp>
#include <realm/column_fwd.hpp>
#include <realm/spec.hpp>
#include <realm/impl/output_stream.hpp>
#include <realm/query_conditions.hpp>
#include <realm/bptree.hpp>
#include <realm/index_string.hpp>
#include <realm/impl/destroy_guard.hpp>
#include <realm/exceptions.hpp>
namespace realm {
// Pre-definitions
struct CascadeState;
class StringIndex;
template<class T>
struct ImplicitNull;
template<class T>
struct ImplicitNull<util::Optional<T>> {
static constexpr bool value = true;
};
template<>
struct ImplicitNull<int64_t> {
static constexpr bool value = false;
};
template<>
struct ImplicitNull<float> {
static constexpr bool value = true;
};
template<>
struct ImplicitNull<double> {
static constexpr bool value = true;
};
// FIXME: Add specialization for ImplicitNull for float, double, StringData, BinaryData.
struct ColumnTemplateBase
{
virtual int compare_values(size_t row1, size_t row2) const = 0;
};
template<class T, class R, Action action, class Condition, class ColType>
R aggregate(const ColType& column, T target, size_t start, size_t end,
size_t limit, size_t* return_ndx);
template<class T>
struct ColumnTemplate : public ColumnTemplateBase
{
// Overridden in column_string.* because == operator of StringData isn't yet locale aware; todo
virtual int compare_values(size_t row1, size_t row2) const
{
// we negate nullability such that the two ternary statements in this method can look identical to reduce
// risk of bugs
bool v1 = !is_null(row1);
bool v2 = !is_null(row2);
if (!v1 || !v2)
return v1 == v2 ? 0 : v1 < v2 ? 1 : -1;
T a = get_val(row1);
T b = get_val(row2);
return a == b ? 0 : a < b ? 1 : -1;
}
// We cannot use already-existing get() methods because StringEnumColumn and LinkList inherit from
// Column and overload get() with different return type than int64_t. Todo, find a way to simplify
virtual T get_val(size_t row) const = 0;
virtual bool is_null(size_t row) const = 0;
};
/// Base class for all column types.
class ColumnBase {
public:
/// Get the number of entries in this column. This operation is relatively
/// slow.
virtual size_t size() const noexcept = 0;
/// \throw LogicError Thrown if this column is not string valued.
virtual void set_string(size_t row_ndx, StringData value);
/// Whether or not this column is nullable.
virtual bool is_nullable() const noexcept;
/// Whether or not the value at \a row_ndx is NULL. If the column is not
/// nullable, always returns false.
virtual bool is_null(size_t row_ndx) const noexcept;
/// Sets the value at \a row_ndx to be NULL.
/// \throw LogicError Thrown if this column is not nullable.
virtual void set_null(size_t row_ndx);
//@{
/// `insert_rows()` inserts the specified number of elements into this column
/// starting at the specified row index. The new elements will have the
/// default value for the column type.
///
/// `erase_rows()` removes the specified number of consecutive elements from
/// this column, starting at the specified row index.
///
/// `move_last_row_over()` removes the element at the specified row index by
/// moving the element at the last row index over it. This reduces the
/// number of elements by one.
///
/// \param prior_num_rows The number of elements in this column prior to the
/// modification.
///
/// \param broken_reciprocal_backlinks If true, link columns must assume
/// that reciprocal backlinks have already been removed. Non-link columns
/// should ignore this argument.
virtual void insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool nullable) = 0;
virtual void erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows,
bool broken_reciprocal_backlinks) = 0;
virtual void move_last_row_over(size_t row_ndx, size_t prior_num_rows,
bool broken_reciprocal_backlinks) = 0;
//@}
/// Remove all elements from this column.
///
/// \param num_rows The total number of rows in this column.
///
/// \param broken_reciprocal_backlinks If true, link columns must assume
/// that reciprocal backlinks have already been removed. Non-link columns
/// should ignore this argument.
virtual void clear(size_t num_rows, bool broken_reciprocal_backlinks) = 0;
/// \brief Swap the elements at the specified indices.
///
/// Behaviour is undefined if:
/// - \a row_ndx_1 or \a row_ndx_2 point to an invalid element (out-of
/// bounds)
/// - \a row_ndx_1 and \a row_ndx_2 point to the same value
virtual void swap_rows(size_t row_ndx_1, size_t row_ndx_2) = 0;
virtual void destroy() noexcept = 0;
void move_assign(ColumnBase& col) noexcept;
virtual ~ColumnBase() noexcept {}
// Getter function for index. For integer index, the caller must supply a buffer that we can store the
// extracted value in (it may be bitpacked, so we cannot return a pointer in to the Array as we do with
// String index).
virtual StringData get_index_data(size_t, StringIndex::StringConversionBuffer& buffer) const noexcept = 0;
// Search index
virtual bool has_search_index() const noexcept;
virtual StringIndex* create_search_index();
virtual void destroy_search_index() noexcept;
virtual const StringIndex* get_search_index() const noexcept;
virtual StringIndex* get_search_index() noexcept;
virtual void set_search_index_ref(ref_type, ArrayParent*, size_t ndx_in_parent,
bool allow_duplicate_values);
virtual void set_search_index_allow_duplicate_values(bool) noexcept;
virtual Allocator& get_alloc() const noexcept = 0;
/// Returns the 'ref' of the root array.
virtual ref_type get_ref() const noexcept = 0;
virtual MemRef get_mem() const noexcept = 0;
virtual void replace_root_array(std::unique_ptr<Array> leaf) = 0;
virtual MemRef clone_deep(Allocator& alloc) const = 0;
virtual void detach(void) = 0;
virtual bool is_attached(void) const noexcept = 0;
static size_t get_size_from_type_and_ref(ColumnType, ref_type, Allocator&) noexcept;
// These assume that the right column compile-time type has been
// figured out.
static size_t get_size_from_ref(ref_type root_ref, Allocator&);
static size_t get_size_from_ref(ref_type spec_ref, ref_type columns_ref, Allocator&);
/// Write a slice of this column to the specified output stream.
virtual ref_type write(size_t slice_offset, size_t slice_size,
size_t table_size, _impl::OutputStream&) const = 0;
virtual void set_parent(ArrayParent*, size_t ndx_in_parent) noexcept = 0;
virtual size_t get_ndx_in_parent() const noexcept = 0;
virtual void set_ndx_in_parent(size_t ndx_in_parent) noexcept = 0;
/// Called to update refs and memory pointers of this column accessor and
/// all its nested accessors, but only in cases where the logical contents
/// in strictly unchanged. Group::commit(), and
/// SharedGroup::commit_and_continue_as_read()() are examples of such
/// cases. In both those cases, the purpose is to keep user visible
/// accessors in a valid state across a commit.
virtual void update_from_parent(size_t old_baseline) noexcept = 0;
//@{
/// cascade_break_backlinks_to() is called iteratively for each column by
/// Table::cascade_break_backlinks_to() with the same arguments as are
/// passed to Table::cascade_break_backlinks_to(). Link columns must
/// override it. The same is true for cascade_break_backlinks_to_all_rows(),
/// except that it is called from
/// Table::cascade_break_backlinks_to_all_rows(), and that it expects
/// Table::cascade_break_backlinks_to_all_rows() to pass the number of rows
/// in the table as \a num_rows.
virtual void cascade_break_backlinks_to(size_t row_ndx, CascadeState&);
virtual void cascade_break_backlinks_to_all_rows(size_t num_rows, CascadeState&);
//@}
void discard_child_accessors() noexcept;
/// For columns that are able to contain subtables, this function returns
/// the pointer to the subtable accessor at the specified row index if it
/// exists, otherwise it returns null. For other column types, this function
/// returns null.
virtual Table* get_subtable_accessor(size_t row_ndx) const noexcept;
/// Detach and remove the subtable accessor at the specified row if it
/// exists. For column types that are unable to contain subtable, this
/// function does nothing.
virtual void discard_subtable_accessor(size_t row_ndx) noexcept;
virtual void adj_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept;
virtual void adj_acc_erase_row(size_t row_ndx) noexcept;
/// See Table::adj_acc_move_over()
virtual void adj_acc_move_over(size_t from_row_ndx,
size_t to_row_ndx) noexcept;
virtual void adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept;
virtual void adj_acc_clear_root_table() noexcept;
enum {
mark_Recursive = 0x01,
mark_LinkTargets = 0x02,
mark_LinkOrigins = 0x04
};
virtual void mark(int type) noexcept;
virtual void bump_link_origin_table_version() noexcept;
/// Refresh the dirty part of the accessor subtree rooted at this column
/// accessor.
///
/// The following conditions are necessary and sufficient for the proper
/// operation of this function:
///
/// - The parent table accessor (excluding its column accessors) is in a
/// valid state (already refreshed).
///
/// - Every subtable accessor in the subtree is marked dirty if it needs to
/// be refreshed, or if it has a descendant accessor that needs to be
/// refreshed.
///
/// - This column accessor, as well as all its descendant accessors, are in
/// structural correspondence with the underlying node hierarchy whose
/// root ref is stored in the parent (`Table::m_columns`) (see
/// AccessorConsistencyLevels).
///
/// - The 'index in parent' property of the cached root array
/// (`root->m_ndx_in_parent`) is valid.
virtual void refresh_accessor_tree(size_t new_col_ndx, const Spec&) = 0;
#ifdef REALM_DEBUG
// Must be upper case to avoid conflict with macro in Objective-C
virtual void verify() const = 0;
virtual void verify(const Table&, size_t col_ndx) const;
virtual void to_dot(std::ostream&, StringData title = StringData()) const = 0;
void dump_node_structure() const; // To std::cerr (for GDB)
virtual void do_dump_node_structure(std::ostream&, int level) const = 0;
void bptree_to_dot(const Array* root, std::ostream& out) const;
#endif
protected:
using SliceHandler = BpTreeBase::SliceHandler;
ColumnBase() {}
ColumnBase(ColumnBase&&) = default;
// Must not assume more than minimal consistency (see
// AccessorConsistencyLevels).
virtual void do_discard_child_accessors() noexcept {}
//@{
/// \tparam L Any type with an appropriate `value_type`, %size(),
/// and %get() members.
template<class L, class T>
size_t lower_bound(const L& list, T value) const noexcept;
template<class L, class T>
size_t upper_bound(const L& list, T value) const noexcept;
//@}
// Node functions
class CreateHandler {
public:
virtual ref_type create_leaf(size_t size) = 0;
~CreateHandler() noexcept {}
};
static ref_type create(Allocator&, size_t size, CreateHandler&);
#ifdef REALM_DEBUG
class LeafToDot;
virtual void leaf_to_dot(MemRef, ArrayParent*, size_t ndx_in_parent,
std::ostream&) const = 0;
#endif
private:
class WriteSliceHandler;
static ref_type build(size_t* rest_size_ptr, size_t fixed_height,
Allocator&, CreateHandler&);
};
// FIXME: Temporary class until all column types have been migrated to use BpTree interface
class ColumnBaseSimple : public ColumnBase {
public:
//@{
/// Returns the array node at the root of this column, but note
/// that there is no guarantee that this node is an inner B+-tree
/// node or a leaf. This is the case for a MixedColumn in
/// particular.
Array* get_root_array() noexcept { return m_array.get(); }
const Array* get_root_array() const noexcept { return m_array.get(); }
//@}
Allocator& get_alloc() const noexcept final { return m_array->get_alloc(); }
void destroy() noexcept override { if (m_array) m_array->destroy_deep(); }
ref_type get_ref() const noexcept final { return m_array->get_ref(); }
MemRef get_mem() const noexcept final { return m_array->get_mem(); }
void detach() noexcept final { m_array->detach(); }
bool is_attached() const noexcept final { return m_array->is_attached(); }
void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept final { m_array->set_parent(parent, ndx_in_parent); }
size_t get_ndx_in_parent() const noexcept final { return m_array->get_ndx_in_parent(); }
void set_ndx_in_parent(size_t ndx_in_parent) noexcept final { m_array->set_ndx_in_parent(ndx_in_parent); }
void update_from_parent(size_t old_baseline) noexcept override { m_array->update_from_parent(old_baseline); }
MemRef clone_deep(Allocator& alloc) const override { return m_array->clone_deep(alloc); }
protected:
ColumnBaseSimple() {}
ColumnBaseSimple(Array* root) : m_array(root) {}
std::unique_ptr<Array> m_array;
void replace_root_array(std::unique_ptr<Array> new_root) final;
bool root_is_leaf() const noexcept { return !m_array->is_inner_bptree_node(); }
/// Introduce a new root node which increments the height of the
/// tree by one.
void introduce_new_root(ref_type new_sibling_ref, Array::TreeInsertBase& state,
bool is_append);
static ref_type write(const Array* root, size_t slice_offset, size_t slice_size,
size_t table_size, SliceHandler&, _impl::OutputStream&);
#if defined(REALM_DEBUG)
void tree_to_dot(std::ostream&) const;
#endif
};
class ColumnBaseWithIndex : public ColumnBase {
public:
~ColumnBaseWithIndex() noexcept override {}
void set_ndx_in_parent(size_t ndx) noexcept override;
void update_from_parent(size_t old_baseline) noexcept override;
void refresh_accessor_tree(size_t, const Spec&) override;
void move_assign(ColumnBaseWithIndex& col) noexcept;
void destroy() noexcept override;
bool has_search_index() const noexcept final { return bool(m_search_index); }
StringIndex* get_search_index() noexcept final { return m_search_index.get(); }
const StringIndex* get_search_index() const noexcept final { return m_search_index.get(); }
void destroy_search_index() noexcept override;
void set_search_index_ref(ref_type ref, ArrayParent* parent,
size_t ndx_in_parent, bool allow_duplicate_valaues) final;
StringIndex* create_search_index() override = 0;
protected:
ColumnBaseWithIndex() {}
ColumnBaseWithIndex(ColumnBaseWithIndex&&) = default;
std::unique_ptr<StringIndex> m_search_index;
};
/// A column (Column) is a single B+-tree, and the root of
/// the column is the root of the B+-tree. All leaf nodes are arrays.
template<class T>
class Column : public ColumnBaseWithIndex, public ColumnTemplate<T> {
public:
using value_type = T;
using LeafInfo = typename BpTree<T>::LeafInfo;
using LeafType = typename BpTree<T>::LeafType;
static constexpr bool nullable = ImplicitNull<T>::value;
struct unattached_root_tag {};
explicit Column() noexcept : m_tree(Allocator::get_default()) {}
explicit Column(std::unique_ptr<Array> root) noexcept;
Column(Allocator&, ref_type);
Column(unattached_root_tag, Allocator&);
Column(Column&&) noexcept = default;
~Column() noexcept override;
void init_from_parent();
void init_from_ref(Allocator&, ref_type);
void init_from_mem(Allocator&, MemRef);
// Accessor concept:
void destroy() noexcept override;
Allocator& get_alloc() const noexcept final;
ref_type get_ref() const noexcept final;
MemRef get_mem() const noexcept final;
void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept override;
size_t get_ndx_in_parent() const noexcept final;
void set_ndx_in_parent(size_t ndx) noexcept final;
void update_from_parent(size_t old_baseline) noexcept override;
void refresh_accessor_tree(size_t, const Spec&) override;
void detach() noexcept final;
bool is_attached() const noexcept final;
MemRef clone_deep(Allocator&) const override;
void move_assign(Column&);
size_t size() const noexcept override;
bool is_empty() const noexcept { return size() == 0; }
bool is_nullable() const noexcept override;
/// Provides access to the leaf that contains the element at the
/// specified index. Upon return \a ndx_in_leaf will be set to the
/// corresponding index relative to the beginning of the leaf.
///
/// LeafInfo is a struct defined by the underlying BpTree<T>
/// data structure, that provides a way for the caller to do
/// leaf caching without instantiating too many objects along
/// the way.
///
/// This function cannot be used for modifying operations as it
/// does not ensure the presence of an unbroken chain of parent
/// accessors. For this reason, the identified leaf should always
/// be accessed through the returned const-qualified reference,
/// and never directly through the specfied fallback accessor.
void get_leaf(size_t ndx, size_t& ndx_in_leaf,
LeafInfo& inout_leaf) const noexcept;
// Getting and setting values
T get_val(size_t ndx) const noexcept final { return get(ndx); }
T get(size_t ndx) const noexcept;
bool is_null(size_t ndx) const noexcept override;
T back() const noexcept;
void set(size_t, T value);
void set_null(size_t) override;
void add(T value = T{});
void insert(size_t ndx, T value = T{}, size_t num_rows = 1);
void erase(size_t row_ndx);
void erase(size_t row_ndx, bool is_last);
void move_last_over(size_t row_ndx, size_t last_row_ndx);
void clear();
// Index support
StringData get_index_data(size_t ndx, StringIndex::StringConversionBuffer& buffer) const noexcept override;
// FIXME: Remove these
uint64_t get_uint(size_t ndx) const noexcept;
ref_type get_as_ref(size_t ndx) const noexcept;
void set_uint(size_t ndx, uint64_t value);
void set_as_ref(size_t ndx, ref_type value);
template<class U>
void adjust(size_t ndx, U diff);
template<class U>
void adjust(U diff);
template<class U>
void adjust_ge(T limit, U diff);
size_t count(T target) const;
typename ColumnTypeTraits<T>::sum_type
sum(size_t start = 0, size_t end = npos, size_t limit = npos, size_t* return_ndx = nullptr) const;
typename ColumnTypeTraits<T>::minmax_type
maximum(size_t start = 0, size_t end = npos, size_t limit = npos, size_t* return_ndx = nullptr) const;
typename ColumnTypeTraits<T>::minmax_type
minimum(size_t start = 0, size_t end = npos, size_t limit = npos, size_t* return_ndx = nullptr) const;
double average(size_t start = 0, size_t end = npos, size_t limit = npos,
size_t* return_ndx = nullptr) const;
size_t find_first(T value, size_t begin = 0, size_t end = npos) const;
void find_all(Column<int64_t>& out_indices, T value,
size_t begin = 0, size_t end = npos) const;
void populate_search_index();
StringIndex* create_search_index() override;
//@{
/// Find the lower/upper bound for the specified value assuming
/// that the elements are already sorted in ascending order
/// according to ordinary integer comparison.
size_t lower_bound(T value) const noexcept;
size_t upper_bound(T value) const noexcept;
//@}
size_t find_gte(T target, size_t start) const;
bool compare(const Column&) const noexcept;
static ref_type create(Allocator&, Array::Type leaf_type = Array::type_Normal,
size_t size = 0, T value = 0);
// Overriding method in ColumnBase
ref_type write(size_t, size_t, size_t,
_impl::OutputStream&) const override;
void insert_rows(size_t, size_t, size_t, bool) override;
void erase_rows(size_t, size_t, size_t, bool) override;
void move_last_row_over(size_t, size_t, bool) override;
/// \brief Swap the elements at the specified indices.
///
/// If this \c Column has a search index defined, it will be updated to
/// reflect the changes induced by the swap.
///
/// Behaviour is undefined if:
/// - \a row_ndx_1 or \a row_ndx_2 point to an invalid element (out-of
/// bounds)
/// - \a row_ndx_1 and \a row_ndx_2 point to the same value
void swap_rows(size_t, size_t) override;
void clear(size_t, bool) override;
/// \param row_ndx Must be `realm::npos` if appending.
void insert_without_updating_index(size_t row_ndx, T value, size_t num_rows);
#ifdef REALM_DEBUG
void verify() const override;
using ColumnBase::verify;
void to_dot(std::ostream&, StringData title) const override;
void tree_to_dot(std::ostream&) const;
MemStats stats() const;
void do_dump_node_structure(std::ostream&, int) const override;
#endif
//@{
/// Returns the array node at the root of this column, but note
/// that there is no guarantee that this node is an inner B+-tree
/// node or a leaf. This is the case for a MixedColumn in
/// particular.
Array* get_root_array() noexcept { return &m_tree.root(); }
const Array* get_root_array() const noexcept { return &m_tree.root(); }
//@}
protected:
bool root_is_leaf() const noexcept { return m_tree.root_is_leaf(); }
void replace_root_array(std::unique_ptr<Array> leaf) final { m_tree.replace_root(std::move(leaf)); }
void set_without_updating_index(size_t row_ndx, T value);
void erase_without_updating_index(size_t row_ndx, bool is_last);
void move_last_over_without_updating_index(size_t row_ndx, size_t last_row_ndx);
void swap_rows_without_updating_index(size_t row_ndx_1, size_t row_ndx_2);
/// If any element points to an array node, this function recursively
/// destroys that array node. Note that the same is **not** true for
/// IntegerColumn::do_erase() and IntegerColumn::do_move_last_over().
///
/// FIXME: Be careful, clear_without_updating_index() currently forgets
/// if the leaf type is Array::type_HasRefs.
void clear_without_updating_index();
#ifdef REALM_DEBUG
void leaf_to_dot(MemRef, ArrayParent*, size_t ndx_in_parent,
std::ostream&) const override;
static void dump_node_structure(const Array& root, std::ostream&, int level);
#endif
private:
class EraseLeafElem;
class CreateHandler;
class SliceHandler;
friend class Array;
friend class ColumnBase;
friend class StringIndex;
BpTree<T> m_tree;
void do_erase(size_t row_ndx, size_t num_rows_to_erase, bool is_last);
};
// Implementation:
inline bool ColumnBase::has_search_index() const noexcept
{
return get_search_index() != nullptr;
}
inline StringIndex* ColumnBase::create_search_index()
{
return nullptr;
}
inline void ColumnBase::destroy_search_index() noexcept
{
}
inline const StringIndex* ColumnBase::get_search_index() const noexcept
{
return nullptr;
}
inline StringIndex* ColumnBase::get_search_index() noexcept
{
return nullptr;
}
inline void ColumnBase::set_search_index_ref(ref_type, ArrayParent*, size_t, bool)
{
}
inline void ColumnBase::set_search_index_allow_duplicate_values(bool) noexcept
{
}
inline void ColumnBase::discard_child_accessors() noexcept
{
do_discard_child_accessors();
}
inline Table* ColumnBase::get_subtable_accessor(size_t) const noexcept
{
return 0;
}
inline void ColumnBase::discard_subtable_accessor(size_t) noexcept
{
// Noop
}
inline void ColumnBase::adj_acc_insert_rows(size_t, size_t) noexcept
{
// Noop
}
inline void ColumnBase::adj_acc_erase_row(size_t) noexcept
{
// Noop
}
inline void ColumnBase::adj_acc_move_over(size_t, size_t) noexcept
{
// Noop
}
inline void ColumnBase::adj_acc_swap_rows(size_t, size_t) noexcept
{
// Noop
}
inline void ColumnBase::adj_acc_clear_root_table() noexcept
{
// Noop
}
inline void ColumnBase::mark(int) noexcept
{
// Noop
}
inline void ColumnBase::bump_link_origin_table_version() noexcept
{
// Noop
}
template<class T>
void Column<T>::set_without_updating_index(size_t ndx, T value)
{
m_tree.set(ndx, std::move(value));
}
template<class T>
void Column<T>::set(size_t ndx, T value)
{
REALM_ASSERT_DEBUG(ndx < size());
if (has_search_index()) {
m_search_index->set(ndx, value);
}
set_without_updating_index(ndx, std::move(value));
}
template<class T>
void Column<T>::set_null(size_t ndx)
{
REALM_ASSERT_DEBUG(ndx < size());
if (!is_nullable()) {
throw LogicError{LogicError::column_not_nullable};
}
if (has_search_index()) {
m_search_index->set(ndx, null{});
}
m_tree.set_null(ndx);
}
// When a value of a signed type is converted to an unsigned type, the C++ standard guarantees that negative values
// are converted from the native representation to 2's complement, but the opposite conversion is left as undefined.
// realm::util::from_twos_compl() is used here to perform the correct opposite unsigned-to-signed conversion,
// which reduces to a no-op when 2's complement is the native representation of negative values.
template<class T>
void Column<T>::set_uint(size_t ndx, uint64_t value)
{
set(ndx, util::from_twos_compl<int_fast64_t>(value));
}
template<class T>
void Column<T>::set_as_ref(size_t ndx, ref_type ref)
{
set(ndx, from_ref(ref));
}
template<class T>
template<class U>
void Column<T>::adjust(size_t ndx, U diff)
{
REALM_ASSERT_3(ndx, <, size());
m_tree.adjust(ndx, diff);
}
template<class T>
template<class U>
void Column<T>::adjust(U diff)
{
m_tree.adjust(diff);
}
template<class T>
template<class U>
void Column<T>::adjust_ge(T limit, U diff)
{
m_tree.adjust_ge(limit, diff);
}
template<class T>
size_t Column<T>::count(T target) const
{
if (has_search_index()) {
return m_search_index->count(target);
}
return to_size_t(aggregate<T, int64_t, act_Count, Equal>(*this, target, 0, size(), npos, nullptr));
}
template<class T>
typename ColumnTypeTraits<T>::sum_type
Column<T>::sum(size_t start, size_t end, size_t limit, size_t* return_ndx) const
{
using sum_type = typename ColumnTypeTraits<T>::sum_type;
if (nullable)
return aggregate<T, sum_type, act_Sum, NotNull>(*this, 0, start, end, limit, return_ndx);
else
return aggregate<T, sum_type, act_Sum, None>(*this, 0, start, end, limit, return_ndx);
}
template<class T>
double Column<T>::average(size_t start, size_t end, size_t limit, size_t* return_ndx) const
{
if (end == size_t(-1))
end = size();
auto s = sum(start, end, limit);
size_t cnt = to_size_t(aggregate<T, int64_t, act_Count, NotNull>(*this, 0, start, end, limit, nullptr));
if (return_ndx)
*return_ndx = cnt;
double avg = double(s) / (cnt == 0 ? 1 : cnt);
return avg;
}
template<class T>
typename ColumnTypeTraits<T>::minmax_type
Column<T>::minimum(size_t start, size_t end, size_t limit, size_t* return_ndx) const
{
using R = typename ColumnTypeTraits<T>::minmax_type;
return aggregate<T, R, act_Min, NotNull>(*this, 0, start, end, limit, return_ndx);
}
template<class T>
typename ColumnTypeTraits<T>::minmax_type
Column<T>::maximum(size_t start, size_t end, size_t limit, size_t* return_ndx) const
{
using R = typename ColumnTypeTraits<T>::minmax_type;
return aggregate<T, R, act_Max, NotNull>(*this, 0, start, end, limit, return_ndx);
}
template<class T>
void Column<T>::get_leaf(size_t ndx, size_t& ndx_in_leaf,
typename BpTree<T>::LeafInfo& inout_leaf_info) const noexcept
{
m_tree.get_leaf(ndx, ndx_in_leaf, inout_leaf_info);
}
template<class T>
StringData Column<T>::get_index_data(size_t ndx, StringIndex::StringConversionBuffer& buffer) const noexcept
{
T x = get(ndx);
StringData str = to_str(x); // takes x by reference, returns StringData pointing to memory in
// this stack frame.
// Copy bytes into buffer:
REALM_ASSERT(str.size() <= StringIndex::string_conversion_buffer_size);
if (str.data() != nullptr) {
std::copy(str.data(), str.data() + str.size(), buffer.data());
return StringData{buffer.data(), str.size()};
}
else {
return str; // "null"
}
}
template<class T>
void Column<T>::populate_search_index()
{
REALM_ASSERT(has_search_index());
// Populate the index
size_t num_rows = size();
for (size_t row_ndx = 0; row_ndx != num_rows; ++row_ndx) {
bool is_append = true;
if (is_null(row_ndx)) {
m_search_index->insert(row_ndx, null{}, 1, is_append); // Throws
}
else {
T value = get(row_ndx);
m_search_index->insert(row_ndx, value, 1, is_append); // Throws
}
}
}
template<class T>
StringIndex* Column<T>::create_search_index()
{
REALM_ASSERT(!has_search_index());
m_search_index.reset(new StringIndex(this, get_alloc())); // Throws
populate_search_index();
return m_search_index.get();
}
template<class T>
size_t Column<T>::find_first(T value, size_t begin, size_t end) const
{
REALM_ASSERT_3(begin, <=, size());
REALM_ASSERT(end == npos || (begin <= end && end <= size()));
if (m_search_index && begin == 0 && end == npos)
return m_search_index->find_first(value);
return m_tree.find_first(value, begin, end);
}
template<class T>
void Column<T>::find_all(IntegerColumn& result, T value, size_t begin, size_t end) const
{
REALM_ASSERT_3(begin, <=, size());
REALM_ASSERT(end == npos || (begin <= end && end <= size()));
if (m_search_index && begin == 0 && end == npos)
return m_search_index->find_all(result, value);
return m_tree.find_all(result, value, begin, end);
}
inline size_t ColumnBase::get_size_from_ref(ref_type root_ref, Allocator& alloc)
{
const char* root_header = alloc.translate(root_ref);
bool root_is_leaf = !Array::get_is_inner_bptree_node_from_header(root_header);
if (root_is_leaf)
return Array::get_size_from_header(root_header);
return Array::get_bptree_size_from_header(root_header);
}
template<class L, class T>
size_t ColumnBase::lower_bound(const L& list, T value) const noexcept
{
size_t i = 0;
size_t size = list.size();
while (0 < size) {
size_t half = size / 2;
size_t mid = i + half;
typename L::value_type probe = list.get(mid);
if (probe < value) {
i = mid + 1;
size -= half + 1;
}
else {
size = half;
}
}
return i;
}
template<class L, class T>
size_t ColumnBase::upper_bound(const L& list, T value) const noexcept
{
size_t i = 0;
size_t size = list.size();
while (0 < size) {
size_t half = size / 2;
size_t mid = i + half;
typename L::value_type probe = list.get(mid);
if (!(value < probe)) {
i = mid + 1;
size -= half + 1;
}
else {
size = half;
}
}
return i;
}
inline ref_type ColumnBase::create(Allocator& alloc, size_t size, CreateHandler& handler)
{
size_t rest_size = size;
size_t fixed_height = 0; // Not fixed
return build(&rest_size, fixed_height, alloc, handler);
}
template<class T>
Column<T>::Column(Allocator& alloc, ref_type ref) : m_tree(BpTreeBase::unattached_tag{})
{
// fixme, must m_search_index be copied here?
m_tree.init_from_ref(alloc, ref);
}
template<class T>
Column<T>::Column(unattached_root_tag, Allocator& alloc) : m_tree(alloc)
{
}
template<class T>
Column<T>::Column(std::unique_ptr<Array> root) noexcept : m_tree(std::move(root))
{
}
template<class T>
Column<T>::~Column() noexcept
{
}
template<class T>
void Column<T>::init_from_parent()
{
m_tree.init_from_parent();
}
template<class T>
void Column<T>::init_from_ref(Allocator& alloc, ref_type ref)
{
m_tree.init_from_ref(alloc, ref);
}
template<class T>
void Column<T>::init_from_mem(Allocator& alloc, MemRef mem)
{
m_tree.init_from_mem(alloc, mem);
}
template<class T>
void Column<T>::destroy() noexcept
{
ColumnBaseWithIndex::destroy();
m_tree.destroy();
}
template<class T>
void Column<T>::move_assign(Column<T>& col)
{
ColumnBaseWithIndex::move_assign(col);
m_tree = std::move(col.m_tree);
}
template<class T>
Allocator& Column<T>::get_alloc() const noexcept
{
return m_tree.get_alloc();
}
template<class T>
void Column<T>::set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept
{
m_tree.set_parent(parent, ndx_in_parent);
}
template<class T>
size_t Column<T>::get_ndx_in_parent() const noexcept
{
return m_tree.get_ndx_in_parent();
}
template<class T>
void Column<T>::set_ndx_in_parent(size_t ndx_in_parent) noexcept
{
ColumnBaseWithIndex::set_ndx_in_parent(ndx_in_parent);
m_tree.set_ndx_in_parent(ndx_in_parent);
}
template<class T>
void Column<T>::detach() noexcept
{
m_tree.detach();
}
template<class T>
bool Column<T>::is_attached() const noexcept
{
return m_tree.is_attached();
}
template<class T>
ref_type Column<T>::get_ref() const noexcept
{
return get_root_array()->get_ref();
}
template<class T>
MemRef Column<T>::get_mem() const noexcept
{
return get_root_array()->get_mem();
}
template<class T>
void Column<T>::update_from_parent(size_t old_baseline) noexcept
{
ColumnBaseWithIndex::update_from_parent(old_baseline);
m_tree.update_from_parent(old_baseline);
}
template<class T>
MemRef Column<T>::clone_deep(Allocator& alloc) const
{
return m_tree.clone_deep(alloc);
}
template<class T>
size_t Column<T>::size() const noexcept
{
return m_tree.size();
}
template<class T>
bool Column<T>::is_nullable() const noexcept
{
return nullable;
}
template<class T>
T Column<T>::get(size_t ndx) const noexcept
{
return m_tree.get(ndx);
}
template<class T>
bool Column<T>::is_null(size_t ndx) const noexcept
{
return m_tree.is_null(ndx);
}
template<class T>
T Column<T>::back() const noexcept
{
return m_tree.back();
}
template<class T>
ref_type Column<T>::get_as_ref(size_t ndx) const noexcept
{
return to_ref(get(ndx));
}
template<class T>
uint64_t Column<T>::get_uint(size_t ndx) const noexcept
{
static_assert(std::is_convertible<T, uint64_t>::value, "T is not convertible to uint.");
return static_cast<uint64_t>(get(ndx));
}
template<class T>
void Column<T>::add(T value)
{
insert(npos, std::move(value));
}
template<class T>
void Column<T>::insert_without_updating_index(size_t row_ndx, T value, size_t num_rows)
{
size_t size = this->size(); // Slow
bool is_append = row_ndx == size || row_ndx == npos;
size_t ndx_or_npos_if_append = is_append ? npos : row_ndx;
m_tree.insert(ndx_or_npos_if_append, std::move(value), num_rows); // Throws
}
template<class T>
void Column<T>::insert(size_t row_ndx, T value, size_t num_rows)
{
size_t size = this->size(); // Slow
bool is_append = row_ndx == size || row_ndx == npos;
size_t ndx_or_npos_if_append = is_append ? npos : row_ndx;
m_tree.insert(ndx_or_npos_if_append, value, num_rows); // Throws
if (has_search_index()) {
row_ndx = is_append ? size : row_ndx;
m_search_index->insert(row_ndx, value, num_rows, is_append); // Throws
}
}
template<class T>
void Column<T>::erase_without_updating_index(size_t row_ndx, bool is_last)
{
m_tree.erase(row_ndx, is_last);
}
template<class T>
void Column<T>::erase(size_t row_ndx)
{
REALM_ASSERT(size() >= 1);
size_t last_row_ndx = size() - 1; // Note that size() is slow
bool is_last = (row_ndx == last_row_ndx);
erase(row_ndx, is_last); // Throws
}
template<class T>
void Column<T>::erase(size_t row_ndx, bool is_last)
{
size_t num_rows_to_erase = 1;
do_erase(row_ndx, num_rows_to_erase, is_last); // Throws
}
template<class T>
void Column<T>::move_last_over_without_updating_index(size_t row_ndx, size_t last_row_ndx)
{
m_tree.move_last_over(row_ndx, last_row_ndx);
}
template<class T>
void Column<T>::move_last_over(size_t row_ndx, size_t last_row_ndx)
{
REALM_ASSERT_3(row_ndx, <=, last_row_ndx);
REALM_ASSERT_DEBUG(last_row_ndx + 1 == size());
if (has_search_index()) {
// remove the value to be overwritten from index
bool is_last = true; // This tells StringIndex::erase() to not adjust subsequent indexes
m_search_index->erase<StringData>(row_ndx, is_last); // Throws
// update index to point to new location
if (row_ndx != last_row_ndx) {
T moved_value = get(last_row_ndx);
m_search_index->update_ref(moved_value, last_row_ndx, row_ndx); // Throws
}
}
move_last_over_without_updating_index(row_ndx, last_row_ndx);
}
template<class T>
void Column<T>::swap_rows(size_t row_ndx_1, size_t row_ndx_2)
{
REALM_ASSERT_3(row_ndx_1, <, size());
REALM_ASSERT_3(row_ndx_2, <, size());
REALM_ASSERT_DEBUG(row_ndx_1 != row_ndx_2);
if (has_search_index()) {
T value_1 = get(row_ndx_1);
T value_2 = get(row_ndx_2);
size_t size = this->size();
bool row_ndx_1_is_last = row_ndx_1 == size - 1;
bool row_ndx_2_is_last = row_ndx_2 == size - 1;
m_search_index->erase<StringData>(row_ndx_1, row_ndx_1_is_last);
m_search_index->insert(row_ndx_1, value_2, 1, row_ndx_1_is_last);
m_search_index->erase<StringData>(row_ndx_2, row_ndx_2_is_last);
m_search_index->insert(row_ndx_2, value_1, 1, row_ndx_2_is_last);
}
swap_rows_without_updating_index(row_ndx_1, row_ndx_2);
}
template<class T>
void Column<T>::swap_rows_without_updating_index(size_t row_ndx_1, size_t row_ndx_2)
{
// FIXME: This can be optimized with direct getters and setters.
T value_1 = get(row_ndx_1);
T value_2 = get(row_ndx_2);
m_tree.set(row_ndx_1, value_2);
m_tree.set(row_ndx_2, value_1);
}
template<class T>
void Column<T>::clear_without_updating_index()
{
m_tree.clear(); // Throws
}
template<class T>
void Column<T>::clear()
{
if (has_search_index()) {
m_search_index->clear();
}
clear_without_updating_index();
}
template<class T, class Enable = void> struct NullOrDefaultValue;
template<class T> struct NullOrDefaultValue<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
static T null_or_default_value(bool is_null)
{
if (is_null) {
return null::get_null_float<T>();
}
else {
return T{};
}
}
};
template<class T> struct NullOrDefaultValue<util::Optional<T>, void> {
static util::Optional<T> null_or_default_value(bool is_null)
{
if (is_null) {
return util::none;
}
else {
return util::some<T>(T{});
}
}
};
template<class T> struct NullOrDefaultValue<T, typename std::enable_if<!ImplicitNull<T>::value>::type> {
static T null_or_default_value(bool is_null)
{
REALM_ASSERT(!is_null);
static_cast<void>(is_null);
return T{};
}
};
// Implementing pure virtual method of ColumnBase.
template<class T>
void Column<T>::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool insert_nulls)
{
REALM_ASSERT_DEBUG(prior_num_rows == size());
REALM_ASSERT(row_ndx <= prior_num_rows);
size_t row_ndx_2 = (row_ndx == prior_num_rows ? realm::npos : row_ndx);
T value = NullOrDefaultValue<T>::null_or_default_value(insert_nulls);
insert(row_ndx_2, value, num_rows_to_insert); // Throws
}
// Implementing pure virtual method of ColumnBase.
template<class T>
void Column<T>::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool)
{
REALM_ASSERT_DEBUG(prior_num_rows == size());
REALM_ASSERT(num_rows_to_erase <= prior_num_rows);
REALM_ASSERT(row_ndx <= prior_num_rows - num_rows_to_erase);
bool is_last = (row_ndx + num_rows_to_erase == prior_num_rows);
do_erase(row_ndx, num_rows_to_erase, is_last); // Throws
}
// Implementing pure virtual method of ColumnBase.
template<class T>
void Column<T>::move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool)
{
REALM_ASSERT_DEBUG(prior_num_rows == size());
REALM_ASSERT(row_ndx < prior_num_rows);
size_t last_row_ndx = prior_num_rows - 1;
move_last_over(row_ndx, last_row_ndx); // Throws
}
// Implementing pure virtual method of ColumnBase.
template<class T>
void Column<T>::clear(size_t, bool)
{
clear(); // Throws
}
template<class T>
size_t Column<T>::lower_bound(T value) const noexcept
{
if (root_is_leaf()) {
auto root = static_cast<const LeafType*>(get_root_array());
return root->lower_bound(value);
}
return ColumnBase::lower_bound(*this, value);
}
template<class T>
size_t Column<T>::upper_bound(T value) const noexcept
{
if (root_is_leaf()) {
auto root = static_cast<const LeafType*>(get_root_array());
return root->upper_bound(value);
}
return ColumnBase::upper_bound(*this, value);
}
// For a *sorted* Column, return first element E for which E >= target or return -1 if none
template<class T>
size_t Column<T>::find_gte(T target, size_t start) const
{
// fixme: slow reference implementation. See Array::find_gte for faster version
size_t ref = 0;
size_t idx;
for (idx = start; idx < size(); ++idx) {
if (get(idx) >= target) {
ref = idx;
break;
}
}
if (idx == size())
ref = not_found;
return ref;
}
template<class T>
bool Column<T>::compare(const Column<T>& c) const noexcept
{
size_t n = size();
if (c.size() != n)
return false;
for (size_t i=0; i<n; ++i) {
bool left_is_null = is_null(i);
bool right_is_null = c.is_null(i);
if (left_is_null != right_is_null) {
return false;
}
if (!left_is_null) {
if (get(i) != c.get(i))
return false;
}
}
return true;
}
template<class T>
class Column<T>::CreateHandler: public ColumnBase::CreateHandler {
public:
CreateHandler(Array::Type leaf_type, T value, Allocator& alloc):
m_value(value), m_alloc(alloc), m_leaf_type(leaf_type) {}
ref_type create_leaf(size_t size) override
{
MemRef mem = BpTree<T>::create_leaf(m_leaf_type, size, m_value, m_alloc); // Throws
return mem.m_ref;
}
private:
const T m_value;
Allocator& m_alloc;
Array::Type m_leaf_type;
};
template<class T>
ref_type Column<T>::create(Allocator& alloc, Array::Type leaf_type, size_t size, T value)
{
CreateHandler handler(leaf_type, std::move(value), alloc);
return ColumnBase::create(alloc, size, handler);
}
template<class T>
ref_type Column<T>::write(size_t slice_offset, size_t slice_size,
size_t table_size, _impl::OutputStream& out) const
{
return m_tree.write(slice_offset, slice_size, table_size, out);
}
template<class T>
void Column<T>::refresh_accessor_tree(size_t new_col_ndx, const Spec& spec)
{
m_tree.init_from_parent();
ColumnBaseWithIndex::refresh_accessor_tree(new_col_ndx, spec);
}
template<class T>
void Column<T>::do_erase(size_t row_ndx, size_t num_rows_to_erase, bool is_last)
{
if (has_search_index()) {
for (size_t i = num_rows_to_erase; i > 0; --i) {
size_t row_ndx_2 = row_ndx + i - 1;
m_search_index->erase<T>(row_ndx_2, is_last); // Throws
}
}
for (size_t i = num_rows_to_erase; i > 0; --i) {
size_t row_ndx_2 = row_ndx + i - 1;
erase_without_updating_index(row_ndx_2, is_last); // Throws
}
}
#ifdef REALM_DEBUG
template<class T>
void Column<T>::verify() const
{
m_tree.verify();
}
template<class T>
void Column<T>::to_dot(std::ostream& out, StringData title) const
{
ref_type ref = get_root_array()->get_ref();
out << "subgraph cluster_integer_column" << ref << " {" << std::endl;
out << " label = \"Integer column";
if (title.size() != 0)
out << "\\n'" << title << "'";
out << "\";" << std::endl;
tree_to_dot(out);
out << "}" << std::endl;
}
template<class T>
void Column<T>::tree_to_dot(std::ostream& out) const
{
ColumnBase::bptree_to_dot(get_root_array(), out);
}
template<class T>
void Column<T>::leaf_to_dot(MemRef leaf_mem, ArrayParent* parent, size_t ndx_in_parent,
std::ostream& out) const
{
BpTree<T>::leaf_to_dot(leaf_mem, parent, ndx_in_parent, out, get_alloc());
}
template<class T>
MemStats Column<T>::stats() const
{
MemStats stats;
get_root_array()->stats(stats);
return stats;
}
namespace _impl {
void leaf_dumper(MemRef mem, Allocator& alloc, std::ostream& out, int level);
}
template<class T>
void Column<T>::do_dump_node_structure(std::ostream& out, int level) const
{
dump_node_structure(*get_root_array(), out, level);
}
template<class T>
void Column<T>::dump_node_structure(const Array& root, std::ostream& out, int level)
{
root.dump_bptree_structure(out, level, &_impl::leaf_dumper);
}
#endif // REALM_DEBUG
} // namespace realm
#endif // REALM_COLUMN_HPP