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_STRING_HPP
20 #define REALM_COLUMN_STRING_HPP
23 #include <realm/array_string.hpp>
24 #include <realm/array_string_long.hpp>
25 #include <realm/array_blobs_big.hpp>
26 #include <realm/column.hpp>
27 #include <realm/column_tpl.hpp>
35 /// A string column (StringColumn) is a single B+-tree, and
36 /// the root of the column is the root of the B+-tree. Leaf nodes are
37 /// either of type ArrayString (array of small strings),
38 /// ArrayStringLong (array of medium strings), or ArrayBigBlobs (array
41 /// A string column can optionally be equipped with a search index. If
42 /// it is, then the root ref of the index is stored in
43 /// Table::m_columns immediately after the root ref of the string
45 class StringColumn : public ColumnBaseSimple {
47 typedef StringData value_type;
49 StringColumn(Allocator&, ref_type, bool nullable = false, size_t column_ndx = npos);
50 ~StringColumn() noexcept override;
52 void destroy() noexcept override;
54 size_t size() const noexcept final;
55 bool is_empty() const noexcept
60 bool is_null(size_t ndx) const noexcept final;
61 void set_null(size_t ndx) final;
62 StringData get(size_t ndx) const noexcept;
63 void set(size_t ndx, StringData);
65 void add(StringData value);
66 void insert(size_t ndx);
67 void insert(size_t ndx, StringData value);
68 void erase(size_t row_ndx);
69 void move_last_over(size_t row_ndx);
70 void swap_rows(size_t row_ndx_1, size_t row_ndx_2) override;
73 size_t count(StringData value) const;
74 size_t find_first(StringData value, size_t begin = 0, size_t end = npos) const;
75 void find_all(IntegerColumn& result, StringData value, size_t begin = 0, size_t end = npos) const;
76 FindRes find_all_no_copy(StringData value, InternalFindResult& result) const;
78 int compare_values(size_t, size_t) const noexcept override;
81 /// Find the lower/upper bound for the specified value assuming
82 /// that the elements are already sorted in ascending order
83 /// according to StringData::operator<().
84 size_t lower_bound_string(StringData value) const noexcept;
85 size_t upper_bound_string(StringData value) const noexcept;
88 void set_string(size_t, StringData) override;
90 bool is_nullable() const noexcept final;
93 StringData get_index_data(size_t ndx, StringIndex::StringConversionBuffer& buffer) const noexcept final;
94 bool has_search_index() const noexcept override;
95 void set_search_index_ref(ref_type, ArrayParent*, size_t) override;
96 StringIndex* get_search_index() noexcept override;
97 const StringIndex* get_search_index() const noexcept override;
98 std::unique_ptr<StringIndex> release_search_index() noexcept;
99 bool supports_search_index() const noexcept final
103 StringIndex* create_search_index() override;
105 // Simply inserts all column values in the index in a loop
106 void populate_search_index();
107 void destroy_search_index() noexcept override;
109 // Optimizing data layout. enforce == true will enforce enumeration;
110 // enforce == false will auto-evaluate if it should be enumerated or not
111 bool auto_enumerate(ref_type& keys, ref_type& values, bool enforce = false) const;
113 /// Compare two string columns for equality.
114 bool compare_string(const StringColumn&) const;
117 leaf_type_Small, ///< ArrayString
118 leaf_type_Medium, ///< ArrayStringLong
119 leaf_type_Big ///< ArrayBigBlobs
122 std::unique_ptr<const ArrayParent> get_leaf(size_t ndx, size_t& out_ndx_in_parent, LeafType& out_leaf_type) const;
124 static ref_type create(Allocator&, size_t size = 0);
126 static size_t get_size_from_ref(ref_type root_ref, Allocator&) noexcept;
128 // Overrriding method in ColumnBase
129 ref_type write(size_t, size_t, size_t, _impl::OutputStream&) const override;
131 void insert_rows(size_t, size_t, size_t, bool) override;
132 void erase_rows(size_t, size_t, size_t, bool) override;
133 void move_last_row_over(size_t, size_t, bool) override;
134 void clear(size_t, bool) override;
135 void set_ndx_in_parent(size_t ndx_in_parent) noexcept override;
136 void update_from_parent(size_t old_baseline) noexcept override;
137 void refresh_accessor_tree(size_t, const Spec&) override;
139 void verify() const override;
140 void verify(const Table&, size_t) const override;
141 void to_dot(std::ostream&, StringData title) const override;
142 void do_dump_node_structure(std::ostream&, int) const override;
145 std::unique_ptr<StringIndex> m_search_index;
148 LeafType get_block(size_t ndx, ArrayParent**, size_t& off, bool use_retval = false) const;
150 /// If you are appending and have the size of the column readily available,
151 /// call the 4 argument version instead. If you are not appending, either
154 /// \param row_ndx Must be `realm::npos` if appending.
155 void do_insert(size_t row_ndx, StringData value, size_t num_rows);
157 /// If you are appending and you do not have the size of the column readily
158 /// available, call the 3 argument version instead. If you are not
159 /// appending, either one is fine.
161 /// \param is_append Must be true if, and only if `row_ndx` is equal to the
162 /// size of the column (before insertion).
163 void do_insert(size_t row_ndx, StringData value, size_t num_rows, bool is_append);
165 /// \param row_ndx Must be `realm::npos` if appending.
166 void bptree_insert(size_t row_ndx, StringData value, size_t num_rows);
168 // Called by Array::bptree_insert().
169 static ref_type leaf_insert(MemRef leaf_mem, ArrayParent&, size_t ndx_in_parent, Allocator&, size_t insert_ndx,
170 BpTreeNode::TreeInsert<StringColumn>& state);
176 void do_erase(size_t row_ndx, bool is_last);
177 void do_move_last_over(size_t row_ndx, size_t last_row_ndx);
178 void do_swap_rows(size_t row_ndx_1, size_t row_ndx_2);
181 /// Root must be a leaf. Upgrades the root leaf as
182 /// necessary. Returns the type of the root leaf as it is upon
184 LeafType upgrade_root_leaf(size_t value_size);
186 void refresh_root_accessor();
188 void leaf_to_dot(MemRef, ArrayParent*, size_t ndx_in_parent, std::ostream&) const override;
190 friend class BpTreeNode;
191 friend class ColumnBase;
197 inline size_t StringColumn::size() const noexcept
199 if (root_is_leaf()) {
200 bool long_strings = m_array->has_refs();
202 // Small strings root leaf
203 ArrayString* leaf = static_cast<ArrayString*>(m_array.get());
206 bool is_big = m_array->get_context_flag();
208 // Medium strings root leaf
209 ArrayStringLong* leaf = static_cast<ArrayStringLong*>(m_array.get());
212 // Big strings root leaf
213 ArrayBigBlobs* leaf = static_cast<ArrayBigBlobs*>(m_array.get());
217 BpTreeNode* node = static_cast<BpTreeNode*>(m_array.get());
218 return node->get_bptree_size();
221 inline void StringColumn::add(StringData value)
223 REALM_ASSERT(!(value.is_null() && !m_nullable));
224 size_t row_ndx = realm::npos;
226 do_insert(row_ndx, value, num_rows); // Throws
229 inline void StringColumn::add()
231 add(m_nullable ? realm::null() : StringData(""));
234 inline void StringColumn::insert(size_t row_ndx, StringData value)
236 REALM_ASSERT(!(value.is_null() && !m_nullable));
237 size_t column_size = this->size();
238 REALM_ASSERT_3(row_ndx, <=, column_size);
240 bool is_append = row_ndx == column_size;
241 do_insert(row_ndx, value, num_rows, is_append); // Throws
244 inline void StringColumn::insert(size_t row_ndx)
246 insert(row_ndx, m_nullable ? realm::null() : StringData(""));
249 inline void StringColumn::erase(size_t row_ndx)
251 size_t last_row_ndx = size() - 1; // Note that size() is slow
252 bool is_last = row_ndx == last_row_ndx;
253 do_erase(row_ndx, is_last); // Throws
256 inline void StringColumn::move_last_over(size_t row_ndx)
258 size_t last_row_ndx = size() - 1; // Note that size() is slow
259 do_move_last_over(row_ndx, last_row_ndx); // Throws
262 inline void StringColumn::swap_rows(size_t row_ndx_1, size_t row_ndx_2)
264 do_swap_rows(row_ndx_1, row_ndx_2); // Throws
267 inline void StringColumn::clear()
269 do_clear(); // Throws
272 inline int StringColumn::compare_values(size_t row1, size_t row2) const noexcept
274 StringData a = get(row1);
275 StringData b = get(row2);
277 if (a.is_null() && !b.is_null())
279 else if (b.is_null() && !a.is_null())
281 else if (a.is_null() && b.is_null())
286 return utf8_compare(a, b) ? 1 : -1;
289 inline void StringColumn::set_string(size_t row_ndx, StringData value)
291 REALM_ASSERT(!(value.is_null() && !m_nullable));
292 set(row_ndx, value); // Throws
295 inline bool StringColumn::has_search_index() const noexcept
297 return m_search_index != 0;
300 inline StringIndex* StringColumn::get_search_index() noexcept
302 return m_search_index.get();
305 inline const StringIndex* StringColumn::get_search_index() const noexcept
307 return m_search_index.get();
310 inline size_t StringColumn::get_size_from_ref(ref_type root_ref, Allocator& alloc) noexcept
312 const char* root_header = alloc.translate(root_ref);
313 bool root_is_leaf = !Array::get_is_inner_bptree_node_from_header(root_header);
315 bool long_strings = Array::get_hasrefs_from_header(root_header);
317 // Small strings leaf
318 return ArrayString::get_size_from_header(root_header);
320 bool is_big = Array::get_context_flag_from_header(root_header);
322 // Medium strings leaf
323 return ArrayStringLong::get_size_from_header(root_header, alloc);
326 return ArrayBigBlobs::get_size_from_header(root_header);
329 return BpTreeNode::get_bptree_size_from_header(root_header);
332 // Implementing pure virtual method of ColumnBase.
333 inline void StringColumn::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows,
336 REALM_ASSERT_DEBUG(prior_num_rows == size());
337 REALM_ASSERT(row_ndx <= prior_num_rows);
338 REALM_ASSERT(!insert_nulls || m_nullable);
340 StringData value = m_nullable ? realm::null() : StringData("");
341 bool is_append = (row_ndx == prior_num_rows);
342 do_insert(row_ndx, value, num_rows_to_insert, is_append); // Throws
345 // Implementing pure virtual method of ColumnBase.
346 inline void StringColumn::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool)
348 REALM_ASSERT_DEBUG(prior_num_rows == size());
349 REALM_ASSERT(num_rows_to_erase <= prior_num_rows);
350 REALM_ASSERT(row_ndx <= prior_num_rows - num_rows_to_erase);
352 bool is_last = (row_ndx + num_rows_to_erase == prior_num_rows);
353 for (size_t i = num_rows_to_erase; i > 0; --i) {
354 size_t row_ndx_2 = row_ndx + i - 1;
355 do_erase(row_ndx_2, is_last); // Throws
359 // Implementing pure virtual method of ColumnBase.
360 inline void StringColumn::move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool)
362 REALM_ASSERT_DEBUG(prior_num_rows == size());
363 REALM_ASSERT(row_ndx < prior_num_rows);
365 size_t last_row_ndx = prior_num_rows - 1;
366 do_move_last_over(row_ndx, last_row_ndx); // Throws
369 // Implementing pure virtual method of ColumnBase.
370 inline void StringColumn::clear(size_t, bool)
372 do_clear(); // Throws
377 #endif // REALM_COLUMN_STRING_HPP