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_ENUM_HPP
20 #define REALM_COLUMN_STRING_ENUM_HPP
22 #include <realm/column_string.hpp>
30 /// From the point of view of the application, an enumerated strings column
31 /// (StringEnumColumn) is like a string column (StringColumn), yet it manages
32 /// its strings in such a way that each unique string is stored only once. In
33 /// fact, an enumerated strings column is a combination of two subcolumns; a
34 /// regular string column (StringColumn) that stores the unique strings, and an
35 /// integer column that stores one unique string index for each entry in the
36 /// enumerated strings column.
38 /// In terms of the underlying node structure, the subcolumn containing the
39 /// unique strings is not a true part of the enumerated strings column. Instead
40 /// it is a part of the spec structure that describes the table of which the
41 /// enumerated strings column is a part. This way, the unique strings can be
42 /// shared across enumerated strings columns of multiple subtables. This also
43 /// means that the root of an enumerated strings column coincides with the root
44 /// of the integer subcolumn, and in some sense, an enumerated strings column is
45 /// just the integer subcolumn.
47 /// An enumerated strings column can optionally be equipped with a
48 /// search index. If it is, then the root ref of the index is stored
49 /// in Table::m_columns immediately after the root ref of the
50 /// enumerated strings column.
51 class StringEnumColumn : public IntegerColumn {
53 typedef StringData value_type;
55 StringEnumColumn(Allocator&, ref_type ref, ref_type keys_ref, bool nullable, size_t column_ndx = npos);
56 ~StringEnumColumn() noexcept override;
57 void destroy() noexcept override;
58 MemRef clone_deep(Allocator& alloc) const override;
60 int compare_values(size_t row1, size_t row2) const noexcept override
62 StringData a = get(row1);
63 StringData b = get(row2);
65 if (a.is_null() && !b.is_null())
67 else if (b.is_null() && !a.is_null())
69 else if (a.is_null() && b.is_null())
75 return utf8_compare(a, b) ? 1 : -1;
78 StringData get(size_t ndx) const noexcept;
79 bool is_null(size_t ndx) const noexcept final;
80 void set(size_t ndx, StringData value);
81 void set_null(size_t ndx) override;
83 void add(StringData value);
84 void insert(size_t ndx);
85 void insert(size_t ndx, StringData value);
86 void erase(size_t row_ndx);
87 void move_last_over(size_t row_ndx);
88 void swap_rows(size_t row_ndx_1, size_t row_ndx_2) override;
90 bool is_nullable() const noexcept final;
92 size_t count(StringData value) const;
93 size_t find_first(StringData value, size_t begin = 0, size_t end = npos) const;
94 void find_all(IntegerColumn& res, StringData value, size_t begin = 0, size_t end = npos) const;
95 FindRes find_all_no_copy(StringData value, InternalFindResult& result) const;
97 size_t count(size_t key_index) const;
98 size_t find_first(size_t key_index, size_t begin = 0, size_t end = -1) const;
99 void find_all(IntegerColumn& res, size_t key_index, size_t begin = 0, size_t end = -1) const;
102 /// Find the lower/upper bound for the specified value assuming
103 /// that the elements are already sorted in ascending order
104 /// according to StringData::operator<().
105 size_t lower_bound_string(StringData value) const noexcept;
106 size_t upper_bound_string(StringData value) const noexcept;
109 void set_string(size_t, StringData) override;
111 void adjust_keys_ndx_in_parent(int diff) noexcept;
114 StringData get_index_data(size_t ndx, StringIndex::StringConversionBuffer& buffer) const noexcept final;
115 bool supports_search_index() const noexcept final
119 StringIndex* create_search_index() override;
120 void install_search_index(std::unique_ptr<StringIndex>) noexcept;
121 void destroy_search_index() noexcept override;
123 // Compare two string columns for equality
124 bool compare_string(const StringColumn&) const;
125 bool compare_string(const StringEnumColumn&) const;
127 void insert_rows(size_t, size_t, size_t, bool) override;
128 void erase_rows(size_t, size_t, size_t, bool) override;
129 void move_last_row_over(size_t, size_t, bool) override;
130 void clear(size_t, bool) override;
131 void update_from_parent(size_t) noexcept override;
132 void refresh_accessor_tree(size_t, const Spec&) override;
134 size_t get_key_ndx(StringData value) const;
135 size_t get_key_ndx_or_add(StringData value);
137 StringColumn& get_keys();
138 const StringColumn& get_keys() const;
141 void verify() const override;
142 void verify(const Table&, size_t) const override;
143 void do_dump_node_structure(std::ostream&, int) const override;
144 void to_dot(std::ostream&, StringData title) const override;
152 /// If you are appending and have the size of the column readily available,
153 /// call the 4 argument version instead. If you are not appending, either
156 /// \param row_ndx Must be `realm::npos` if appending.
157 void do_insert(size_t row_ndx, StringData value, size_t num_rows);
159 /// If you are appending and you do not have the size of the column readily
160 /// available, call the 3 argument version instead. If you are not
161 /// appending, either one is fine.
163 /// \param is_append Must be true if, and only if `row_ndx` is equal to the
164 /// size of the column (before insertion).
165 void do_insert(size_t row_ndx, StringData value, size_t num_rows, bool is_append);
167 void do_erase(size_t row_ndx, bool is_last);
168 void do_move_last_over(size_t row_ndx, size_t last_row_ndx);
175 inline StringData StringEnumColumn::get(size_t ndx) const noexcept
177 REALM_ASSERT_3(ndx, <, IntegerColumn::size());
178 size_t key_ndx = to_size_t(IntegerColumn::get(ndx));
179 StringData sd = m_keys.get(key_ndx);
180 REALM_ASSERT_DEBUG(!(!m_nullable && sd.is_null()));
184 inline bool StringEnumColumn::is_null(size_t ndx) const noexcept
186 return is_nullable() && get(ndx).is_null();
189 inline void StringEnumColumn::add()
191 add(m_nullable ? realm::null() : StringData(""));
194 inline void StringEnumColumn::add(StringData value)
196 REALM_ASSERT_DEBUG(!(!m_nullable && value.is_null()));
197 size_t row_ndx = realm::npos;
199 do_insert(row_ndx, value, num_rows); // Throws
202 inline void StringEnumColumn::insert(size_t row_ndx)
204 insert(row_ndx, m_nullable ? realm::null() : StringData(""));
207 inline void StringEnumColumn::insert(size_t row_ndx, StringData value)
209 REALM_ASSERT_DEBUG(!(!m_nullable && value.is_null()));
210 size_t column_size = this->size();
211 REALM_ASSERT_3(row_ndx, <=, column_size);
213 bool is_append = row_ndx == column_size;
214 do_insert(row_ndx, value, num_rows, is_append); // Throws
217 inline void StringEnumColumn::erase(size_t row_ndx)
219 size_t last_row_ndx = size() - 1; // Note that size() is slow
220 bool is_last = row_ndx == last_row_ndx;
221 do_erase(row_ndx, is_last); // Throws
224 inline void StringEnumColumn::move_last_over(size_t row_ndx)
226 size_t last_row_ndx = size() - 1; // Note that size() is slow
227 do_move_last_over(row_ndx, last_row_ndx); // Throws
230 inline void StringEnumColumn::clear()
232 do_clear(); // Throws
235 // Overriding virtual method of Column.
236 inline void StringEnumColumn::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows,
239 REALM_ASSERT_DEBUG(prior_num_rows == size());
240 REALM_ASSERT(row_ndx <= prior_num_rows);
241 REALM_ASSERT(!insert_nulls || m_nullable);
243 StringData value = m_nullable ? realm::null() : StringData("");
244 bool is_append = (row_ndx == prior_num_rows);
245 do_insert(row_ndx, value, num_rows_to_insert, is_append); // Throws
248 // Overriding virtual method of Column.
249 inline void StringEnumColumn::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool)
251 REALM_ASSERT_DEBUG(prior_num_rows == size());
252 REALM_ASSERT(num_rows_to_erase <= prior_num_rows);
253 REALM_ASSERT(row_ndx <= prior_num_rows - num_rows_to_erase);
255 bool is_last = (row_ndx + num_rows_to_erase == prior_num_rows);
256 for (size_t i = num_rows_to_erase; i > 0; --i) {
257 size_t row_ndx_2 = row_ndx + i - 1;
258 do_erase(row_ndx_2, is_last); // Throws
262 // Overriding virtual method of Column.
263 inline void StringEnumColumn::move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool)
265 REALM_ASSERT_DEBUG(prior_num_rows == size());
266 REALM_ASSERT(row_ndx < prior_num_rows);
268 size_t last_row_ndx = prior_num_rows - 1;
269 do_move_last_over(row_ndx, last_row_ndx); // Throws
272 // Overriding virtual method of Column.
273 inline void StringEnumColumn::clear(size_t, bool)
275 do_clear(); // Throws
278 inline size_t StringEnumColumn::lower_bound_string(StringData value) const noexcept
280 return ColumnBase::lower_bound(*this, value);
283 inline size_t StringEnumColumn::upper_bound_string(StringData value) const noexcept
285 return ColumnBase::upper_bound(*this, value);
288 inline void StringEnumColumn::set_string(size_t row_ndx, StringData value)
290 set(row_ndx, value); // Throws
293 inline void StringEnumColumn::set_null(size_t row_ndx)
295 set(row_ndx, realm::null{});
298 inline StringColumn& StringEnumColumn::get_keys()
303 inline const StringColumn& StringEnumColumn::get_keys() const
311 #endif // REALM_COLUMN_STRING_ENUM_HPP