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 #include <realm/column_binary.hpp>
20 #include <realm/column_timestamp.hpp>
24 inline MixedColumn::MixedColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx)
25 : ColumnBaseSimple(column_ndx)
27 create(alloc, ref, table, column_ndx);
30 inline void MixedColumn::adj_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept
32 m_data->adj_acc_insert_rows(row_ndx, num_rows);
35 inline void MixedColumn::adj_acc_erase_row(size_t row_ndx) noexcept
37 m_data->adj_acc_erase_row(row_ndx);
40 inline void MixedColumn::adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept
42 m_data->adj_acc_swap_rows(row_ndx_1, row_ndx_2);
45 inline void MixedColumn::adj_acc_move_row(size_t from_ndx, size_t to_ndx) noexcept
47 m_data->adj_acc_move_row(from_ndx, to_ndx);
50 inline void MixedColumn::adj_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept
52 m_data->adj_acc_move_over(from_row_ndx, to_row_ndx);
55 inline void MixedColumn::adj_acc_clear_root_table() noexcept
57 m_data->adj_acc_clear_root_table();
60 inline ref_type MixedColumn::get_subtable_ref(size_t row_ndx) const noexcept
62 REALM_ASSERT_3(row_ndx, <, m_types->size());
63 if (m_types->get(row_ndx) != type_Table)
65 return m_data->get_as_ref(row_ndx);
68 inline size_t MixedColumn::get_subtable_size(size_t row_ndx) const noexcept
70 ref_type top_ref = get_subtable_ref(row_ndx);
73 return _impl::TableFriend::get_size_from_ref(top_ref, m_data->get_alloc());
76 inline TableRef MixedColumn::get_subtable_accessor(size_t row_ndx) const noexcept
78 return m_data->get_subtable_accessor(row_ndx);
81 inline void MixedColumn::discard_subtable_accessor(size_t row_ndx) noexcept
83 m_data->discard_subtable_accessor(row_ndx);
86 inline TableRef MixedColumn::get_subtable_tableref(size_t row_ndx)
88 REALM_ASSERT_3(row_ndx, <, m_types->size());
89 if (m_types->get(row_ndx) != type_Table)
91 return m_data->get_subtable_tableref(row_ndx); // Throws
94 inline ConstTableRef MixedColumn::get_subtable_tableref(size_t subtable_ndx) const
96 return const_cast<MixedColumn*>(this)->get_subtable_tableref(subtable_ndx);
99 inline void MixedColumn::discard_child_accessors() noexcept
101 m_data->discard_child_accessors();
109 #define REALM_BIT63 0x8000000000000000ULL
111 inline int64_t MixedColumn::get_value(size_t ndx) const noexcept
113 REALM_ASSERT_3(ndx, <, m_types->size());
115 // Shift the unsigned value right - ensuring 0 gets in from left.
116 // Shifting signed integers right doesn't ensure 0's.
117 uint64_t value = uint64_t(m_data->get(ndx)) >> 1;
118 return int64_t(value);
121 inline int64_t MixedColumn::get_int(size_t ndx) const noexcept
123 // Get first 63 bits of the integer value
124 int64_t value = get_value(ndx);
126 // restore 'sign'-bit from the column-type
127 MixedColType col_type = MixedColType(m_types->get(ndx));
128 if (col_type == mixcol_IntNeg) {
129 // FIXME: Bad cast of result of '|' from unsigned to signed
130 value |= REALM_BIT63; // set sign bit (63)
133 REALM_ASSERT_3(col_type, ==, mixcol_Int);
138 inline bool MixedColumn::get_bool(size_t ndx) const noexcept
140 REALM_ASSERT_3(m_types->get(ndx), ==, mixcol_Bool);
142 return (get_value(ndx) != 0);
145 inline OldDateTime MixedColumn::get_olddatetime(size_t ndx) const noexcept
147 REALM_ASSERT_3(m_types->get(ndx), ==, mixcol_OldDateTime);
149 return OldDateTime(get_value(ndx));
152 inline float MixedColumn::get_float(size_t ndx) const noexcept
154 static_assert(std::numeric_limits<float>::is_iec559, "'float' is not IEEE");
155 static_assert((sizeof(float) * CHAR_BIT == 32), "Assume 32 bit float.");
156 REALM_ASSERT_3(m_types->get(ndx), ==, mixcol_Float);
158 return type_punning<float>(get_value(ndx));
161 inline double MixedColumn::get_double(size_t ndx) const noexcept
163 static_assert(std::numeric_limits<double>::is_iec559, "'double' is not IEEE");
164 static_assert((sizeof(double) * CHAR_BIT == 64), "Assume 64 bit double.");
166 int64_t int_val = get_value(ndx);
168 // restore 'sign'-bit from the column-type
169 MixedColType col_type = MixedColType(m_types->get(ndx));
170 if (col_type == mixcol_DoubleNeg)
171 int_val |= REALM_BIT63; // set sign bit (63)
173 REALM_ASSERT_3(col_type, ==, mixcol_Double);
175 return type_punning<double>(int_val);
178 inline StringData MixedColumn::get_string(size_t ndx) const noexcept
180 REALM_ASSERT_3(ndx, <, m_types->size());
181 REALM_ASSERT_3(m_types->get(ndx), ==, mixcol_String);
182 REALM_ASSERT(m_binary_data);
184 size_t data_ndx = size_t(int64_t(m_data->get(ndx)) >> 1);
185 return m_binary_data->get_string(data_ndx);
188 inline BinaryData MixedColumn::get_binary(size_t ndx) const noexcept
190 REALM_ASSERT_3(ndx, <, m_types->size());
191 REALM_ASSERT_3(m_types->get(ndx), ==, mixcol_Binary);
192 REALM_ASSERT(m_binary_data);
194 size_t data_ndx = size_t(uint64_t(m_data->get(ndx)) >> 1);
195 return m_binary_data->get(data_ndx);
198 inline Timestamp MixedColumn::get_timestamp(size_t ndx) const noexcept
200 REALM_ASSERT_3(ndx, <, m_types->size());
201 REALM_ASSERT_3(m_types->get(ndx), ==, mixcol_Timestamp);
202 REALM_ASSERT(m_timestamp_data);
203 size_t data_ndx = size_t(uint64_t(m_data->get(ndx)) >> 1);
204 return m_timestamp_data->get(data_ndx);
211 // Set a int64 value.
212 // Store 63 bit of the value in m_data. Store sign bit in m_types.
214 inline void MixedColumn::set_int64(size_t ndx, int64_t value, MixedColType pos_type, MixedColType neg_type)
216 REALM_ASSERT_3(ndx, <, m_types->size());
218 // If sign-bit is set in value, 'store' it in the column-type
219 MixedColType coltype = ((value & REALM_BIT63) == 0) ? pos_type : neg_type;
221 // Remove refs or binary data (sets type to double)
222 clear_value_and_discard_subtab_acc(ndx, coltype); // Throws
224 // Shift value one bit and set lowest bit to indicate that this is not a ref
225 value = (value << 1) + 1;
226 m_data->set(ndx, value);
229 inline void MixedColumn::set_int(size_t ndx, int64_t value)
231 set_int64(ndx, value, mixcol_Int, mixcol_IntNeg); // Throws
234 inline void MixedColumn::set_double(size_t ndx, double value)
236 int64_t val64 = type_punning<int64_t>(value);
237 set_int64(ndx, val64, mixcol_Double, mixcol_DoubleNeg); // Throws
240 inline void MixedColumn::set_value(size_t ndx, int64_t value, MixedColType coltype)
242 REALM_ASSERT_3(ndx, <, m_types->size());
244 // Remove refs or binary data (sets type to float)
245 clear_value_and_discard_subtab_acc(ndx, coltype); // Throws
247 // Shift value one bit and set lowest bit to indicate that this is not a ref
248 int64_t v = (value << 1) + 1;
249 m_data->set(ndx, v); // Throws
252 inline void MixedColumn::set_float(size_t ndx, float value)
254 int64_t val64 = type_punning<int64_t>(value);
255 set_value(ndx, val64, mixcol_Float); // Throws
258 inline void MixedColumn::set_bool(size_t ndx, bool value)
260 set_value(ndx, (value ? 1 : 0), mixcol_Bool); // Throws
263 inline void MixedColumn::set_olddatetime(size_t ndx, OldDateTime value)
265 set_value(ndx, int64_t(value.get_olddatetime()), mixcol_OldDateTime); // Throws
268 inline void MixedColumn::set_subtable(size_t ndx, const Table* t)
270 REALM_ASSERT_3(ndx, <, m_types->size());
271 typedef _impl::TableFriend tf;
274 ref = tf::clone(*t, get_alloc()); // Throws
277 ref = tf::create_empty_table(get_alloc()); // Throws
279 // Remove any previous refs or binary data
280 clear_value_and_discard_subtab_acc(ndx, mixcol_Table); // Throws
281 m_data->set(ndx, ref); // Throws
288 inline void MixedColumn::insert_value(size_t row_ndx, int_fast64_t types_value, int_fast64_t data_value)
290 size_t types_size = m_types->size(); // Slow
291 bool is_append = row_ndx == types_size;
292 size_t row_ndx_2 = is_append ? realm::npos : row_ndx;
294 m_types->insert_without_updating_index(row_ndx_2, types_value, num_rows); // Throws
295 m_data->do_insert(row_ndx_2, data_value, num_rows); // Throws
298 // Insert a int64 value.
299 // Store 63 bit of the value in m_data. Store sign bit in m_types.
301 inline void MixedColumn::insert_int(size_t ndx, int_fast64_t value, MixedColType type)
303 int_fast64_t types_value = type;
304 // Shift value one bit and set lowest bit to indicate that this is not a ref
305 int_fast64_t data_value = 1 + (value << 1);
306 insert_value(ndx, types_value, data_value); // Throws
309 inline void MixedColumn::insert_pos_neg(size_t ndx, int_fast64_t value, MixedColType pos_type, MixedColType neg_type)
311 // 'store' the sign-bit in the integer-type
312 MixedColType type = (value & REALM_BIT63) == 0 ? pos_type : neg_type;
313 int_fast64_t types_value = type;
314 // Shift value one bit and set lowest bit to indicate that this is not a ref
315 int_fast64_t data_value = 1 + (value << 1);
316 insert_value(ndx, types_value, data_value); // Throws
319 inline void MixedColumn::insert_int(size_t ndx, int_fast64_t value)
321 insert_pos_neg(ndx, value, mixcol_Int, mixcol_IntNeg); // Throws
324 inline void MixedColumn::insert_double(size_t ndx, double value)
326 int_fast64_t value_2 = type_punning<int64_t>(value);
327 insert_pos_neg(ndx, value_2, mixcol_Double, mixcol_DoubleNeg); // Throws
330 inline void MixedColumn::insert_float(size_t ndx, float value)
332 int_fast64_t value_2 = type_punning<int32_t>(value);
333 insert_int(ndx, value_2, mixcol_Float); // Throws
336 inline void MixedColumn::insert_bool(size_t ndx, bool value)
338 int_fast64_t value_2 = int_fast64_t(value);
339 insert_int(ndx, value_2, mixcol_Bool); // Throws
342 inline void MixedColumn::insert_olddatetime(size_t ndx, OldDateTime value)
344 int_fast64_t value_2 = int_fast64_t(value.get_olddatetime());
345 insert_int(ndx, value_2, mixcol_OldDateTime); // Throws
348 inline void MixedColumn::insert_timestamp(size_t ndx, Timestamp value)
350 ensure_timestamp_column();
351 size_t data_ndx = m_timestamp_data->size();
352 m_timestamp_data->add(value); // Throws
353 insert_int(ndx, int_fast64_t(data_ndx), mixcol_Timestamp);
356 inline void MixedColumn::insert_string(size_t ndx, StringData value)
358 ensure_binary_data_column();
359 size_t blob_ndx = m_binary_data->size();
360 m_binary_data->add_string(value); // Throws
362 int_fast64_t value_2 = int_fast64_t(blob_ndx);
363 insert_int(ndx, value_2, mixcol_String); // Throws
366 inline void MixedColumn::insert_binary(size_t ndx, BinaryData value)
368 ensure_binary_data_column();
369 size_t blob_ndx = m_binary_data->size();
370 m_binary_data->add(value); // Throws
372 int_fast64_t value_2 = int_fast64_t(blob_ndx);
373 insert_int(ndx, value_2, mixcol_Binary); // Throws
376 inline void MixedColumn::insert_subtable(size_t ndx, const Table* t)
378 typedef _impl::TableFriend tf;
381 ref = tf::clone(*t, get_alloc()); // Throws
384 ref = tf::create_empty_table(get_alloc()); // Throws
386 int_fast64_t types_value = mixcol_Table;
387 int_fast64_t data_value = int_fast64_t(ref);
388 insert_value(ndx, types_value, data_value); // Throws
391 inline void MixedColumn::erase(size_t row_ndx)
393 size_t num_rows_to_erase = 1;
394 size_t prior_num_rows = size(); // Note that size() is slow
395 do_erase(row_ndx, num_rows_to_erase, prior_num_rows); // Throws
398 inline void MixedColumn::move_last_over(size_t row_ndx)
400 size_t prior_num_rows = size(); // Note that size() is slow
401 do_move_last_over(row_ndx, prior_num_rows); // Throws
404 inline void MixedColumn::swap_rows(size_t row_ndx_1, size_t row_ndx_2)
406 do_swap_rows(row_ndx_1, row_ndx_2);
409 inline void MixedColumn::clear()
411 size_t num_rows = size(); // Note that size() is slow
412 do_clear(num_rows); // Throws
415 inline size_t MixedColumn::get_size_from_ref(ref_type root_ref, Allocator& alloc) noexcept
417 const char* root_header = alloc.translate(root_ref);
418 ref_type types_ref = to_ref(Array::get(root_header, 0));
419 return IntegerColumn::get_size_from_ref(types_ref, alloc);
422 inline void MixedColumn::clear_value_and_discard_subtab_acc(size_t row_ndx, MixedColType new_type)
424 MixedColType old_type = clear_value(row_ndx, new_type);
425 if (old_type == mixcol_Table)
426 m_data->discard_subtable_accessor(row_ndx);
429 // Implementing pure virtual method of ColumnBase.
430 inline void MixedColumn::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows,
433 REALM_ASSERT_DEBUG(prior_num_rows == size());
434 REALM_ASSERT(row_ndx <= prior_num_rows);
435 REALM_ASSERT(!insert_nulls);
437 size_t row_ndx_2 = (row_ndx == prior_num_rows ? realm::npos : row_ndx);
439 int_fast64_t type_value = mixcol_Int;
440 m_types->insert_without_updating_index(row_ndx_2, type_value, num_rows_to_insert); // Throws
442 // The least significant bit indicates that the rest of the bits form an
443 // integer value, so 1 is actually zero.
444 int_fast64_t data_value = 1;
445 m_data->do_insert(row_ndx_2, data_value, num_rows_to_insert); // Throws
448 // Implementing pure virtual method of ColumnBase.
449 inline void MixedColumn::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool)
451 do_erase(row_ndx, num_rows_to_erase, prior_num_rows); // Throws
454 // Implementing pure virtual method of ColumnBase.
455 inline void MixedColumn::move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool)
457 do_move_last_over(row_ndx, prior_num_rows); // Throws
460 // Implementing pure virtual method of ColumnBase.
461 inline void MixedColumn::clear(size_t num_rows, bool)
463 do_clear(num_rows); // Throws
466 inline void MixedColumn::mark(int type) noexcept
471 inline void MixedColumn::refresh_accessor_tree(size_t col_ndx, const Spec& spec)
473 ColumnBaseSimple::refresh_accessor_tree(col_ndx, spec);
475 get_root_array()->init_from_parent();
476 m_types->refresh_accessor_tree(col_ndx, spec); // Throws
477 m_data->refresh_accessor_tree(col_ndx, spec); // Throws
479 m_binary_data.reset();
480 // See if m_binary_data needs to be created.
481 if (get_root_array()->size() >= 3) {
482 ref_type ref = get_root_array()->get_as_ref(2);
483 m_binary_data.reset(new BinaryColumn(get_alloc(), ref)); // Throws
484 m_binary_data->set_parent(get_root_array(), 2);
487 m_timestamp_data.reset();
488 // See if m_timestamp_data needs to be created.
489 if (get_root_array()->size() >= 4) {
490 ref_type ref = get_root_array()->get_as_ref(3);
491 // When adding/creating a Mixed column the user cannot specify nullability, so the "true" below
492 // makes it implicitly nullable, which may not be wanted. But it's OK since Mixed columns are not
493 // publicly supported
494 m_timestamp_data.reset(new TimestampColumn(true /*fixme*/, get_alloc(), ref)); // Throws
495 m_timestamp_data->set_parent(get_root_array(), 3);
499 REALM_ASSERT_3(get_root_array()->size(), >=, 3);
500 m_binary_data->refresh_accessor_tree(col_ndx, spec); // Throws
502 if (m_timestamp_data) {
503 REALM_ASSERT_3(get_root_array()->size(), >=, 4);
504 m_timestamp_data->refresh_accessor_tree(col_ndx, spec); // Throws
508 inline void MixedColumn::RefsColumn::refresh_accessor_tree(size_t col_ndx, const Spec& spec)
510 SubtableColumnBase::refresh_accessor_tree(col_ndx, spec); // Throws
511 m_subtable_map.refresh_accessor_tree(); // Throws