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_ARRAY_INTEGER_HPP
20 #define REALM_ARRAY_INTEGER_HPP
22 #include <realm/array.hpp>
23 #include <realm/util/safe_int_ops.hpp>
24 #include <realm/util/optional.hpp>
28 class ArrayInteger : public Array {
30 typedef int64_t value_type;
32 explicit ArrayInteger(Allocator&) noexcept;
33 ~ArrayInteger() noexcept override
37 // Disable copying, this is not allowed.
38 ArrayInteger& operator=(const ArrayInteger&) = delete;
39 ArrayInteger(const ArrayInteger&) = delete;
41 void create(Type type = type_Normal, bool context_flag = false);
43 void add(int64_t value);
44 void set(size_t ndx, int64_t value);
45 void set_uint(size_t ndx, uint_fast64_t value) noexcept;
46 int64_t get(size_t ndx) const noexcept;
47 uint64_t get_uint(size_t ndx) const noexcept;
48 static int64_t get(const char* header, size_t ndx) noexcept;
49 bool compare(const ArrayInteger& a) const noexcept;
51 /// Add \a diff to the element at the specified index.
52 void adjust(size_t ndx, int_fast64_t diff);
54 /// Add \a diff to all the elements in the specified index range.
55 void adjust(size_t begin, size_t end, int_fast64_t diff);
57 /// Add signed \a diff to all elements that are greater than, or equal to \a
59 void adjust_ge(int_fast64_t limit, int_fast64_t diff);
61 int64_t operator[](size_t ndx) const noexcept
65 int64_t front() const noexcept;
66 int64_t back() const noexcept;
68 size_t lower_bound(int64_t value) const noexcept;
69 size_t upper_bound(int64_t value) const noexcept;
71 std::vector<int64_t> to_vector() const;
75 bool minmax(size_t from, size_t to, uint64_t maxdiff, int64_t* min, int64_t* max) const;
78 class ArrayIntNull : public Array {
80 using value_type = util::Optional<int64_t>;
82 explicit ArrayIntNull(Allocator&) noexcept;
83 ~ArrayIntNull() noexcept override;
85 /// Construct an array of the specified type and size, and return just the
86 /// reference to the underlying memory. All elements will be initialized to
87 /// the specified value.
88 static MemRef create_array(Type, bool context_flag, size_t size, value_type value, Allocator&);
89 void create(Type = type_Normal, bool context_flag = false);
91 void init_from_ref(ref_type) noexcept;
92 void init_from_mem(MemRef) noexcept;
93 void init_from_parent() noexcept;
95 size_t size() const noexcept;
96 bool is_empty() const noexcept;
98 void insert(size_t ndx, value_type value);
99 void add(value_type value);
100 void set(size_t ndx, value_type value) noexcept;
101 value_type get(size_t ndx) const noexcept;
102 static value_type get(const char* header, size_t ndx) noexcept;
103 void get_chunk(size_t ndx, value_type res[8]) const noexcept;
104 void set_null(size_t ndx) noexcept;
105 bool is_null(size_t ndx) const noexcept;
106 int64_t null_value() const noexcept;
108 value_type operator[](size_t ndx) const noexcept;
109 value_type front() const noexcept;
110 value_type back() const noexcept;
111 void erase(size_t ndx);
112 void erase(size_t begin, size_t end);
113 void truncate(size_t size);
115 void set_all_to_zero();
117 void move(size_t begin, size_t end, size_t dest_begin);
118 void move_backward(size_t begin, size_t end, size_t dest_end);
120 size_t lower_bound(int64_t value) const noexcept;
121 size_t upper_bound(int64_t value) const noexcept;
123 int64_t sum(size_t start = 0, size_t end = npos) const;
124 size_t count(int64_t value) const noexcept;
125 bool maximum(int64_t& result, size_t start = 0, size_t end = npos, size_t* return_ndx = nullptr) const;
126 bool minimum(int64_t& result, size_t start = 0, size_t end = npos, size_t* return_ndx = nullptr) const;
128 bool find(int cond, Action action, value_type value, size_t start, size_t end, size_t baseindex,
129 QueryState<int64_t>* state) const;
131 template <class cond, Action action, size_t bitwidth, class Callback>
132 bool find(value_type value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
133 Callback callback) const;
135 // This is the one installed into the m_finder slots.
136 template <class cond, Action action, size_t bitwidth>
137 bool find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state) const;
139 template <class cond, Action action, class Callback>
140 bool find(value_type value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
141 Callback callback) const;
143 // Optimized implementation for release mode
144 template <class cond, Action action, size_t bitwidth, class Callback>
145 bool find_optimized(value_type value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
146 Callback callback) const;
148 // Called for each search result
149 template <Action action, class Callback>
150 bool find_action(size_t index, value_type value, QueryState<int64_t>* state, Callback callback) const;
152 template <Action action, class Callback>
153 bool find_action_pattern(size_t index, uint64_t pattern, QueryState<int64_t>* state, Callback callback) const;
155 // Wrappers for backwards compatibility and for simple use without
156 // setting up state initialization etc
157 template <class cond>
158 size_t find_first(value_type value, size_t start = 0, size_t end = npos) const;
160 void find_all(IntegerColumn* result, value_type value, size_t col_offset = 0, size_t begin = 0,
161 size_t end = npos) const;
164 size_t find_first(value_type value, size_t begin = 0, size_t end = npos) const;
167 // Overwrite Array::bptree_leaf_insert to correctly split nodes.
168 ref_type bptree_leaf_insert(size_t ndx, value_type value, TreeInsertBase& state);
170 MemRef slice(size_t offset, size_t slice_size, Allocator& target_alloc) const;
172 /// Construct a deep copy of the specified slice of this array using the
173 /// specified target allocator. Subarrays will be cloned.
174 MemRef slice_and_clone_children(size_t offset, size_t slice_size, Allocator& target_alloc) const;
177 void avoid_null_collision(int64_t value);
180 template <bool find_max>
181 bool minmax_helper(int64_t& result, size_t start = 0, size_t end = npos, size_t* return_ndx = nullptr) const;
183 int_fast64_t choose_random_null(int64_t incoming) const;
184 void replace_nulls_with(int64_t new_null);
185 bool can_use_as_null(int64_t value) const;
191 inline ArrayInteger::ArrayInteger(Allocator& allocator) noexcept
194 m_is_inner_bptree_node = false;
197 inline void ArrayInteger::add(int64_t value)
202 inline int64_t ArrayInteger::get(size_t ndx) const noexcept
204 return Array::get(ndx);
207 inline int64_t ArrayInteger::get(const char* header, size_t ndx) noexcept
209 return Array::get(header, ndx);
212 inline void ArrayInteger::set(size_t ndx, int64_t value)
214 Array::set(ndx, value);
217 inline void ArrayInteger::set_uint(size_t ndx, uint_fast64_t value) noexcept
219 // When a value of a signed type is converted to an unsigned type, the C++
220 // standard guarantees that negative values are converted from the native
221 // representation to 2's complement, but the effect of conversions in the
222 // opposite direction is left unspecified by the
223 // standard. `realm::util::from_twos_compl()` is used here to perform the
224 // correct opposite unsigned-to-signed conversion, which reduces to a no-op
225 // when 2's complement is the native representation of negative values.
226 set(ndx, util::from_twos_compl<int_fast64_t>(value));
229 inline bool ArrayInteger::compare(const ArrayInteger& a) const noexcept
231 if (a.size() != size())
234 for (size_t i = 0; i < size(); ++i) {
235 if (get(i) != a.get(i))
242 inline int64_t ArrayInteger::front() const noexcept
244 return Array::front();
247 inline int64_t ArrayInteger::back() const noexcept
249 return Array::back();
252 inline void ArrayInteger::adjust(size_t ndx, int_fast64_t diff)
254 Array::adjust(ndx, diff);
257 inline void ArrayInteger::adjust(size_t begin, size_t end, int_fast64_t diff)
259 Array::adjust(begin, end, diff);
262 inline void ArrayInteger::adjust_ge(int_fast64_t limit, int_fast64_t diff)
264 Array::adjust_ge(limit, diff);
267 inline size_t ArrayInteger::lower_bound(int64_t value) const noexcept
269 return lower_bound_int(value);
272 inline size_t ArrayInteger::upper_bound(int64_t value) const noexcept
274 return upper_bound_int(value);
278 inline ArrayIntNull::ArrayIntNull(Allocator& allocator) noexcept
283 inline ArrayIntNull::~ArrayIntNull() noexcept
287 inline void ArrayIntNull::create(Type type, bool context_flag)
289 MemRef r = create_array(type, context_flag, 0, util::none, m_alloc);
294 inline size_t ArrayIntNull::size() const noexcept
296 return Array::size() - 1;
299 inline bool ArrayIntNull::is_empty() const noexcept
304 inline void ArrayIntNull::insert(size_t ndx, value_type value)
307 avoid_null_collision(*value);
308 Array::insert(ndx + 1, *value);
311 Array::insert(ndx + 1, null_value());
315 inline void ArrayIntNull::add(value_type value)
318 avoid_null_collision(*value);
322 Array::add(null_value());
326 inline void ArrayIntNull::set(size_t ndx, value_type value) noexcept
329 avoid_null_collision(*value);
330 Array::set(ndx + 1, *value);
333 Array::set(ndx + 1, null_value());
337 inline void ArrayIntNull::set_null(size_t ndx) noexcept
339 Array::set(ndx + 1, null_value());
342 inline ArrayIntNull::value_type ArrayIntNull::get(size_t ndx) const noexcept
344 int64_t value = Array::get(ndx + 1);
345 if (value == null_value()) {
348 return util::some<int64_t>(value);
351 inline ArrayIntNull::value_type ArrayIntNull::get(const char* header, size_t ndx) noexcept
353 int64_t null_value = Array::get(header, 0);
354 int64_t value = Array::get(header, ndx + 1);
355 if (value == null_value) {
359 return util::some<int64_t>(value);
363 inline bool ArrayIntNull::is_null(size_t ndx) const noexcept
368 inline int64_t ArrayIntNull::null_value() const noexcept
370 return Array::get(0);
373 inline ArrayIntNull::value_type ArrayIntNull::operator[](size_t ndx) const noexcept
378 inline ArrayIntNull::value_type ArrayIntNull::front() const noexcept
383 inline ArrayIntNull::value_type ArrayIntNull::back() const noexcept
385 return Array::back();
388 inline void ArrayIntNull::erase(size_t ndx)
390 Array::erase(ndx + 1);
393 inline void ArrayIntNull::erase(size_t begin, size_t end)
395 Array::erase(begin + 1, end + 1);
398 inline void ArrayIntNull::truncate(size_t to_size)
400 Array::truncate(to_size + 1);
403 inline void ArrayIntNull::clear()
408 inline void ArrayIntNull::set_all_to_zero()
410 // FIXME: Array::set_all_to_zero does something else
411 for (size_t i = 0; i < size(); ++i) {
416 inline void ArrayIntNull::move(size_t begin, size_t end, size_t dest_begin)
418 Array::move(begin + 1, end + 1, dest_begin + 1);
421 inline void ArrayIntNull::move_backward(size_t begin, size_t end, size_t dest_end)
423 Array::move_backward(begin + 1, end + 1, dest_end + 1);
426 inline size_t ArrayIntNull::lower_bound(int64_t value) const noexcept
428 // FIXME: Consider this behaviour with NULLs.
429 // Array::lower_bound_int assumes an already sorted array, but
430 // this array could be sorted with nulls first or last.
431 return Array::lower_bound_int(value);
434 inline size_t ArrayIntNull::upper_bound(int64_t value) const noexcept
436 // FIXME: see lower_bound
437 return Array::upper_bound_int(value);
440 inline int64_t ArrayIntNull::sum(size_t start, size_t end) const
443 int64_t sum_of_range = 0;
446 for (size_t i = start; i < end; ++i) {
447 value_type x = get(i);
455 inline size_t ArrayIntNull::count(int64_t value) const noexcept
457 size_t count_of_value = Array::count(value);
458 if (value == null_value()) {
461 return count_of_value;
465 template <bool find_max>
466 inline bool ArrayIntNull::minmax_helper(int64_t& result, size_t start, size_t end, size_t* return_ndx) const
468 size_t best_index = 1;
476 REALM_ASSERT(start < m_size && end <= m_size && start < end);
485 *return_ndx = best_index - 1;
490 int64_t m = Array::get(start);
492 const int64_t null_val = null_value();
493 for (; start < end; ++start) {
494 const int64_t v = Array::get(start);
495 if (find_max ? v > m : v < m) {
506 *return_ndx = best_index - 1;
511 inline bool ArrayIntNull::maximum(int64_t& result, size_t start, size_t end, size_t* return_ndx) const
513 return minmax_helper<true>(result, start, end, return_ndx);
516 inline bool ArrayIntNull::minimum(int64_t& result, size_t start, size_t end, size_t* return_ndx) const
518 return minmax_helper<false>(result, start, end, return_ndx);
521 inline bool ArrayIntNull::find(int cond, Action action, value_type value, size_t start, size_t end, size_t baseindex,
522 QueryState<int64_t>* state) const
525 return Array::find(cond, action, *value, start, end, baseindex, state, true /*treat as nullable array*/,
526 false /*search parameter given in 'value' argument*/);
529 return Array::find(cond, action, 0 /* unused dummy*/, start, end, baseindex, state,
530 true /*treat as nullable array*/, true /*search for null, ignore value argument*/);
534 template <class cond, Action action, size_t bitwidth, class Callback>
535 bool ArrayIntNull::find(value_type value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
536 Callback callback) const
539 return Array::find<cond, action>(*value, start, end, baseindex, state, std::forward<Callback>(callback),
540 true /*treat as nullable array*/,
541 false /*search parameter given in 'value' argument*/);
544 return Array::find<cond, action>(0 /*ignored*/, start, end, baseindex, state,
545 std::forward<Callback>(callback), true /*treat as nullable array*/,
546 true /*search for null, ignore value argument*/);
551 template <class cond, Action action, size_t bitwidth>
552 bool ArrayIntNull::find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state) const
554 return Array::find<cond, action>(value, start, end, baseindex, state, true /*treat as nullable array*/,
555 false /*search parameter given in 'value' argument*/);
559 template <class cond, Action action, class Callback>
560 bool ArrayIntNull::find(value_type value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
561 Callback callback) const
564 return Array::find<cond, action>(*value, start, end, baseindex, state, std::forward<Callback>(callback),
565 true /*treat as nullable array*/,
566 false /*search parameter given in 'value' argument*/);
569 return Array::find<cond, action>(0 /*ignored*/, start, end, baseindex, state,
570 std::forward<Callback>(callback), true /*treat as nullable array*/,
571 true /*search for null, ignore value argument*/);
576 template <Action action, class Callback>
577 bool ArrayIntNull::find_action(size_t index, value_type value, QueryState<int64_t>* state, Callback callback) const
580 return Array::find_action<action, Callback>(index, *value, state, callback, true /*treat as nullable array*/,
581 false /*search parameter given in 'value' argument*/);
584 return Array::find_action<action, Callback>(index, 0 /* ignored */, state, callback,
585 true /*treat as nullable array*/,
586 true /*search for null, ignore value argument*/);
591 template <Action action, class Callback>
592 bool ArrayIntNull::find_action_pattern(size_t index, uint64_t pattern, QueryState<int64_t>* state,
593 Callback callback) const
595 return Array::find_action_pattern<action, Callback>(index, pattern, state, callback,
596 true /*treat as nullable array*/,
597 false /*search parameter given in 'value' argument*/);
601 template <class cond>
602 size_t ArrayIntNull::find_first(value_type value, size_t start, size_t end) const
604 QueryState<int64_t> state;
605 state.init(act_ReturnFirst, nullptr, 1);
607 Array::find<cond, act_ReturnFirst>(*value, start, end, 0, &state, Array::CallbackDummy(),
608 true /*treat as nullable array*/,
609 false /*search parameter given in 'value' argument*/);
612 Array::find<cond, act_ReturnFirst>(0 /*ignored*/, start, end, 0, &state, Array::CallbackDummy(),
613 true /*treat as nullable array*/,
614 true /*search for null, ignore value argument*/);
617 if (state.m_match_count > 0)
618 return to_size_t(state.m_state);
623 inline size_t ArrayIntNull::find_first(value_type value, size_t begin, size_t end) const
625 return find_first<Equal>(value, begin, end);
629 #endif // REALM_ARRAY_INTEGER_HPP