--- /dev/null
+/*************************************************************************
+ *
+ * Copyright 2016 Realm Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ **************************************************************************/
+
+/*
+Searching: The main finding function is:
+ template <class cond, Action action, size_t bitwidth, class Callback>
+ void find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState *state, Callback callback) const
+
+ cond: One of Equal, NotEqual, Greater, etc. classes
+ Action: One of act_ReturnFirst, act_FindAll, act_Max, act_CallbackIdx, etc, constants
+ Callback: Optional function to call for each search result. Will be called if action == act_CallbackIdx
+
+ find() will call find_action_pattern() or find_action() that again calls match() for each search result which
+ optionally calls callback():
+
+ find() -> find_action() -------> bool match() -> bool callback()
+ | ^
+ +-> find_action_pattern()----+
+
+ If callback() returns false, find() will exit, otherwise it will keep searching remaining items in array.
+*/
+
+#ifndef REALM_ARRAY_HPP
+#define REALM_ARRAY_HPP
+
+#include <cmath>
+#include <cstdlib> // size_t
+#include <algorithm>
+#include <utility>
+#include <vector>
+#include <ostream>
+
+#include <cstdint> // unint8_t etc
+
+#include <realm/util/assert.hpp>
+#include <realm/util/file_mapper.hpp>
+#include <realm/utilities.hpp>
+#include <realm/alloc.hpp>
+#include <realm/string_data.hpp>
+#include <realm/query_conditions.hpp>
+#include <realm/column_fwd.hpp>
+#include <realm/array_direct.hpp>
+
+/*
+ MMX: mmintrin.h
+ SSE: xmmintrin.h
+ SSE2: emmintrin.h
+ SSE3: pmmintrin.h
+ SSSE3: tmmintrin.h
+ SSE4A: ammintrin.h
+ SSE4.1: smmintrin.h
+ SSE4.2: nmmintrin.h
+*/
+#ifdef REALM_COMPILER_SSE
+#include <emmintrin.h> // SSE2
+#include <realm/realm_nmmintrin.h> // SSE42
+#endif
+
+namespace realm {
+
+enum Action {
+ act_ReturnFirst,
+ act_Sum,
+ act_Max,
+ act_Min,
+ act_Count,
+ act_FindAll,
+ act_CallIdx,
+ act_CallbackIdx,
+ act_CallbackVal,
+ act_CallbackNone,
+ act_CallbackBoth,
+ act_Average
+};
+
+template <class T>
+inline T no0(T v)
+{
+ return v == 0 ? 1 : v;
+}
+
+/// Special index value. It has various meanings depending on
+/// context. It is returned by some search functions to indicate 'not
+/// found'. It is similar in function to std::string::npos.
+const size_t npos = size_t(-1);
+
+// Maximum number of bytes that the payload of an array can be
+const size_t max_array_payload = 0x00ffffffL;
+const size_t max_array_payload_aligned = 0x00fffff8L;
+
+/// Alias for realm::npos.
+const size_t not_found = npos;
+
+// Pre-definitions
+class Array;
+class StringColumn;
+class GroupWriter;
+template <class T>
+class QueryState;
+namespace _impl {
+class ArrayWriterBase;
+}
+
+
+struct MemStats {
+ size_t allocated = 0;
+ size_t used = 0;
+ size_t array_count = 0;
+};
+
+#ifdef REALM_DEBUG
+template <class C, class T>
+std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& out, MemStats stats);
+#endif
+
+
+// Stores a value obtained from Array::get(). It is a ref if the least
+// significant bit is clear, otherwise it is a tagged integer. A tagged interger
+// is obtained from a logical integer value by left shifting by one bit position
+// (multiplying by two), and then setting the least significant bit to
+// one. Clearly, this means that the maximum value that can be stored as a
+// tagged integer is 2**63 - 1.
+class RefOrTagged {
+public:
+ bool is_ref() const noexcept;
+ bool is_tagged() const noexcept;
+ ref_type get_as_ref() const noexcept;
+ uint_fast64_t get_as_int() const noexcept;
+
+ static RefOrTagged make_ref(ref_type) noexcept;
+ static RefOrTagged make_tagged(uint_fast64_t) noexcept;
+
+private:
+ int_fast64_t m_value;
+ RefOrTagged(int_fast64_t) noexcept;
+ friend class Array;
+};
+
+
+class ArrayParent {
+public:
+ virtual ~ArrayParent() noexcept
+ {
+ }
+
+protected:
+ virtual void update_child_ref(size_t child_ndx, ref_type new_ref) = 0;
+
+ virtual ref_type get_child_ref(size_t child_ndx) const noexcept = 0;
+
+ // Used only by Array::to_dot().
+ virtual std::pair<ref_type, size_t> get_to_dot_parent(size_t ndx_in_parent) const = 0;
+
+ friend class Array;
+};
+
+struct TreeInsertBase {
+ size_t m_split_offset;
+ size_t m_split_size;
+};
+
+/// Provides access to individual array nodes of the database.
+///
+/// This class serves purely as an accessor, it assumes no ownership of the
+/// referenced memory.
+///
+/// An array accessor can be in one of two states: attached or unattached. It is
+/// in the attached state if, and only if is_attached() returns true. Most
+/// non-static member functions of this class have undefined behaviour if the
+/// accessor is in the unattached state. The exceptions are: is_attached(),
+/// detach(), create(), init_from_ref(), init_from_mem(), init_from_parent(),
+/// has_parent(), get_parent(), set_parent(), get_ndx_in_parent(),
+/// set_ndx_in_parent(), adjust_ndx_in_parent(), and get_ref_from_parent().
+///
+/// An array accessor contains information about the parent of the referenced
+/// array node. This 'reverse' reference is not explicitely present in the
+/// underlying node hierarchy, but it is needed when modifying an array. A
+/// modification may lead to relocation of the underlying array node, and the
+/// parent must be updated accordingly. Since this applies recursivly all the
+/// way to the root node, it is essential that the entire chain of parent
+/// accessors is constructed and propperly maintained when a particular array is
+/// modified.
+///
+/// The parent reference (`pointer to parent`, `index in parent`) is updated
+/// independently from the state of attachment to an underlying node. In
+/// particular, the parent reference remains valid and is unannfected by changes
+/// in attachment. These two aspects of the state of the accessor is updated
+/// independently, and it is entirely the responsibility of the caller to update
+/// them such that they are consistent with the underlying node hierarchy before
+/// calling any method that modifies the underlying array node.
+///
+/// FIXME: This class currently has fragments of ownership, in particular the
+/// constructors that allocate underlying memory. On the other hand, the
+/// destructor never frees the memory. This is a problematic situation, because
+/// it so easily becomes an obscure source of leaks. There are three options for
+/// a fix of which the third is most attractive but hardest to implement: (1)
+/// Remove all traces of ownership semantics, that is, remove the constructors
+/// that allocate memory, but keep the trivial copy constructor. For this to
+/// work, it is important that the constness of the accessor has nothing to do
+/// with the constness of the underlying memory, otherwise constness can be
+/// violated simply by copying the accessor. (2) Disallov copying but associate
+/// the constness of the accessor with the constness of the underlying
+/// memory. (3) Provide full ownership semantics like is done for Table
+/// accessors, and provide a proper copy constructor that really produces a copy
+/// of the array. For this to work, the class should assume ownership if, and
+/// only if there is no parent. A copy produced by a copy constructor will not
+/// have a parent. Even if the original was part of a database, the copy will be
+/// free-standing, that is, not be part of any database. For intra, or inter
+/// database copying, one would have to also specify the target allocator.
+class Array : public ArrayParent {
+public:
+ // void state_init(int action, QueryState *state);
+ // bool match(int action, size_t index, int64_t value, QueryState *state);
+
+ /// Create an array accessor in the unattached state.
+ explicit Array(Allocator&) noexcept;
+
+ ~Array() noexcept override
+ {
+ }
+
+ enum Type {
+ type_Normal,
+
+ /// This array is the main array of an innner node of a B+-tree as used
+ /// in table columns.
+ type_InnerBptreeNode,
+
+ /// This array may contain refs to subarrays. An element whose least
+ /// significant bit is zero, is a ref pointing to a subarray. An element
+ /// whose least significant bit is one, is just a value. It is the
+ /// responsibility of the application to ensure that non-ref values have
+ /// their least significant bit set. This will generally be done by
+ /// shifting the desired vlue to the left by one bit position, and then
+ /// setting the vacated bit to one.
+ type_HasRefs
+ };
+
+ /// Create a new integer array of the specified type and size, and filled
+ /// with the specified value, and attach this accessor to it. This does not
+ /// modify the parent reference information of this accessor.
+ ///
+ /// Note that the caller assumes ownership of the allocated underlying
+ /// node. It is not owned by the accessor.
+ void create(Type, bool context_flag = false, size_t size = 0, int_fast64_t value = 0);
+
+ /// Reinitialize this array accessor to point to the specified new
+ /// underlying memory. This does not modify the parent reference information
+ /// of this accessor.
+ void init_from_ref(ref_type) noexcept;
+
+ /// Same as init_from_ref(ref_type) but avoid the mapping of 'ref' to memory
+ /// pointer.
+ void init_from_mem(MemRef) noexcept;
+
+ /// Same as `init_from_ref(get_ref_from_parent())`.
+ void init_from_parent() noexcept;
+
+ /// Update the parents reference to this child. This requires, of course,
+ /// that the parent information stored in this child is up to date. If the
+ /// parent pointer is set to null, this function has no effect.
+ void update_parent();
+
+ /// Called in the context of Group::commit() to ensure that attached
+ /// accessors stay valid across a commit. Please note that this works only
+ /// for non-transactional commits. Accessors obtained during a transaction
+ /// are always detached when the transaction ends.
+ ///
+ /// Returns true if, and only if the array has changed. If the array has not
+ /// changed, then its children are guaranteed to also not have changed.
+ bool update_from_parent(size_t old_baseline) noexcept;
+
+ /// Change the type of an already attached array node.
+ ///
+ /// The effect of calling this function on an unattached accessor is
+ /// undefined.
+ void set_type(Type);
+
+ /// Construct a complete copy of this array (including its subarrays) using
+ /// the specified target allocator and return just the reference to the
+ /// underlying memory.
+ MemRef clone_deep(Allocator& target_alloc) const;
+
+ /// Construct an empty integer array of the specified type, and return just
+ /// the reference to the underlying memory.
+ static MemRef create_empty_array(Type, bool context_flag, Allocator&);
+
+ /// Construct an integer array of the specified type and size, and return
+ /// just the reference to the underlying memory. All elements will be
+ /// initialized to the specified value.
+ static MemRef create_array(Type, bool context_flag, size_t size, int_fast64_t value, Allocator&);
+
+ /// Construct a shallow copy of the specified slice of this array using the
+ /// specified target allocator. Subarrays will **not** be cloned. See
+ /// slice_and_clone_children() for an alternative.
+ MemRef slice(size_t offset, size_t slice_size, Allocator& target_alloc) const;
+
+ /// Construct a deep copy of the specified slice of this array using the
+ /// specified target allocator. Subarrays will be cloned.
+ MemRef slice_and_clone_children(size_t offset, size_t slice_size, Allocator& target_alloc) const;
+
+ // Parent tracking
+ bool has_parent() const noexcept;
+ ArrayParent* get_parent() const noexcept;
+
+ /// Setting a new parent affects ownership of the attached array node, if
+ /// any. If a non-null parent is specified, and there was no parent
+ /// originally, then the caller passes ownership to the parent, and vice
+ /// versa. This assumes, of course, that the change in parentship reflects a
+ /// corresponding change in the list of children in the affected parents.
+ void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept;
+
+ size_t get_ndx_in_parent() const noexcept;
+ void set_ndx_in_parent(size_t) noexcept;
+ void adjust_ndx_in_parent(int diff) noexcept;
+
+ /// Get the ref of this array as known to the parent. The caller must ensure
+ /// that the parent information ('pointer to parent' and 'index in parent')
+ /// is correct before calling this function.
+ ref_type get_ref_from_parent() const noexcept;
+
+ bool is_attached() const noexcept;
+
+ /// Detach from the underlying array node. This method has no effect if the
+ /// accessor is currently unattached (idempotency).
+ void detach() noexcept;
+
+ size_t size() const noexcept;
+ bool is_empty() const noexcept;
+ Type get_type() const noexcept;
+
+ static void add_to_column(IntegerColumn* column, int64_t value);
+
+ void insert(size_t ndx, int_fast64_t value);
+ void add(int_fast64_t value);
+
+ /// This function is guaranteed to not throw if the current width is
+ /// sufficient for the specified value (e.g. if you have called
+ /// ensure_minimum_width(value)) and get_alloc().is_read_only(get_ref())
+ /// returns false (noexcept:array-set). Note that for a value of zero, the
+ /// first criterion is trivially satisfied.
+ void set(size_t ndx, int64_t value);
+
+ void set_as_ref(size_t ndx, ref_type ref);
+
+ template <size_t w>
+ void set(size_t ndx, int64_t value);
+
+ int64_t get(size_t ndx) const noexcept;
+
+ template <size_t w>
+ int64_t get(size_t ndx) const noexcept;
+
+ void get_chunk(size_t ndx, int64_t res[8]) const noexcept;
+
+ template <size_t w>
+ void get_chunk(size_t ndx, int64_t res[8]) const noexcept;
+
+ ref_type get_as_ref(size_t ndx) const noexcept;
+
+ RefOrTagged get_as_ref_or_tagged(size_t ndx) const noexcept;
+ void set(size_t ndx, RefOrTagged);
+ void add(RefOrTagged);
+ void ensure_minimum_width(RefOrTagged);
+
+ int64_t front() const noexcept;
+ int64_t back() const noexcept;
+
+ /// Remove the element at the specified index, and move elements at higher
+ /// indexes to the next lower index.
+ ///
+ /// This function does **not** destroy removed subarrays. That is, if the
+ /// erased element is a 'ref' pointing to a subarray, then that subarray
+ /// will not be destroyed automatically.
+ ///
+ /// This function guarantees that no exceptions will be thrown if
+ /// get_alloc().is_read_only(get_ref()) would return false before the
+ /// call. This is automatically guaranteed if the array is used in a
+ /// non-transactional context, or if the array has already been successfully
+ /// modified within the current write transaction.
+ void erase(size_t ndx);
+
+ /// Same as erase(size_t), but remove all elements in the specified
+ /// range.
+ ///
+ /// Please note that this function does **not** destroy removed subarrays.
+ ///
+ /// This function guarantees that no exceptions will be thrown if
+ /// get_alloc().is_read_only(get_ref()) would return false before the call.
+ void erase(size_t begin, size_t end);
+
+ /// Reduce the size of this array to the specified number of elements. It is
+ /// an error to specify a size that is greater than the current size of this
+ /// array. The effect of doing so is undefined. This is just a shorthand for
+ /// calling the ranged erase() function with appropriate arguments.
+ ///
+ /// Please note that this function does **not** destroy removed
+ /// subarrays. See clear_and_destroy_children() for an alternative.
+ ///
+ /// This function guarantees that no exceptions will be thrown if
+ /// get_alloc().is_read_only(get_ref()) would return false before the call.
+ void truncate(size_t new_size);
+
+ /// Reduce the size of this array to the specified number of elements. It is
+ /// an error to specify a size that is greater than the current size of this
+ /// array. The effect of doing so is undefined. Subarrays will be destroyed
+ /// recursively, as if by a call to `destroy_deep(subarray_ref, alloc)`.
+ ///
+ /// This function is guaranteed not to throw if
+ /// get_alloc().is_read_only(get_ref()) returns false.
+ void truncate_and_destroy_children(size_t new_size);
+
+ /// Remove every element from this array. This is just a shorthand for
+ /// calling truncate(0).
+ ///
+ /// Please note that this function does **not** destroy removed
+ /// subarrays. See clear_and_destroy_children() for an alternative.
+ ///
+ /// This function guarantees that no exceptions will be thrown if
+ /// get_alloc().is_read_only(get_ref()) would return false before the call.
+ void clear();
+
+ /// Remove every element in this array. Subarrays will be destroyed
+ /// recursively, as if by a call to `destroy_deep(subarray_ref,
+ /// alloc)`. This is just a shorthand for calling
+ /// truncate_and_destroy_children(0).
+ ///
+ /// This function guarantees that no exceptions will be thrown if
+ /// get_alloc().is_read_only(get_ref()) would return false before the call.
+ void clear_and_destroy_children();
+
+ /// If neccessary, expand the representation so that it can store the
+ /// specified value.
+ void ensure_minimum_width(int_fast64_t value);
+
+ /// This one may change the represenation of the array, so be carefull if
+ /// you call it after ensure_minimum_width().
+ void set_all_to_zero();
+
+ /// Add \a diff to the element at the specified index.
+ void adjust(size_t ndx, int_fast64_t diff);
+
+ /// Add \a diff to all the elements in the specified index range.
+ void adjust(size_t begin, size_t end, int_fast64_t diff);
+
+ /// Add signed \a diff to all elements that are greater than, or equal to \a
+ /// limit.
+ void adjust_ge(int_fast64_t limit, int_fast64_t diff);
+
+ //@{
+ /// These are similar in spirit to std::move() and std::move_backward from
+ /// `<algorithm>`. \a dest_begin must not be in the range [`begin`,`end`), and
+ /// \a dest_end must not be in the range (`begin`,`end`].
+ ///
+ /// These functions are guaranteed to not throw if
+ /// `get_alloc().is_read_only(get_ref())` returns false.
+ void move(size_t begin, size_t end, size_t dest_begin);
+ void move_backward(size_t begin, size_t end, size_t dest_end);
+ //@}
+
+ /// move_rotate moves one element from \a from to be located at index \a to,
+ /// shifting all elements inbetween by one.
+ ///
+ /// If \a from is larger than \a to, the elements inbetween are shifted down.
+ /// If \a to is larger than \a from, the elements inbetween are shifted up.
+ ///
+ /// This function is guaranteed to not throw if
+ /// `get_alloc().is_read_only(get_ref())` returns false.
+ void move_rotate(size_t from, size_t to, size_t num_elems = 1);
+
+ //@{
+ /// Find the lower/upper bound of the specified value in a sequence of
+ /// integers which must already be sorted ascendingly.
+ ///
+ /// For an integer value '`v`', lower_bound_int(v) returns the index '`l`'
+ /// of the first element such that `get(l) ≥ v`, and upper_bound_int(v)
+ /// returns the index '`u`' of the first element such that `get(u) >
+ /// v`. In both cases, if no such element is found, the returned value is
+ /// the number of elements in the array.
+ ///
+ /// 3 3 3 4 4 4 5 6 7 9 9 9
+ /// ^ ^ ^ ^ ^
+ /// | | | | |
+ /// | | | | -- Lower and upper bound of 15
+ /// | | | |
+ /// | | | -- Lower and upper bound of 8
+ /// | | |
+ /// | | -- Upper bound of 4
+ /// | |
+ /// | -- Lower bound of 4
+ /// |
+ /// -- Lower and upper bound of 1
+ ///
+ /// These functions are similar to std::lower_bound() and
+ /// std::upper_bound().
+ ///
+ /// We currently use binary search. See for example
+ /// http://www.tbray.org/ongoing/When/200x/2003/03/22/Binary.
+ ///
+ /// FIXME: It may be worth considering if overall efficiency can be improved
+ /// by doing a linear search for short sequences.
+ size_t lower_bound_int(int64_t value) const noexcept;
+ size_t upper_bound_int(int64_t value) const noexcept;
+ //@}
+
+ /// \brief Search the \c Array for a value greater or equal than \a target,
+ /// starting the search at the \a start index. If \a indirection is
+ /// provided, use it as a look-up table to iterate over the \c Array.
+ ///
+ /// If \a indirection is not provided, then the \c Array must be sorted in
+ /// ascending order. If \a indirection is provided, then its values should
+ /// point to indices in this \c Array in such a way that iteration happens
+ /// in ascending order.
+ ///
+ /// Behaviour is undefined if:
+ /// - a value in \a indirection is out of bounds for this \c Array;
+ /// - \a indirection does not contain at least as many elements as this \c
+ /// Array;
+ /// - sorting conditions are not respected;
+ /// - \a start is greater than the number of elements in this \c Array or
+ /// \a indirection (if provided).
+ ///
+ /// \param target the smallest value to search for
+ /// \param start the offset at which to start searching in the array
+ /// \param indirection an \c Array containing valid indices of values in
+ /// this \c Array, sorted in ascending order
+ /// \return the index of the value if found, or realm::not_found otherwise
+ size_t find_gte(const int64_t target, size_t start, size_t end = size_t(-1)) const;
+
+ void preset(int64_t min, int64_t max, size_t num_items);
+ void preset(size_t bitwidth, size_t num_items);
+
+ int64_t sum(size_t start = 0, size_t end = size_t(-1)) const;
+ size_t count(int64_t value) const noexcept;
+
+ bool maximum(int64_t& result, size_t start = 0, size_t end = size_t(-1), size_t* return_ndx = nullptr) const;
+
+ bool minimum(int64_t& result, size_t start = 0, size_t end = size_t(-1), size_t* return_ndx = nullptr) const;
+
+ /// This information is guaranteed to be cached in the array accessor.
+ bool is_inner_bptree_node() const noexcept;
+
+ /// Returns true if type is either type_HasRefs or type_InnerColumnNode.
+ ///
+ /// This information is guaranteed to be cached in the array accessor.
+ bool has_refs() const noexcept;
+ void set_has_refs(bool) noexcept;
+
+ /// This information is guaranteed to be cached in the array accessor.
+ ///
+ /// Columns and indexes can use the context bit to differentiate leaf types.
+ bool get_context_flag() const noexcept;
+ void set_context_flag(bool) noexcept;
+
+ ref_type get_ref() const noexcept;
+ MemRef get_mem() const noexcept;
+
+ /// Destroy only the array that this accessor is attached to, not the
+ /// children of that array. See non-static destroy_deep() for an
+ /// alternative. If this accessor is already in the detached state, this
+ /// function has no effect (idempotency).
+ void destroy() noexcept;
+
+ /// Recursively destroy children (as if calling
+ /// clear_and_destroy_children()), then put this accessor into the detached
+ /// state (as if calling detach()), then free the allocated memory. If this
+ /// accessor is already in the detached state, this function has no effect
+ /// (idempotency).
+ void destroy_deep() noexcept;
+
+ /// Shorthand for `destroy(MemRef(ref, alloc), alloc)`.
+ static void destroy(ref_type ref, Allocator& alloc) noexcept;
+
+ /// Destroy only the specified array node, not its children. See also
+ /// destroy_deep(MemRef, Allocator&).
+ static void destroy(MemRef, Allocator&) noexcept;
+
+ /// Shorthand for `destroy_deep(MemRef(ref, alloc), alloc)`.
+ static void destroy_deep(ref_type ref, Allocator& alloc) noexcept;
+
+ /// Destroy the specified array node and all of its children, recursively.
+ ///
+ /// This is done by freeing the specified array node after calling
+ /// destroy_deep() for every contained 'ref' element.
+ static void destroy_deep(MemRef, Allocator&) noexcept;
+
+ Allocator& get_alloc() const noexcept
+ {
+ return m_alloc;
+ }
+
+ // Serialization
+
+ /// Returns the ref (position in the target stream) of the written copy of
+ /// this array, or the ref of the original array if \a only_if_modified is
+ /// true, and this array is unmodified (Alloc::is_read_only()).
+ ///
+ /// The number of bytes that will be written by a non-recursive invocation
+ /// of this function is exactly the number returned by get_byte_size().
+ ///
+ /// \param out The destination stream (writer).
+ ///
+ /// \param deep If true, recursively write out subarrays, but still subject
+ /// to \a only_if_modified.
+ ///
+ /// \param only_if_modified Set to `false` to always write, or to `true` to
+ /// only write the array if it has been modified.
+ ref_type write(_impl::ArrayWriterBase& out, bool deep, bool only_if_modified) const;
+
+ /// Same as non-static write() with `deep` set to true. This is for the
+ /// cases where you do not already have an array accessor available.
+ static ref_type write(ref_type, Allocator&, _impl::ArrayWriterBase&, bool only_if_modified);
+
+ // Main finding function - used for find_first, find_all, sum, max, min, etc.
+ bool find(int cond, Action action, int64_t value, size_t start, size_t end, size_t baseindex,
+ QueryState<int64_t>* state, bool nullable_array = false, bool find_null = false) const;
+
+ // Templated find function to avoid conversion to and from integer represenation of condition
+ template <class cond>
+ bool find(Action action, int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+ bool nullable_array = false, bool find_null = false) const
+ {
+ if (action == act_ReturnFirst) {
+ REALM_TEMPEX3(return find, cond, act_ReturnFirst, m_width,
+ (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null))
+ }
+ else if (action == act_Sum) {
+ REALM_TEMPEX3(return find, cond, act_Sum, m_width,
+ (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null))
+ }
+ else if (action == act_Min) {
+ REALM_TEMPEX3(return find, cond, act_Min, m_width,
+ (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null))
+ }
+ else if (action == act_Max) {
+ REALM_TEMPEX3(return find, cond, act_Max, m_width,
+ (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null))
+ }
+ else if (action == act_Count) {
+ REALM_TEMPEX3(return find, cond, act_Count, m_width,
+ (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null))
+ }
+ else if (action == act_FindAll) {
+ REALM_TEMPEX3(return find, cond, act_FindAll, m_width,
+ (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null))
+ }
+ else if (action == act_CallbackIdx) {
+ REALM_TEMPEX3(return find, cond, act_CallbackIdx, m_width,
+ (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null))
+ }
+ REALM_ASSERT_DEBUG(false);
+ return false;
+ }
+
+
+ /*
+ bool find(int cond, Action action, null, size_t start, size_t end, size_t baseindex,
+ QueryState<int64_t>* state) const;
+ */
+
+ template <class cond, Action action, size_t bitwidth, class Callback>
+ bool find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+ Callback callback, bool nullable_array = false, bool find_null = false) const;
+
+ // This is the one installed into the m_vtable->finder slots.
+ template <class cond, Action action, size_t bitwidth>
+ bool find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state) const;
+
+ template <class cond, Action action, class Callback>
+ bool find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+ Callback callback, bool nullable_array = false, bool find_null = false) const;
+
+ /*
+ template <class cond, Action action, class Callback>
+ bool find(null, size_t start, size_t end, size_t baseindex,
+ QueryState<int64_t>* state, Callback callback) const;
+ */
+
+ // Optimized implementation for release mode
+ template <class cond, Action action, size_t bitwidth, class Callback>
+ bool find_optimized(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+ Callback callback, bool nullable_array = false, bool find_null = false) const;
+
+ // Called for each search result
+ template <Action action, class Callback>
+ bool find_action(size_t index, util::Optional<int64_t> value, QueryState<int64_t>* state,
+ Callback callback) const;
+
+ template <Action action, class Callback>
+ bool find_action_pattern(size_t index, uint64_t pattern, QueryState<int64_t>* state, Callback callback) const;
+
+ // Wrappers for backwards compatibility and for simple use without
+ // setting up state initialization etc
+ template <class cond>
+ size_t find_first(int64_t value, size_t start = 0, size_t end = size_t(-1)) const;
+
+ void find_all(IntegerColumn* result, int64_t value, size_t col_offset = 0, size_t begin = 0,
+ size_t end = size_t(-1)) const;
+
+ size_t find_first(int64_t value, size_t begin = 0, size_t end = size_t(-1)) const;
+
+ // Non-SSE find for the four functions Equal/NotEqual/Less/Greater
+ template <class cond, Action action, size_t bitwidth, class Callback>
+ bool compare(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+ Callback callback) const;
+
+ // Non-SSE find for Equal/NotEqual
+ template <bool eq, Action action, size_t width, class Callback>
+ inline bool compare_equality(int64_t value, size_t start, size_t end, size_t baseindex,
+ QueryState<int64_t>* state, Callback callback) const;
+
+ // Non-SSE find for Less/Greater
+ template <bool gt, Action action, size_t bitwidth, class Callback>
+ bool compare_relation(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+ Callback callback) const;
+
+ template <class cond, Action action, size_t foreign_width, class Callback, size_t width>
+ bool compare_leafs_4(const Array* foreign, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+ Callback callback) const;
+
+ template <class cond, Action action, class Callback, size_t bitwidth, size_t foreign_bitwidth>
+ bool compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+ Callback callback) const;
+
+ template <class cond, Action action, class Callback>
+ bool compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+ Callback callback) const;
+
+ template <class cond, Action action, size_t width, class Callback>
+ bool compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+ Callback callback) const;
+
+// SSE find for the four functions Equal/NotEqual/Less/Greater
+#ifdef REALM_COMPILER_SSE
+ template <class cond, Action action, size_t width, class Callback>
+ bool find_sse(int64_t value, __m128i* data, size_t items, QueryState<int64_t>* state, size_t baseindex,
+ Callback callback) const;
+
+ template <class cond, Action action, size_t width, class Callback>
+ REALM_FORCEINLINE bool find_sse_intern(__m128i* action_data, __m128i* data, size_t items,
+ QueryState<int64_t>* state, size_t baseindex, Callback callback) const;
+
+#endif
+
+ template <size_t width>
+ inline bool test_zero(uint64_t value) const; // Tests value for 0-elements
+
+ template <bool eq, size_t width>
+ size_t find_zero(uint64_t v) const; // Finds position of 0/non-zero element
+
+ template <size_t width, bool zero>
+ uint64_t cascade(uint64_t a) const; // Sets lowermost bits of zero or non-zero elements
+
+ template <bool gt, size_t width>
+ int64_t
+ find_gtlt_magic(int64_t v) const; // Compute magic constant needed for searching for value 'v' using bit hacks
+
+ template <size_t width>
+ inline int64_t lower_bits() const; // Return chunk with lower bit set in each element
+
+ size_t first_set_bit(unsigned int v) const;
+ size_t first_set_bit64(int64_t v) const;
+
+ template <size_t w>
+ int64_t get_universal(const char* const data, const size_t ndx) const;
+
+ // Find value greater/less in 64-bit chunk - only works for positive values
+ template <bool gt, Action action, size_t width, class Callback>
+ bool find_gtlt_fast(uint64_t chunk, uint64_t magic, QueryState<int64_t>* state, size_t baseindex,
+ Callback callback) const;
+
+ // Find value greater/less in 64-bit chunk - no constraints
+ template <bool gt, Action action, size_t width, class Callback>
+ bool find_gtlt(int64_t v, uint64_t chunk, QueryState<int64_t>* state, size_t baseindex, Callback callback) const;
+
+ ref_type bptree_leaf_insert(size_t ndx, int64_t, TreeInsertBase& state);
+
+ /// Get the specified element without the cost of constructing an
+ /// array instance. If an array instance is already available, or
+ /// you need to get multiple values, then this method will be
+ /// slower.
+ static int_fast64_t get(const char* header, size_t ndx) noexcept;
+
+ /// Like get(const char*, size_t) but gets two consecutive
+ /// elements.
+ static std::pair<int64_t, int64_t> get_two(const char* header, size_t ndx) noexcept;
+
+ static void get_three(const char* data, size_t ndx, ref_type& v0, ref_type& v1, ref_type& v2) noexcept;
+
+ /// The meaning of 'width' depends on the context in which this
+ /// array is used.
+ size_t get_width() const noexcept
+ {
+ return m_width;
+ }
+
+ static char* get_data_from_header(char*) noexcept;
+ static char* get_header_from_data(char*) noexcept;
+ static const char* get_data_from_header(const char*) noexcept;
+
+ enum WidthType {
+ wtype_Bits = 0,
+ wtype_Multiply = 1,
+ wtype_Ignore = 2,
+ };
+
+ static bool get_is_inner_bptree_node_from_header(const char*) noexcept;
+ static bool get_hasrefs_from_header(const char*) noexcept;
+ static bool get_context_flag_from_header(const char*) noexcept;
+ static WidthType get_wtype_from_header(const char*) noexcept;
+ static uint_least8_t get_width_from_header(const char*) noexcept;
+ static size_t get_size_from_header(const char*) noexcept;
+
+ static Type get_type_from_header(const char*) noexcept;
+
+ /// Get the number of bytes currently in use by this array. This
+ /// includes the array header, but it does not include allocated
+ /// bytes corresponding to excess capacity. The result is
+ /// guaranteed to be a multiple of 8 (i.e., 64-bit aligned).
+ ///
+ /// This number is exactly the number of bytes that will be
+ /// written by a non-recursive invocation of write().
+ size_t get_byte_size() const noexcept;
+
+ /// Get the maximum number of bytes that can be written by a
+ /// non-recursive invocation of write() on an array with the
+ /// specified number of elements, that is, the maximum value that
+ /// can be returned by get_byte_size().
+ static size_t get_max_byte_size(size_t num_elems) noexcept;
+
+ /// FIXME: Belongs in IntegerArray
+ static size_t calc_aligned_byte_size(size_t size, int width);
+
+ class MemUsageHandler {
+ public:
+ virtual void handle(ref_type ref, size_t allocated, size_t used) = 0;
+ };
+
+ void report_memory_usage(MemUsageHandler&) const;
+
+ void stats(MemStats& stats_dest) const noexcept;
+
+#ifdef REALM_DEBUG
+ void print() const;
+ void verify() const;
+ typedef size_t (*LeafVerifier)(MemRef, Allocator&);
+ void verify_bptree(LeafVerifier) const;
+ typedef void (*LeafDumper)(MemRef, Allocator&, std::ostream&, int level);
+ void dump_bptree_structure(std::ostream&, int level, LeafDumper) const;
+ void to_dot(std::ostream&, StringData title = StringData()) const;
+ class ToDotHandler {
+ public:
+ virtual void to_dot(MemRef leaf_mem, ArrayParent*, size_t ndx_in_parent, std::ostream&) = 0;
+ ~ToDotHandler()
+ {
+ }
+ };
+ void bptree_to_dot(std::ostream&, ToDotHandler&) const;
+ void to_dot_parent_edge(std::ostream&) const;
+#endif
+
+ static const int header_size = 8; // Number of bytes used by header
+
+ // The encryption layer relies on headers always fitting within a single page.
+ static_assert(header_size == 8, "Header must always fit in entirely on a page");
+
+ Array& operator=(const Array&) = delete; // not allowed
+ Array(const Array&) = delete; // not allowed
+protected:
+ typedef bool (*CallbackDummy)(int64_t);
+
+protected:
+ // Includes array header. Not necessarily 8-byte aligned.
+ virtual size_t calc_byte_len(size_t num_items, size_t width) const;
+
+ virtual size_t calc_item_count(size_t bytes, size_t width) const noexcept;
+
+ bool get_is_inner_bptree_node_from_header() const noexcept;
+ bool get_hasrefs_from_header() const noexcept;
+ bool get_context_flag_from_header() const noexcept;
+ WidthType get_wtype_from_header() const noexcept;
+ uint_least8_t get_width_from_header() const noexcept;
+ size_t get_size_from_header() const noexcept;
+
+ // Undefined behavior if m_alloc.is_read_only(m_ref) returns true
+ size_t get_capacity_from_header() const noexcept;
+
+ void set_header_is_inner_bptree_node(bool value) noexcept;
+ void set_header_hasrefs(bool value) noexcept;
+ void set_header_context_flag(bool value) noexcept;
+ void set_header_wtype(WidthType value) noexcept;
+ void set_header_width(int value) noexcept;
+ void set_header_size(size_t value) noexcept;
+ void set_header_capacity(size_t value) noexcept;
+
+ static void set_header_is_inner_bptree_node(bool value, char* header) noexcept;
+ static void set_header_hasrefs(bool value, char* header) noexcept;
+ static void set_header_context_flag(bool value, char* header) noexcept;
+ static void set_header_wtype(WidthType value, char* header) noexcept;
+ static void set_header_width(int value, char* header) noexcept;
+ static void set_header_size(size_t value, char* header) noexcept;
+ static void set_header_capacity(size_t value, char* header) noexcept;
+
+ static void init_header(char* header, bool is_inner_bptree_node, bool has_refs, bool context_flag,
+ WidthType width_type, int width, size_t size, size_t capacity) noexcept;
+
+
+ // This returns the minimum value ("lower bound") of the representable values
+ // for the given bit width. Valid widths are 0, 1, 2, 4, 8, 16, 32, and 64.
+ template <size_t width>
+ static int_fast64_t lbound_for_width() noexcept;
+
+ static int_fast64_t lbound_for_width(size_t width) noexcept;
+
+ // This returns the maximum value ("inclusive upper bound") of the representable values
+ // for the given bit width. Valid widths are 0, 1, 2, 4, 8, 16, 32, and 64.
+ template <size_t width>
+ static int_fast64_t ubound_for_width() noexcept;
+
+ static int_fast64_t ubound_for_width(size_t width) noexcept;
+
+ template <size_t width>
+ void set_width() noexcept;
+ void set_width(size_t) noexcept;
+ void alloc(size_t init_size, size_t width);
+ void copy_on_write();
+
+private:
+ void do_copy_on_write(size_t minimum_size = 0);
+ void do_ensure_minimum_width(int_fast64_t);
+
+ template <size_t w>
+ int64_t sum(size_t start, size_t end) const;
+
+ template <bool max, size_t w>
+ bool minmax(int64_t& result, size_t start, size_t end, size_t* return_ndx) const;
+
+ template <size_t w>
+ size_t find_gte(const int64_t target, size_t start, size_t end) const;
+
+ template <size_t w>
+ size_t adjust_ge(size_t start, size_t end, int_fast64_t limit, int_fast64_t diff);
+
+protected:
+ /// The total size in bytes (including the header) of a new empty
+ /// array. Must be a multiple of 8 (i.e., 64-bit aligned).
+ static const size_t initial_capacity = 128;
+
+ /// It is an error to specify a non-zero value unless the width
+ /// type is wtype_Bits. It is also an error to specify a non-zero
+ /// size if the width type is wtype_Ignore.
+ static MemRef create(Type, bool context_flag, WidthType, size_t size, int_fast64_t value, Allocator&);
+
+ static MemRef clone(MemRef header, Allocator& alloc, Allocator& target_alloc);
+
+ /// Get the address of the header of this array.
+ char* get_header() noexcept;
+
+ /// Same as get_byte_size().
+ static size_t get_byte_size_from_header(const char*) noexcept;
+
+ // Undefined behavior if array is in immutable memory
+ static size_t get_capacity_from_header(const char*) noexcept;
+
+ // Overriding method in ArrayParent
+ void update_child_ref(size_t, ref_type) override;
+
+ // Overriding method in ArrayParent
+ ref_type get_child_ref(size_t) const noexcept override;
+
+ void destroy_children(size_t offset = 0) noexcept;
+
+ std::pair<ref_type, size_t> get_to_dot_parent(size_t ndx_in_parent) const override;
+
+ bool is_read_only() const noexcept;
+
+protected:
+ // Getters and Setters for adaptive-packed arrays
+ typedef int64_t (Array::*Getter)(size_t) const; // Note: getters must not throw
+ typedef void (Array::*Setter)(size_t, int64_t);
+ typedef bool (Array::*Finder)(int64_t, size_t, size_t, size_t, QueryState<int64_t>*) const;
+ typedef void (Array::*ChunkGetter)(size_t, int64_t res[8]) const; // Note: getters must not throw
+
+ struct VTable {
+ Getter getter;
+ ChunkGetter chunk_getter;
+ Setter setter;
+ Finder finder[cond_VTABLE_FINDER_COUNT]; // one for each active function pointer
+ };
+ template <size_t w>
+ struct VTableForWidth;
+
+protected:
+ /// Takes a 64-bit value and returns the minimum number of bits needed
+ /// to fit the value. For alignment this is rounded up to nearest
+ /// log2. Posssible results {0, 1, 2, 4, 8, 16, 32, 64}
+ static size_t bit_width(int64_t value);
+
+ void report_memory_usage_2(MemUsageHandler&) const;
+
+private:
+ Getter m_getter = nullptr; // cached to avoid indirection
+ const VTable* m_vtable = nullptr;
+
+public:
+ // FIXME: Should not be public
+ char* m_data = nullptr; // Points to first byte after header
+
+#if REALM_ENABLE_MEMDEBUG
+ // If m_no_relocation is false, then copy_on_write() will always relocate this array, regardless if it's
+ // required or not. If it's true, then it will never relocate, which is currently only expeted inside
+ // GroupWriter::write_group() due to a unique chicken/egg problem (see description there).
+ bool m_no_relocation = false;
+#endif
+
+protected:
+ int64_t m_lbound; // min number that can be stored with current m_width
+ int64_t m_ubound; // max number that can be stored with current m_width
+
+ size_t m_size = 0; // Number of elements currently stored.
+ size_t m_capacity = 0; // Number of elements that fit inside the allocated memory.
+
+ Allocator& m_alloc;
+
+private:
+ size_t m_ref;
+ ArrayParent* m_parent = nullptr;
+ size_t m_ndx_in_parent = 0; // Ignored if m_parent is null.
+
+protected:
+ uint_least8_t m_width = 0; // Size of an element (meaning depend on type of array).
+ bool m_is_inner_bptree_node; // This array is an inner node of B+-tree.
+ bool m_has_refs; // Elements whose first bit is zero are refs to subarrays.
+ bool m_context_flag; // Meaning depends on context.
+
+private:
+ ref_type do_write_shallow(_impl::ArrayWriterBase&) const;
+ ref_type do_write_deep(_impl::ArrayWriterBase&, bool only_if_modified) const;
+ static size_t calc_byte_size(WidthType wtype, size_t size, uint_least8_t width) noexcept;
+
+ friend class SlabAlloc;
+ friend class GroupWriter;
+ friend class StringColumn;
+};
+
+
+// Implementation:
+
+class QueryStateBase {
+ virtual void dyncast()
+ {
+ }
+};
+
+template <>
+class QueryState<int64_t> : public QueryStateBase {
+public:
+ int64_t m_state;
+ size_t m_match_count;
+ size_t m_limit;
+ size_t m_minmax_index; // used only for min/max, to save index of current min/max value
+
+ template <Action action>
+ bool uses_val()
+ {
+ if (action == act_Max || action == act_Min || action == act_Sum)
+ return true;
+ else
+ return false;
+ }
+
+ void init(Action action, IntegerColumn* akku, size_t limit)
+ {
+ m_match_count = 0;
+ m_limit = limit;
+ m_minmax_index = not_found;
+
+ if (action == act_Max)
+ m_state = -0x7fffffffffffffffLL - 1LL;
+ else if (action == act_Min)
+ m_state = 0x7fffffffffffffffLL;
+ else if (action == act_ReturnFirst)
+ m_state = not_found;
+ else if (action == act_Sum)
+ m_state = 0;
+ else if (action == act_Count)
+ m_state = 0;
+ else if (action == act_FindAll)
+ m_state = reinterpret_cast<int64_t>(akku);
+ else if (action == act_CallbackIdx) {
+ }
+ else {
+ REALM_ASSERT_DEBUG(false);
+ }
+ }
+
+ template <Action action, bool pattern>
+ inline bool match(size_t index, uint64_t indexpattern, int64_t value)
+ {
+ if (pattern) {
+ if (action == act_Count) {
+ // If we are close to 'limit' argument in query, we cannot count-up a complete chunk. Count up single
+ // elements instead
+ if (m_match_count + 64 >= m_limit)
+ return false;
+
+ m_state += fast_popcount64(indexpattern);
+ m_match_count = size_t(m_state);
+ return true;
+ }
+ // Other aggregates cannot (yet) use bit pattern for anything. Make Array-finder call with pattern = false
+ // instead
+ return false;
+ }
+
+ ++m_match_count;
+
+ if (action == act_Max) {
+ if (value > m_state) {
+ m_state = value;
+ m_minmax_index = index;
+ }
+ }
+ else if (action == act_Min) {
+ if (value < m_state) {
+ m_state = value;
+ m_minmax_index = index;
+ }
+ }
+ else if (action == act_Sum)
+ m_state += value;
+ else if (action == act_Count) {
+ m_state++;
+ m_match_count = size_t(m_state);
+ }
+ else if (action == act_FindAll) {
+ Array::add_to_column(reinterpret_cast<IntegerColumn*>(m_state), index);
+ }
+ else if (action == act_ReturnFirst) {
+ m_state = index;
+ return false;
+ }
+ else {
+ REALM_ASSERT_DEBUG(false);
+ }
+ return (m_limit > m_match_count);
+ }
+
+ template <Action action, bool pattern>
+ inline bool match(size_t index, uint64_t indexpattern, util::Optional<int64_t> value)
+ {
+ // FIXME: This is a temporary hack for nullable integers.
+ if (value) {
+ return match<action, pattern>(index, indexpattern, *value);
+ }
+
+ // If value is null, the only sensible actions are count, find_all, and return first.
+ // Max, min, and sum should all have no effect.
+ if (action == act_Count) {
+ m_state++;
+ m_match_count = size_t(m_state);
+ }
+ else if (action == act_FindAll) {
+ Array::add_to_column(reinterpret_cast<IntegerColumn*>(m_state), index);
+ }
+ else if (action == act_ReturnFirst) {
+ m_match_count++;
+ m_state = index;
+ return false;
+ }
+ return m_limit > m_match_count;
+ }
+};
+
+// Used only for Basic-types: currently float and double
+template <class R>
+class QueryState : public QueryStateBase {
+public:
+ R m_state;
+ size_t m_match_count;
+ size_t m_limit;
+ size_t m_minmax_index; // used only for min/max, to save index of current min/max value
+
+ template <Action action>
+ bool uses_val()
+ {
+ return (action == act_Max || action == act_Min || action == act_Sum || action == act_Count);
+ }
+
+ void init(Action action, Array*, size_t limit)
+ {
+ REALM_ASSERT((std::is_same<R, float>::value || std::is_same<R, double>::value));
+ m_match_count = 0;
+ m_limit = limit;
+ m_minmax_index = not_found;
+
+ if (action == act_Max)
+ m_state = -std::numeric_limits<R>::infinity();
+ else if (action == act_Min)
+ m_state = std::numeric_limits<R>::infinity();
+ else if (action == act_Sum)
+ m_state = 0.0;
+ else {
+ REALM_ASSERT_DEBUG(false);
+ }
+ }
+
+ template <Action action, bool pattern, typename resulttype>
+ inline bool match(size_t index, uint64_t /*indexpattern*/, resulttype value)
+ {
+ if (pattern)
+ return false;
+
+ static_assert(action == act_Sum || action == act_Max || action == act_Min || action == act_Count,
+ "Search action not supported");
+
+ if (action == act_Count) {
+ ++m_match_count;
+ }
+ else if (!null::is_null_float(value)) {
+ ++m_match_count;
+ if (action == act_Max) {
+ if (value > m_state) {
+ m_state = value;
+ m_minmax_index = index;
+ }
+ }
+ else if (action == act_Min) {
+ if (value < m_state) {
+ m_state = value;
+ m_minmax_index = index;
+ }
+ }
+ else if (action == act_Sum)
+ m_state += value;
+ else {
+ REALM_ASSERT_DEBUG(false);
+ }
+ }
+
+ return (m_limit > m_match_count);
+ }
+};
+
+inline bool RefOrTagged::is_ref() const noexcept
+{
+ return (m_value & 1) == 0;
+}
+
+inline bool RefOrTagged::is_tagged() const noexcept
+{
+ return !is_ref();
+}
+
+inline ref_type RefOrTagged::get_as_ref() const noexcept
+{
+ // to_ref() is defined in <alloc.hpp>
+ return to_ref(m_value);
+}
+
+inline uint_fast64_t RefOrTagged::get_as_int() const noexcept
+{
+ // The bitwise AND is there in case uint_fast64_t is wider than 64 bits.
+ return (uint_fast64_t(m_value) & 0xFFFFFFFFFFFFFFFFULL) >> 1;
+}
+
+inline RefOrTagged RefOrTagged::make_ref(ref_type ref) noexcept
+{
+ // from_ref() is defined in <alloc.hpp>
+ int_fast64_t value = from_ref(ref);
+ return RefOrTagged(value);
+}
+
+inline RefOrTagged RefOrTagged::make_tagged(uint_fast64_t i) noexcept
+{
+ REALM_ASSERT(i < (1ULL << 63));
+ int_fast64_t value = util::from_twos_compl<int_fast64_t>((i << 1) | 1);
+ return RefOrTagged(value);
+}
+
+inline RefOrTagged::RefOrTagged(int_fast64_t value) noexcept
+ : m_value(value)
+{
+}
+
+inline Array::Array(Allocator& allocator) noexcept
+ : m_alloc(allocator)
+{
+}
+
+inline void Array::create(Type type, bool context_flag, size_t length, int_fast64_t value)
+{
+ MemRef mem = create_array(type, context_flag, length, value, m_alloc); // Throws
+ init_from_mem(mem);
+}
+
+
+inline void Array::init_from_ref(ref_type ref) noexcept
+{
+ REALM_ASSERT_DEBUG(ref);
+ char* header = m_alloc.translate(ref);
+ init_from_mem(MemRef(header, ref, m_alloc));
+}
+
+
+inline void Array::init_from_parent() noexcept
+{
+ ref_type ref = get_ref_from_parent();
+ init_from_ref(ref);
+}
+
+
+inline Array::Type Array::get_type() const noexcept
+{
+ if (m_is_inner_bptree_node) {
+ REALM_ASSERT_DEBUG(m_has_refs);
+ return type_InnerBptreeNode;
+ }
+ if (m_has_refs)
+ return type_HasRefs;
+ return type_Normal;
+}
+
+
+inline void Array::get_chunk(size_t ndx, int64_t res[8]) const noexcept
+{
+ REALM_ASSERT_DEBUG(ndx < m_size);
+ (this->*(m_vtable->chunk_getter))(ndx, res);
+}
+
+
+inline int64_t Array::get(size_t ndx) const noexcept
+{
+ REALM_ASSERT_DEBUG(is_attached());
+ REALM_ASSERT_DEBUG(ndx < m_size);
+ return (this->*m_getter)(ndx);
+
+ // Two ideas that are not efficient but may be worth looking into again:
+ /*
+ // Assume correct width is found early in REALM_TEMPEX, which is the case for B tree offsets that
+ // are probably either 2^16 long. Turns out to be 25% faster if found immediately, but 50-300% slower
+ // if found later
+ REALM_TEMPEX(return get, (ndx));
+ */
+ /*
+ // Slightly slower in both of the if-cases. Also needs an matchcount m_size check too, to avoid
+ // reading beyond array.
+ if (m_width >= 8 && m_size > ndx + 7)
+ return get<64>(ndx >> m_shift) & m_widthmask;
+ else
+ return (this->*(m_vtable->getter))(ndx);
+ */
+}
+
+inline int64_t Array::front() const noexcept
+{
+ return get(0);
+}
+
+inline int64_t Array::back() const noexcept
+{
+ return get(m_size - 1);
+}
+
+inline ref_type Array::get_as_ref(size_t ndx) const noexcept
+{
+ REALM_ASSERT_DEBUG(is_attached());
+ REALM_ASSERT_DEBUG(m_has_refs);
+ int64_t v = get(ndx);
+ return to_ref(v);
+}
+
+inline RefOrTagged Array::get_as_ref_or_tagged(size_t ndx) const noexcept
+{
+ REALM_ASSERT(has_refs());
+ return RefOrTagged(get(ndx));
+}
+
+inline void Array::set(size_t ndx, RefOrTagged ref_or_tagged)
+{
+ REALM_ASSERT(has_refs());
+ set(ndx, ref_or_tagged.m_value); // Throws
+}
+
+inline void Array::add(RefOrTagged ref_or_tagged)
+{
+ REALM_ASSERT(has_refs());
+ add(ref_or_tagged.m_value); // Throws
+}
+
+inline void Array::ensure_minimum_width(RefOrTagged ref_or_tagged)
+{
+ REALM_ASSERT(has_refs());
+ ensure_minimum_width(ref_or_tagged.m_value); // Throws
+}
+
+inline bool Array::is_inner_bptree_node() const noexcept
+{
+ return m_is_inner_bptree_node;
+}
+
+inline bool Array::has_refs() const noexcept
+{
+ return m_has_refs;
+}
+
+inline void Array::set_has_refs(bool value) noexcept
+{
+ if (m_has_refs != value) {
+ REALM_ASSERT(!is_read_only());
+ m_has_refs = value;
+ set_header_hasrefs(value);
+ }
+}
+
+inline bool Array::get_context_flag() const noexcept
+{
+ return m_context_flag;
+}
+
+inline void Array::set_context_flag(bool value) noexcept
+{
+ if (m_context_flag != value) {
+ REALM_ASSERT(!is_read_only());
+ m_context_flag = value;
+ set_header_context_flag(value);
+ }
+}
+
+inline ref_type Array::get_ref() const noexcept
+{
+ return m_ref;
+}
+
+inline MemRef Array::get_mem() const noexcept
+{
+ return MemRef(get_header_from_data(m_data), m_ref, m_alloc);
+}
+
+inline void Array::destroy() noexcept
+{
+ if (!is_attached())
+ return;
+ char* header = get_header_from_data(m_data);
+ m_alloc.free_(m_ref, header);
+ m_data = nullptr;
+}
+
+inline void Array::destroy_deep() noexcept
+{
+ if (!is_attached())
+ return;
+
+ if (m_has_refs)
+ destroy_children();
+
+ char* header = get_header_from_data(m_data);
+ m_alloc.free_(m_ref, header);
+ m_data = nullptr;
+}
+
+inline ref_type Array::write(_impl::ArrayWriterBase& out, bool deep, bool only_if_modified) const
+{
+ REALM_ASSERT(is_attached());
+
+ if (only_if_modified && m_alloc.is_read_only(m_ref))
+ return m_ref;
+
+ if (!deep || !m_has_refs)
+ return do_write_shallow(out); // Throws
+
+ return do_write_deep(out, only_if_modified); // Throws
+}
+
+inline ref_type Array::write(ref_type ref, Allocator& alloc, _impl::ArrayWriterBase& out, bool only_if_modified)
+{
+ if (only_if_modified && alloc.is_read_only(ref))
+ return ref;
+
+ Array array(alloc);
+ array.init_from_ref(ref);
+
+ if (!array.m_has_refs)
+ return array.do_write_shallow(out); // Throws
+
+ return array.do_write_deep(out, only_if_modified); // Throws
+}
+
+inline void Array::add(int_fast64_t value)
+{
+ insert(m_size, value);
+}
+
+inline void Array::erase(size_t ndx)
+{
+ // This can throw, but only if array is currently in read-only
+ // memory.
+ move(ndx + 1, size(), ndx);
+
+ // Update size (also in header)
+ --m_size;
+ set_header_size(m_size);
+}
+
+
+inline void Array::erase(size_t begin, size_t end)
+{
+ if (begin != end) {
+ // This can throw, but only if array is currently in read-only memory.
+ move(end, size(), begin); // Throws
+
+ // Update size (also in header)
+ m_size -= end - begin;
+ set_header_size(m_size);
+ }
+}
+
+inline void Array::clear()
+{
+ truncate(0); // Throws
+}
+
+inline void Array::clear_and_destroy_children()
+{
+ truncate_and_destroy_children(0);
+}
+
+inline void Array::destroy(ref_type ref, Allocator& alloc) noexcept
+{
+ destroy(MemRef(ref, alloc), alloc);
+}
+
+inline void Array::destroy(MemRef mem, Allocator& alloc) noexcept
+{
+ alloc.free_(mem);
+}
+
+inline void Array::destroy_deep(ref_type ref, Allocator& alloc) noexcept
+{
+ destroy_deep(MemRef(ref, alloc), alloc);
+}
+
+inline void Array::destroy_deep(MemRef mem, Allocator& alloc) noexcept
+{
+ if (!get_hasrefs_from_header(mem.get_addr())) {
+ alloc.free_(mem);
+ return;
+ }
+ Array array(alloc);
+ array.init_from_mem(mem);
+ array.destroy_deep();
+}
+
+
+inline void Array::adjust(size_t ndx, int_fast64_t diff)
+{
+ REALM_ASSERT_3(ndx, <=, m_size);
+ if (diff != 0) {
+ // FIXME: Should be optimized
+ int_fast64_t v = get(ndx);
+ set(ndx, int64_t(v + diff)); // Throws
+ }
+}
+
+inline void Array::adjust(size_t begin, size_t end, int_fast64_t diff)
+{
+ if (diff != 0) {
+ // FIXME: Should be optimized
+ for (size_t i = begin; i != end; ++i)
+ adjust(i, diff); // Throws
+ }
+}
+
+
+//-------------------------------------------------
+
+inline bool Array::get_is_inner_bptree_node_from_header(const char* header) noexcept
+{
+ typedef unsigned char uchar;
+ const uchar* h = reinterpret_cast<const uchar*>(header);
+ return (int(h[4]) & 0x80) != 0;
+}
+inline bool Array::get_hasrefs_from_header(const char* header) noexcept
+{
+ typedef unsigned char uchar;
+ const uchar* h = reinterpret_cast<const uchar*>(header);
+ return (int(h[4]) & 0x40) != 0;
+}
+inline bool Array::get_context_flag_from_header(const char* header) noexcept
+{
+ typedef unsigned char uchar;
+ const uchar* h = reinterpret_cast<const uchar*>(header);
+ return (int(h[4]) & 0x20) != 0;
+}
+inline Array::WidthType Array::get_wtype_from_header(const char* header) noexcept
+{
+ typedef unsigned char uchar;
+ const uchar* h = reinterpret_cast<const uchar*>(header);
+ return WidthType((int(h[4]) & 0x18) >> 3);
+}
+inline uint_least8_t Array::get_width_from_header(const char* header) noexcept
+{
+ typedef unsigned char uchar;
+ const uchar* h = reinterpret_cast<const uchar*>(header);
+ return uint_least8_t((1 << (int(h[4]) & 0x07)) >> 1);
+}
+inline size_t Array::get_size_from_header(const char* header) noexcept
+{
+ typedef unsigned char uchar;
+ const uchar* h = reinterpret_cast<const uchar*>(header);
+ return (size_t(h[5]) << 16) + (size_t(h[6]) << 8) + h[7];
+}
+inline size_t Array::get_capacity_from_header(const char* header) noexcept
+{
+ typedef unsigned char uchar;
+ const uchar* h = reinterpret_cast<const uchar*>(header);
+ return (size_t(h[0]) << 16) + (size_t(h[1]) << 8) + h[2];
+}
+
+
+inline char* Array::get_data_from_header(char* header) noexcept
+{
+ return header + header_size;
+}
+inline char* Array::get_header_from_data(char* data) noexcept
+{
+ return data - header_size;
+}
+inline const char* Array::get_data_from_header(const char* header) noexcept
+{
+ return get_data_from_header(const_cast<char*>(header));
+}
+
+
+inline bool Array::get_is_inner_bptree_node_from_header() const noexcept
+{
+ return get_is_inner_bptree_node_from_header(get_header_from_data(m_data));
+}
+inline bool Array::get_hasrefs_from_header() const noexcept
+{
+ return get_hasrefs_from_header(get_header_from_data(m_data));
+}
+inline bool Array::get_context_flag_from_header() const noexcept
+{
+ return get_context_flag_from_header(get_header_from_data(m_data));
+}
+inline Array::WidthType Array::get_wtype_from_header() const noexcept
+{
+ return get_wtype_from_header(get_header_from_data(m_data));
+}
+inline uint_least8_t Array::get_width_from_header() const noexcept
+{
+ return get_width_from_header(get_header_from_data(m_data));
+}
+inline size_t Array::get_size_from_header() const noexcept
+{
+ return get_size_from_header(get_header_from_data(m_data));
+}
+inline size_t Array::get_capacity_from_header() const noexcept
+{
+ return get_capacity_from_header(get_header_from_data(m_data));
+}
+
+
+inline void Array::set_header_is_inner_bptree_node(bool value, char* header) noexcept
+{
+ typedef unsigned char uchar;
+ uchar* h = reinterpret_cast<uchar*>(header);
+ h[4] = uchar((int(h[4]) & ~0x80) | int(value) << 7);
+}
+
+inline void Array::set_header_hasrefs(bool value, char* header) noexcept
+{
+ typedef unsigned char uchar;
+ uchar* h = reinterpret_cast<uchar*>(header);
+ h[4] = uchar((int(h[4]) & ~0x40) | int(value) << 6);
+}
+
+inline void Array::set_header_context_flag(bool value, char* header) noexcept
+{
+ typedef unsigned char uchar;
+ uchar* h = reinterpret_cast<uchar*>(header);
+ h[4] = uchar((int(h[4]) & ~0x20) | int(value) << 5);
+}
+
+inline void Array::set_header_wtype(WidthType value, char* header) noexcept
+{
+ // Indicates how to calculate size in bytes based on width
+ // 0: bits (width/8) * size
+ // 1: multiply width * size
+ // 2: ignore 1 * size
+ typedef unsigned char uchar;
+ uchar* h = reinterpret_cast<uchar*>(header);
+ h[4] = uchar((int(h[4]) & ~0x18) | int(value) << 3);
+}
+
+inline void Array::set_header_width(int value, char* header) noexcept
+{
+ // Pack width in 3 bits (log2)
+ int w = 0;
+ while (value) {
+ ++w;
+ value >>= 1;
+ }
+ REALM_ASSERT_3(w, <, 8);
+
+ typedef unsigned char uchar;
+ uchar* h = reinterpret_cast<uchar*>(header);
+ h[4] = uchar((int(h[4]) & ~0x7) | w);
+}
+
+inline void Array::set_header_size(size_t value, char* header) noexcept
+{
+ REALM_ASSERT_3(value, <=, max_array_payload);
+ typedef unsigned char uchar;
+ uchar* h = reinterpret_cast<uchar*>(header);
+ h[5] = uchar((value >> 16) & 0x000000FF);
+ h[6] = uchar((value >> 8) & 0x000000FF);
+ h[7] = uchar(value & 0x000000FF);
+}
+
+// Note: There is a copy of this function is test_alloc.cpp
+inline void Array::set_header_capacity(size_t value, char* header) noexcept
+{
+ REALM_ASSERT_3(value, <=, max_array_payload);
+ typedef unsigned char uchar;
+ uchar* h = reinterpret_cast<uchar*>(header);
+ h[0] = uchar((value >> 16) & 0x000000FF);
+ h[1] = uchar((value >> 8) & 0x000000FF);
+ h[2] = uchar(value & 0x000000FF);
+}
+
+
+inline void Array::set_header_is_inner_bptree_node(bool value) noexcept
+{
+ set_header_is_inner_bptree_node(value, get_header_from_data(m_data));
+}
+inline void Array::set_header_hasrefs(bool value) noexcept
+{
+ set_header_hasrefs(value, get_header_from_data(m_data));
+}
+inline void Array::set_header_context_flag(bool value) noexcept
+{
+ set_header_context_flag(value, get_header_from_data(m_data));
+}
+inline void Array::set_header_wtype(WidthType value) noexcept
+{
+ set_header_wtype(value, get_header_from_data(m_data));
+}
+inline void Array::set_header_width(int value) noexcept
+{
+ set_header_width(value, get_header_from_data(m_data));
+}
+inline void Array::set_header_size(size_t value) noexcept
+{
+ set_header_size(value, get_header_from_data(m_data));
+}
+inline void Array::set_header_capacity(size_t value) noexcept
+{
+ set_header_capacity(value, get_header_from_data(m_data));
+}
+
+
+inline Array::Type Array::get_type_from_header(const char* header) noexcept
+{
+ if (get_is_inner_bptree_node_from_header(header))
+ return type_InnerBptreeNode;
+ if (get_hasrefs_from_header(header))
+ return type_HasRefs;
+ return type_Normal;
+}
+
+
+inline char* Array::get_header() noexcept
+{
+ return get_header_from_data(m_data);
+}
+
+inline size_t Array::calc_byte_size(WidthType wtype, size_t size, uint_least8_t width) noexcept
+{
+ size_t num_bytes = 0;
+ switch (wtype) {
+ case wtype_Bits: {
+ // Current assumption is that size is at most 2^24 and that width is at most 64.
+ // In that case the following will never overflow. (Assuming that size_t is at least 32 bits)
+ REALM_ASSERT_3(size, <, 0x1000000);
+ size_t num_bits = size * width;
+ num_bytes = (num_bits + 7) >> 3;
+ break;
+ }
+ case wtype_Multiply: {
+ num_bytes = size * width;
+ break;
+ }
+ case wtype_Ignore:
+ num_bytes = size;
+ break;
+ }
+
+ // Ensure 8-byte alignment
+ num_bytes = (num_bytes + 7) & ~size_t(7);
+
+ num_bytes += header_size;
+
+ return num_bytes;
+}
+
+inline size_t Array::get_byte_size() const noexcept
+{
+ const char* header = get_header_from_data(m_data);
+ WidthType wtype = get_wtype_from_header(header);
+ size_t num_bytes = calc_byte_size(wtype, m_size, m_width);
+
+ REALM_ASSERT_7(m_alloc.is_read_only(m_ref), ==, true, ||, num_bytes, <=, get_capacity_from_header(header));
+
+ return num_bytes;
+}
+
+
+inline size_t Array::get_byte_size_from_header(const char* header) noexcept
+{
+ size_t size = get_size_from_header(header);
+ uint_least8_t width = get_width_from_header(header);
+ WidthType wtype = get_wtype_from_header(header);
+ size_t num_bytes = calc_byte_size(wtype, size, width);
+
+ return num_bytes;
+}
+
+
+inline void Array::init_header(char* header, bool is_inner_bptree_node, bool has_refs, bool context_flag,
+ WidthType width_type, int width, size_t size, size_t capacity) noexcept
+{
+ // Note: Since the header layout contains unallocated bit and/or
+ // bytes, it is important that we put the entire header into a
+ // well defined state initially.
+ std::fill(header, header + header_size, 0);
+ set_header_is_inner_bptree_node(is_inner_bptree_node, header);
+ set_header_hasrefs(has_refs, header);
+ set_header_context_flag(context_flag, header);
+ set_header_wtype(width_type, header);
+ set_header_width(width, header);
+ set_header_size(size, header);
+ set_header_capacity(capacity, header);
+}
+
+
+//-------------------------------------------------
+
+inline MemRef Array::clone_deep(Allocator& target_alloc) const
+{
+ char* header = get_header_from_data(m_data);
+ return clone(MemRef(header, m_ref, m_alloc), m_alloc, target_alloc); // Throws
+}
+
+inline MemRef Array::create_empty_array(Type type, bool context_flag, Allocator& alloc)
+{
+ size_t size = 0;
+ int_fast64_t value = 0;
+ return create_array(type, context_flag, size, value, alloc); // Throws
+}
+
+inline MemRef Array::create_array(Type type, bool context_flag, size_t size, int_fast64_t value, Allocator& alloc)
+{
+ return create(type, context_flag, wtype_Bits, size, value, alloc); // Throws
+}
+
+inline bool Array::has_parent() const noexcept
+{
+ return m_parent != nullptr;
+}
+
+inline ArrayParent* Array::get_parent() const noexcept
+{
+ return m_parent;
+}
+
+inline void Array::set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept
+{
+ m_parent = parent;
+ m_ndx_in_parent = ndx_in_parent;
+}
+
+inline size_t Array::get_ndx_in_parent() const noexcept
+{
+ return m_ndx_in_parent;
+}
+
+inline void Array::set_ndx_in_parent(size_t ndx) noexcept
+{
+ m_ndx_in_parent = ndx;
+}
+
+inline void Array::adjust_ndx_in_parent(int diff) noexcept
+{
+ // Note that `diff` is promoted to an unsigned type, and that
+ // C++03 still guarantees the expected result regardless of the
+ // sizes of `int` and `decltype(m_ndx_in_parent)`.
+ m_ndx_in_parent += diff;
+}
+
+inline ref_type Array::get_ref_from_parent() const noexcept
+{
+ ref_type ref = m_parent->get_child_ref(m_ndx_in_parent);
+ return ref;
+}
+
+inline bool Array::is_attached() const noexcept
+{
+ return m_data != nullptr;
+}
+
+inline void Array::detach() noexcept
+{
+ m_data = nullptr;
+}
+
+inline size_t Array::size() const noexcept
+{
+ REALM_ASSERT_DEBUG(is_attached());
+ return m_size;
+}
+
+inline bool Array::is_empty() const noexcept
+{
+ return size() == 0;
+}
+
+inline size_t Array::get_max_byte_size(size_t num_elems) noexcept
+{
+ int max_bytes_per_elem = 8;
+ return header_size + num_elems * max_bytes_per_elem;
+}
+
+inline void Array::update_parent()
+{
+ if (m_parent)
+ m_parent->update_child_ref(m_ndx_in_parent, m_ref);
+}
+
+
+inline void Array::update_child_ref(size_t child_ndx, ref_type new_ref)
+{
+ set(child_ndx, new_ref);
+}
+
+inline ref_type Array::get_child_ref(size_t child_ndx) const noexcept
+{
+ return get_as_ref(child_ndx);
+}
+
+inline bool Array::is_read_only() const noexcept
+{
+ REALM_ASSERT_DEBUG(is_attached());
+ return m_alloc.is_read_only(m_ref);
+}
+
+inline void Array::copy_on_write()
+{
+#if REALM_ENABLE_MEMDEBUG
+ // We want to relocate this array regardless if there is a need or not, in order to catch use-after-free bugs.
+ // Only exception is inside GroupWriter::write_group() (see explanation at the definition of the m_no_relocation
+ // member)
+ if (!m_no_relocation) {
+#else
+ if (is_read_only()) {
+#endif
+ do_copy_on_write();
+ }
+}
+
+inline void Array::ensure_minimum_width(int_fast64_t value)
+{
+ if (value >= m_lbound && value <= m_ubound)
+ return;
+ do_ensure_minimum_width(value);
+}
+
+
+//*************************************************************************************
+// Finding code *
+//*************************************************************************************
+
+template <size_t w>
+int64_t Array::get(size_t ndx) const noexcept
+{
+ return get_universal<w>(m_data, ndx);
+}
+
+template <size_t w>
+int64_t Array::get_universal(const char* data, size_t ndx) const
+{
+ if (w == 0) {
+ return 0;
+ }
+ else if (w == 1) {
+ size_t offset = ndx >> 3;
+ return (data[offset] >> (ndx & 7)) & 0x01;
+ }
+ else if (w == 2) {
+ size_t offset = ndx >> 2;
+ return (data[offset] >> ((ndx & 3) << 1)) & 0x03;
+ }
+ else if (w == 4) {
+ size_t offset = ndx >> 1;
+ return (data[offset] >> ((ndx & 1) << 2)) & 0x0F;
+ }
+ else if (w == 8) {
+ return *reinterpret_cast<const signed char*>(data + ndx);
+ }
+ else if (w == 16) {
+ size_t offset = ndx * 2;
+ return *reinterpret_cast<const int16_t*>(data + offset);
+ }
+ else if (w == 32) {
+ size_t offset = ndx * 4;
+ return *reinterpret_cast<const int32_t*>(data + offset);
+ }
+ else if (w == 64) {
+ size_t offset = ndx * 8;
+ return *reinterpret_cast<const int64_t*>(data + offset);
+ }
+ else {
+ REALM_ASSERT_DEBUG(false);
+ return int64_t(-1);
+ }
+}
+
+/*
+find() (calls find_optimized()) will call match() for each search result.
+
+If pattern == true:
+ 'indexpattern' contains a 64-bit chunk of elements, each of 'width' bits in size where each element indicates a
+ match if its lower bit is set, otherwise it indicates a non-match. 'index' tells the database row index of the
+ first element. You must return true if you chose to 'consume' the chunk or false if not. If not, then Array-finder
+ will afterwards call match() successive times with pattern == false.
+
+If pattern == false:
+ 'index' tells the row index of a single match and 'value' tells its value. Return false to make Array-finder break
+ its search or return true to let it continue until 'end' or 'limit'.
+
+Array-finder decides itself if - and when - it wants to pass you an indexpattern. It depends on array bit width, match
+frequency, and whether the arithemetic and computations for the given search criteria makes it feasible to construct
+such a pattern.
+*/
+
+// These wrapper functions only exist to enable a possibility to make the compiler see that 'value' and/or 'index' are
+// unused, such that caller's computation of these values will not be made. Only works if find_action() and
+// find_action_pattern() rewritten as macros. Note: This problem has been fixed in next upcoming array.hpp version
+template <Action action, class Callback>
+bool Array::find_action(size_t index, util::Optional<int64_t> value, QueryState<int64_t>* state,
+ Callback callback) const
+{
+ if (action == act_CallbackIdx)
+ return callback(index);
+ else
+ return state->match<action, false>(index, 0, value);
+}
+template <Action action, class Callback>
+bool Array::find_action_pattern(size_t index, uint64_t pattern, QueryState<int64_t>* state, Callback callback) const
+{
+ static_cast<void>(callback);
+ if (action == act_CallbackIdx) {
+ // Possible future optimization: call callback(index) like in above find_action(), in a loop for each bit set
+ // in 'pattern'
+ return false;
+ }
+ return state->match<action, true>(index, pattern, 0);
+}
+
+
+template <size_t width, bool zero>
+uint64_t Array::cascade(uint64_t a) const
+{
+ // Takes a chunk of values as argument and sets the least significant bit for each
+ // element which is zero or non-zero, depending on the template parameter.
+ // Example for zero=true:
+ // width == 4 and a = 0x5fd07a107610f610
+ // will return: 0x0001000100010001
+
+ // static values needed for fast population count
+ const uint64_t m1 = 0x5555555555555555ULL;
+
+ if (width == 1) {
+ return zero ? ~a : a;
+ }
+ else if (width == 2) {
+ // Masks to avoid spillover between segments in cascades
+ const uint64_t c1 = ~0ULL / 0x3 * 0x1;
+
+ a |= (a >> 1) & c1; // cascade ones in non-zeroed segments
+ a &= m1; // isolate single bit in each segment
+ if (zero)
+ a ^= m1; // reverse isolated bits if checking for zeroed segments
+
+ return a;
+ }
+ else if (width == 4) {
+ const uint64_t m = ~0ULL / 0xF * 0x1;
+
+ // Masks to avoid spillover between segments in cascades
+ const uint64_t c1 = ~0ULL / 0xF * 0x7;
+ const uint64_t c2 = ~0ULL / 0xF * 0x3;
+
+ a |= (a >> 1) & c1; // cascade ones in non-zeroed segments
+ a |= (a >> 2) & c2;
+ a &= m; // isolate single bit in each segment
+ if (zero)
+ a ^= m; // reverse isolated bits if checking for zeroed segments
+
+ return a;
+ }
+ else if (width == 8) {
+ const uint64_t m = ~0ULL / 0xFF * 0x1;
+
+ // Masks to avoid spillover between segments in cascades
+ const uint64_t c1 = ~0ULL / 0xFF * 0x7F;
+ const uint64_t c2 = ~0ULL / 0xFF * 0x3F;
+ const uint64_t c3 = ~0ULL / 0xFF * 0x0F;
+
+ a |= (a >> 1) & c1; // cascade ones in non-zeroed segments
+ a |= (a >> 2) & c2;
+ a |= (a >> 4) & c3;
+ a &= m; // isolate single bit in each segment
+ if (zero)
+ a ^= m; // reverse isolated bits if checking for zeroed segments
+
+ return a;
+ }
+ else if (width == 16) {
+ const uint64_t m = ~0ULL / 0xFFFF * 0x1;
+
+ // Masks to avoid spillover between segments in cascades
+ const uint64_t c1 = ~0ULL / 0xFFFF * 0x7FFF;
+ const uint64_t c2 = ~0ULL / 0xFFFF * 0x3FFF;
+ const uint64_t c3 = ~0ULL / 0xFFFF * 0x0FFF;
+ const uint64_t c4 = ~0ULL / 0xFFFF * 0x00FF;
+
+ a |= (a >> 1) & c1; // cascade ones in non-zeroed segments
+ a |= (a >> 2) & c2;
+ a |= (a >> 4) & c3;
+ a |= (a >> 8) & c4;
+ a &= m; // isolate single bit in each segment
+ if (zero)
+ a ^= m; // reverse isolated bits if checking for zeroed segments
+
+ return a;
+ }
+
+ else if (width == 32) {
+ const uint64_t m = ~0ULL / 0xFFFFFFFF * 0x1;
+
+ // Masks to avoid spillover between segments in cascades
+ const uint64_t c1 = ~0ULL / 0xFFFFFFFF * 0x7FFFFFFF;
+ const uint64_t c2 = ~0ULL / 0xFFFFFFFF * 0x3FFFFFFF;
+ const uint64_t c3 = ~0ULL / 0xFFFFFFFF * 0x0FFFFFFF;
+ const uint64_t c4 = ~0ULL / 0xFFFFFFFF * 0x00FFFFFF;
+ const uint64_t c5 = ~0ULL / 0xFFFFFFFF * 0x0000FFFF;
+
+ a |= (a >> 1) & c1; // cascade ones in non-zeroed segments
+ a |= (a >> 2) & c2;
+ a |= (a >> 4) & c3;
+ a |= (a >> 8) & c4;
+ a |= (a >> 16) & c5;
+ a &= m; // isolate single bit in each segment
+ if (zero)
+ a ^= m; // reverse isolated bits if checking for zeroed segments
+
+ return a;
+ }
+ else if (width == 64) {
+ return (a == 0) == zero;
+ }
+ else {
+ REALM_ASSERT_DEBUG(false);
+ return uint64_t(-1);
+ }
+}
+
+// This is the main finding function for Array. Other finding functions are just wrappers around this one.
+// Search for 'value' using condition cond (Equal, NotEqual, Less, etc) and call find_action() or
+// find_action_pattern() for each match. Break and return if find_action() returns false or 'end' is reached.
+
+// If nullable_array is set, then find_optimized() will treat the array is being nullable, i.e. it will skip the
+// first entry and compare correctly against null, etc.
+//
+// If find_null is set, it means that we search for a null. In that case, `value` is ignored. If find_null is set,
+// then nullable_array must be set too.
+template <class cond, Action action, size_t bitwidth, class Callback>
+bool Array::find_optimized(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+ Callback callback, bool nullable_array, bool find_null) const
+{
+ REALM_ASSERT(!(find_null && !nullable_array));
+ REALM_ASSERT_DEBUG(start <= m_size && (end <= m_size || end == size_t(-1)) && start <= end);
+
+ size_t start2 = start;
+ cond c;
+
+ if (end == npos)
+ end = nullable_array ? size() - 1 : size();
+
+ if (nullable_array) {
+ // We were called by find() of a nullable array. So skip first entry, take nulls in count, etc, etc. Fixme:
+ // Huge speed optimizations are possible here! This is a very simple generic method.
+ for (; start2 < end; start2++) {
+ int64_t v = get<bitwidth>(start2 + 1);
+ if (c(v, value, v == get(0), find_null)) {
+ util::Optional<int64_t> v2(v == get(0) ? util::none : util::make_optional(v));
+ if (!find_action<action, Callback>(start2 + baseindex, v2, state, callback))
+ return false; // tell caller to stop aggregating/search
+ }
+ }
+ return true; // tell caller to continue aggregating/search (on next array leafs)
+ }
+
+
+ // Test first few items with no initial time overhead
+ if (start2 > 0) {
+ if (m_size > start2 && c(get<bitwidth>(start2), value) && start2 < end) {
+ if (!find_action<action, Callback>(start2 + baseindex, get<bitwidth>(start2), state, callback))
+ return false;
+ }
+
+ ++start2;
+
+ if (m_size > start2 && c(get<bitwidth>(start2), value) && start2 < end) {
+ if (!find_action<action, Callback>(start2 + baseindex, get<bitwidth>(start2), state, callback))
+ return false;
+ }
+
+ ++start2;
+
+ if (m_size > start2 && c(get<bitwidth>(start2), value) && start2 < end) {
+ if (!find_action<action, Callback>(start2 + baseindex, get<bitwidth>(start2), state, callback))
+ return false;
+ }
+
+ ++start2;
+
+ if (m_size > start2 && c(get<bitwidth>(start2), value) && start2 < end) {
+ if (!find_action<action, Callback>(start2 + baseindex, get<bitwidth>(start2), state, callback))
+ return false;
+ }
+
+ ++start2;
+ }
+
+ if (!(m_size > start2 && start2 < end))
+ return true;
+
+ if (end == size_t(-1))
+ end = m_size;
+
+ // Return immediately if no items in array can match (such as if cond == Greater && value == 100 &&
+ // m_ubound == 15)
+ if (!c.can_match(value, m_lbound, m_ubound))
+ return true;
+
+ // optimization if all items are guaranteed to match (such as cond == NotEqual && value == 100 && m_ubound == 15)
+ if (c.will_match(value, m_lbound, m_ubound)) {
+ size_t end2;
+
+ if (action == act_CallbackIdx)
+ end2 = end;
+ else {
+ REALM_ASSERT_DEBUG(state->m_match_count < state->m_limit);
+ size_t process = state->m_limit - state->m_match_count;
+ end2 = end - start2 > process ? start2 + process : end;
+ }
+ if (action == act_Sum || action == act_Max || action == act_Min) {
+ int64_t res;
+ size_t res_ndx = 0;
+ if (action == act_Sum)
+ res = Array::sum(start2, end2);
+ if (action == act_Max)
+ Array::maximum(res, start2, end2, &res_ndx);
+ if (action == act_Min)
+ Array::minimum(res, start2, end2, &res_ndx);
+
+ find_action<action, Callback>(res_ndx + baseindex, res, state, callback);
+ // find_action will increment match count by 1, so we need to `-1` from the number of elements that
+ // we performed the fast Array methods on.
+ state->m_match_count += end2 - start2 - 1;
+ }
+ else if (action == act_Count) {
+ state->m_state += end2 - start2;
+ }
+ else {
+ for (; start2 < end2; start2++)
+ if (!find_action<action, Callback>(start2 + baseindex, get<bitwidth>(start2), state, callback))
+ return false;
+ }
+ return true;
+ }
+
+ // finder cannot handle this bitwidth
+ REALM_ASSERT_3(m_width, !=, 0);
+
+#if defined(REALM_COMPILER_SSE)
+ // Only use SSE if payload is at least one SSE chunk (128 bits) in size. Also note taht SSE doesn't support
+ // Less-than comparison for 64-bit values.
+ if ((!(std::is_same<cond, Less>::value && m_width == 64)) && end - start2 >= sizeof(__m128i) && m_width >= 8 &&
+ (sseavx<42>() || (sseavx<30>() && std::is_same<cond, Equal>::value && m_width < 64))) {
+
+ // find_sse() must start2 at 16-byte boundary, so search area before that using compare_equality()
+ __m128i* const a = reinterpret_cast<__m128i*>(round_up(m_data + start2 * bitwidth / 8, sizeof(__m128i)));
+ __m128i* const b = reinterpret_cast<__m128i*>(round_down(m_data + end * bitwidth / 8, sizeof(__m128i)));
+
+ if (!compare<cond, action, bitwidth, Callback>(
+ value, start2, (reinterpret_cast<char*>(a) - m_data) * 8 / no0(bitwidth), baseindex, state, callback))
+ return false;
+
+ // Search aligned area with SSE
+ if (b > a) {
+ if (sseavx<42>()) {
+ if (!find_sse<cond, action, bitwidth, Callback>(
+ value, a, b - a, state,
+ baseindex + ((reinterpret_cast<char*>(a) - m_data) * 8 / no0(bitwidth)), callback))
+ return false;
+ }
+ else if (sseavx<30>()) {
+
+ if (!find_sse<Equal, action, bitwidth, Callback>(
+ value, a, b - a, state,
+ baseindex + ((reinterpret_cast<char*>(a) - m_data) * 8 / no0(bitwidth)), callback))
+ return false;
+ }
+ }
+
+ // Search remainder with compare_equality()
+ if (!compare<cond, action, bitwidth, Callback>(
+ value, (reinterpret_cast<char*>(b) - m_data) * 8 / no0(bitwidth), end, baseindex, state, callback))
+ return false;
+
+ return true;
+ }
+ else {
+ return compare<cond, action, bitwidth, Callback>(value, start2, end, baseindex, state, callback);
+ }
+#else
+ return compare<cond, action, bitwidth, Callback>(value, start2, end, baseindex, state, callback);
+#endif
+}
+
+template <size_t width>
+inline int64_t Array::lower_bits() const
+{
+ if (width == 1)
+ return 0xFFFFFFFFFFFFFFFFULL;
+ else if (width == 2)
+ return 0x5555555555555555ULL;
+ else if (width == 4)
+ return 0x1111111111111111ULL;
+ else if (width == 8)
+ return 0x0101010101010101ULL;
+ else if (width == 16)
+ return 0x0001000100010001ULL;
+ else if (width == 32)
+ return 0x0000000100000001ULL;
+ else if (width == 64)
+ return 0x0000000000000001ULL;
+ else {
+ REALM_ASSERT_DEBUG(false);
+ return int64_t(-1);
+ }
+}
+
+// Tests if any chunk in 'value' is 0
+template <size_t width>
+inline bool Array::test_zero(uint64_t value) const
+{
+ uint64_t hasZeroByte;
+ uint64_t lower = lower_bits<width>();
+ uint64_t upper = lower_bits<width>() * 1ULL << (width == 0 ? 0 : (width - 1ULL));
+ hasZeroByte = (value - lower) & ~value & upper;
+ return hasZeroByte != 0;
+}
+
+// Finds first zero (if eq == true) or non-zero (if eq == false) element in v and returns its position.
+// IMPORTANT: This function assumes that at least 1 item matches (test this with test_zero() or other means first)!
+template <bool eq, size_t width>
+size_t Array::find_zero(uint64_t v) const
+{
+ size_t start = 0;
+ uint64_t hasZeroByte;
+ // Warning free way of computing (1ULL << width) - 1
+ uint64_t mask = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) - 1ULL));
+
+ if (eq == (((v >> (width * start)) & mask) == 0)) {
+ return 0;
+ }
+
+ // Bisection optimization, speeds up small bitwidths with high match frequency. More partions than 2 do NOT pay
+ // off because the work done by test_zero() is wasted for the cases where the value exists in first half, but
+ // useful if it exists in last half. Sweet spot turns out to be the widths and partitions below.
+ if (width <= 8) {
+ hasZeroByte = test_zero<width>(v | 0xffffffff00000000ULL);
+ if (eq ? !hasZeroByte : (v & 0x00000000ffffffffULL) == 0) {
+ // 00?? -> increasing
+ start += 64 / no0(width) / 2;
+ if (width <= 4) {
+ hasZeroByte = test_zero<width>(v | 0xffff000000000000ULL);
+ if (eq ? !hasZeroByte : (v & 0x0000ffffffffffffULL) == 0) {
+ // 000?
+ start += 64 / no0(width) / 4;
+ }
+ }
+ }
+ else {
+ if (width <= 4) {
+ // ??00
+ hasZeroByte = test_zero<width>(v | 0xffffffffffff0000ULL);
+ if (eq ? !hasZeroByte : (v & 0x000000000000ffffULL) == 0) {
+ // 0?00
+ start += 64 / no0(width) / 4;
+ }
+ }
+ }
+ }
+
+ while (eq == (((v >> (width * start)) & mask) != 0)) {
+ // You must only call find_zero() if you are sure that at least 1 item matches
+ REALM_ASSERT_3(start, <=, 8 * sizeof(v));
+ start++;
+ }
+
+ return start;
+}
+
+// Generate a magic constant used for later bithacks
+template <bool gt, size_t width>
+int64_t Array::find_gtlt_magic(int64_t v) const
+{
+ uint64_t mask1 = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) -
+ 1ULL)); // Warning free way of computing (1ULL << width) - 1
+ uint64_t mask2 = mask1 >> 1;
+ uint64_t magic = gt ? (~0ULL / no0(mask1) * (mask2 - v)) : (~0ULL / no0(mask1) * v);
+ return magic;
+}
+
+template <bool gt, Action action, size_t width, class Callback>
+bool Array::find_gtlt_fast(uint64_t chunk, uint64_t magic, QueryState<int64_t>* state, size_t baseindex,
+ Callback callback) const
+{
+ // Tests if a a chunk of values contains values that are greater (if gt == true) or less (if gt == false) than v.
+ // Fast, but limited to work when all values in the chunk are positive.
+
+ uint64_t mask1 = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) -
+ 1ULL)); // Warning free way of computing (1ULL << width) - 1
+ uint64_t mask2 = mask1 >> 1;
+ uint64_t m = gt ? (((chunk + magic) | chunk) & ~0ULL / no0(mask1) * (mask2 + 1))
+ : ((chunk - magic) & ~chunk & ~0ULL / no0(mask1) * (mask2 + 1));
+ size_t p = 0;
+ while (m) {
+ if (find_action_pattern<action, Callback>(baseindex, m >> (no0(width) - 1), state, callback))
+ break; // consumed, so do not call find_action()
+
+ size_t t = first_set_bit64(m) / no0(width);
+ p += t;
+ if (!find_action<action, Callback>(p + baseindex, (chunk >> (p * width)) & mask1, state, callback))
+ return false;
+
+ if ((t + 1) * width == 64)
+ m = 0;
+ else
+ m >>= (t + 1) * width;
+ p++;
+ }
+
+ return true;
+}
+
+// clang-format off
+template <bool gt, Action action, size_t width, class Callback>
+bool Array::find_gtlt(int64_t v, uint64_t chunk, QueryState<int64_t>* state, size_t baseindex, Callback callback) const
+{
+ // Find items in 'chunk' that are greater (if gt == true) or smaller (if gt == false) than 'v'. Fixme, __forceinline can make it crash in vS2010 - find out why
+ if (width == 1) {
+ for (size_t t = 0; t < 64; t++) {
+ if (gt ? static_cast<int64_t>(chunk & 0x1) > v : static_cast<int64_t>(chunk & 0x1) < v) {if (!find_action<action, Callback>( t + baseindex, static_cast<int64_t>(chunk & 0x1), state, callback)) return false;}
+ chunk >>= 1;
+ }
+ }
+ else if (width == 2) {
+ // Alot (50% +) faster than loop/compiler-unrolled loop
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 0 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 1 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 2 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 3 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 4 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 5 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 6 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 7 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 8 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 9 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 10 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 11 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 12 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 13 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 14 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 15 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 16 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 17 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 18 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 19 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 20 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 21 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 22 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 23 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 24 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 25 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 26 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 27 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 28 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 29 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 30 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 31 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+ chunk >>= 2;
+ }
+ else if (width == 4) {
+ if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 0 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+ chunk >>= 4;
+ if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 1 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+ chunk >>= 4;
+ if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 2 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+ chunk >>= 4;
+ if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 3 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+ chunk >>= 4;
+ if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 4 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+ chunk >>= 4;
+ if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 5 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+ chunk >>= 4;
+ if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 6 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+ chunk >>= 4;
+ if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 7 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+ chunk >>= 4;
+
+ if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 8 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+ chunk >>= 4;
+ if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 9 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+ chunk >>= 4;
+ if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 10 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+ chunk >>= 4;
+ if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 11 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+ chunk >>= 4;
+ if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 12 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+ chunk >>= 4;
+ if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 13 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+ chunk >>= 4;
+ if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 14 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+ chunk >>= 4;
+ if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 15 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+ chunk >>= 4;
+ }
+ else if (width == 8) {
+ if (gt ? static_cast<int8_t>(chunk) > v : static_cast<int8_t>(chunk) < v) {if (!find_action<action, Callback>( 0 + baseindex, static_cast<int8_t>(chunk), state, callback)) return false;}
+ chunk >>= 8;
+ if (gt ? static_cast<int8_t>(chunk) > v : static_cast<int8_t>(chunk) < v) {if (!find_action<action, Callback>( 1 + baseindex, static_cast<int8_t>(chunk), state, callback)) return false;}
+ chunk >>= 8;
+ if (gt ? static_cast<int8_t>(chunk) > v : static_cast<int8_t>(chunk) < v) {if (!find_action<action, Callback>( 2 + baseindex, static_cast<int8_t>(chunk), state, callback)) return false;}
+ chunk >>= 8;
+ if (gt ? static_cast<int8_t>(chunk) > v : static_cast<int8_t>(chunk) < v) {if (!find_action<action, Callback>( 3 + baseindex, static_cast<int8_t>(chunk), state, callback)) return false;}
+ chunk >>= 8;
+ if (gt ? static_cast<int8_t>(chunk) > v : static_cast<int8_t>(chunk) < v) {if (!find_action<action, Callback>( 4 + baseindex, static_cast<int8_t>(chunk), state, callback)) return false;}
+ chunk >>= 8;
+ if (gt ? static_cast<int8_t>(chunk) > v : static_cast<int8_t>(chunk) < v) {if (!find_action<action, Callback>( 5 + baseindex, static_cast<int8_t>(chunk), state, callback)) return false;}
+ chunk >>= 8;
+ if (gt ? static_cast<int8_t>(chunk) > v : static_cast<int8_t>(chunk) < v) {if (!find_action<action, Callback>( 6 + baseindex, static_cast<int8_t>(chunk), state, callback)) return false;}
+ chunk >>= 8;
+ if (gt ? static_cast<int8_t>(chunk) > v : static_cast<int8_t>(chunk) < v) {if (!find_action<action, Callback>( 7 + baseindex, static_cast<int8_t>(chunk), state, callback)) return false;}
+ chunk >>= 8;
+ }
+ else if (width == 16) {
+
+ if (gt ? static_cast<short int>(chunk >> 0 * 16) > v : static_cast<short int>(chunk >> 0 * 16) < v) {if (!find_action<action, Callback>( 0 + baseindex, static_cast<short int>(chunk >> 0 * 16), state, callback)) return false;};
+ if (gt ? static_cast<short int>(chunk >> 1 * 16) > v : static_cast<short int>(chunk >> 1 * 16) < v) {if (!find_action<action, Callback>( 1 + baseindex, static_cast<short int>(chunk >> 1 * 16), state, callback)) return false;};
+ if (gt ? static_cast<short int>(chunk >> 2 * 16) > v : static_cast<short int>(chunk >> 2 * 16) < v) {if (!find_action<action, Callback>( 2 + baseindex, static_cast<short int>(chunk >> 2 * 16), state, callback)) return false;};
+ if (gt ? static_cast<short int>(chunk >> 3 * 16) > v : static_cast<short int>(chunk >> 3 * 16) < v) {if (!find_action<action, Callback>( 3 + baseindex, static_cast<short int>(chunk >> 3 * 16), state, callback)) return false;};
+ }
+ else if (width == 32) {
+ if (gt ? static_cast<int>(chunk) > v : static_cast<int>(chunk) < v) {if (!find_action<action, Callback>( 0 + baseindex, static_cast<int>(chunk), state, callback)) return false;}
+ chunk >>= 32;
+ if (gt ? static_cast<int>(chunk) > v : static_cast<int>(chunk) < v) {if (!find_action<action, Callback>( 1 + baseindex, static_cast<int>(chunk), state, callback)) return false;}
+ chunk >>= 32;
+ }
+ else if (width == 64) {
+ if (gt ? static_cast<int64_t>(v) > v : static_cast<int64_t>(v) < v) {if (!find_action<action, Callback>( 0 + baseindex, static_cast<int64_t>(v), state, callback)) return false;};
+ }
+
+ return true;
+}
+// clang-format on
+
+/// Find items in this Array that are equal (eq == true) or different (eq = false) from 'value'
+template <bool eq, Action action, size_t width, class Callback>
+inline bool Array::compare_equality(int64_t value, size_t start, size_t end, size_t baseindex,
+ QueryState<int64_t>* state, Callback callback) const
+{
+ REALM_ASSERT_DEBUG(start <= m_size && (end <= m_size || end == size_t(-1)) && start <= end);
+
+ size_t ee = round_up(start, 64 / no0(width));
+ ee = ee > end ? end : ee;
+ for (; start < ee; ++start)
+ if (eq ? (get<width>(start) == value) : (get<width>(start) != value)) {
+ if (!find_action<action, Callback>(start + baseindex, get<width>(start), state, callback))
+ return false;
+ }
+
+ if (start >= end)
+ return true;
+
+ if (width != 32 && width != 64) {
+ const int64_t* p = reinterpret_cast<const int64_t*>(m_data + (start * width / 8));
+ const int64_t* const e = reinterpret_cast<int64_t*>(m_data + (end * width / 8)) - 1;
+ const uint64_t mask = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) -
+ 1ULL)); // Warning free way of computing (1ULL << width) - 1
+ const uint64_t valuemask =
+ ~0ULL / no0(mask) * (value & mask); // the "== ? :" is to avoid division by 0 compiler error
+
+ while (p < e) {
+ uint64_t chunk = *p;
+ uint64_t v2 = chunk ^ valuemask;
+ start = (p - reinterpret_cast<int64_t*>(m_data)) * 8 * 8 / no0(width);
+ size_t a = 0;
+
+ while (eq ? test_zero<width>(v2) : v2) {
+
+ if (find_action_pattern<action, Callback>(start + baseindex, cascade<width, eq>(v2), state, callback))
+ break; // consumed
+
+ size_t t = find_zero<eq, width>(v2);
+ a += t;
+
+ if (a >= 64 / no0(width))
+ break;
+
+ if (!find_action<action, Callback>(a + start + baseindex, get<width>(start + t), state, callback))
+ return false;
+ v2 >>= (t + 1) * width;
+ a += 1;
+ }
+
+ ++p;
+ }
+
+ // Loop ended because we are near end or end of array. No need to optimize search in remainder in this case
+ // because end of array means that
+ // lots of search work has taken place prior to ending here. So time spent searching remainder is relatively
+ // tiny
+ start = (p - reinterpret_cast<int64_t*>(m_data)) * 8 * 8 / no0(width);
+ }
+
+ while (start < end) {
+ if (eq ? get<width>(start) == value : get<width>(start) != value) {
+ if (!find_action<action, Callback>(start + baseindex, get<width>(start), state, callback))
+ return false;
+ }
+ ++start;
+ }
+
+ return true;
+}
+
+// There exists a couple of find() functions that take more or less template arguments. Always call the one that
+// takes as most as possible to get best performance.
+
+// This is the one installed into the m_vtable->finder slots.
+template <class cond, Action action, size_t bitwidth>
+bool Array::find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state) const
+{
+ return find<cond, action, bitwidth>(value, start, end, baseindex, state, CallbackDummy());
+}
+
+template <class cond, Action action, class Callback>
+bool Array::find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+ Callback callback, bool nullable_array, bool find_null) const
+{
+ REALM_TEMPEX4(return find, cond, action, m_width, Callback,
+ (value, start, end, baseindex, state, callback, nullable_array, find_null));
+}
+
+template <class cond, Action action, size_t bitwidth, class Callback>
+bool Array::find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+ Callback callback, bool nullable_array, bool find_null) const
+{
+ return find_optimized<cond, action, bitwidth, Callback>(value, start, end, baseindex, state, callback,
+ nullable_array, find_null);
+}
+
+#ifdef REALM_COMPILER_SSE
+// 'items' is the number of 16-byte SSE chunks. Returns index of packed element relative to first integer of first
+// chunk
+template <class cond, Action action, size_t width, class Callback>
+bool Array::find_sse(int64_t value, __m128i* data, size_t items, QueryState<int64_t>* state, size_t baseindex,
+ Callback callback) const
+{
+ __m128i search = {0};
+
+ if (width == 8)
+ search = _mm_set1_epi8(static_cast<char>(value));
+ else if (width == 16)
+ search = _mm_set1_epi16(static_cast<short int>(value));
+ else if (width == 32)
+ search = _mm_set1_epi32(static_cast<int>(value));
+ else if (width == 64) {
+ if (std::is_same<cond, Less>::value)
+ REALM_ASSERT(false);
+ else
+ search = _mm_set_epi64x(value, value);
+ }
+
+ return find_sse_intern<cond, action, width, Callback>(data, &search, items, state, baseindex, callback);
+}
+
+// Compares packed action_data with packed data (equal, less, etc) and performs aggregate action (max, min, sum,
+// find_all, etc) on value inside action_data for first match, if any
+template <class cond, Action action, size_t width, class Callback>
+REALM_FORCEINLINE bool Array::find_sse_intern(__m128i* action_data, __m128i* data, size_t items,
+ QueryState<int64_t>* state, size_t baseindex, Callback callback) const
+{
+ size_t i = 0;
+ __m128i compare_result = {0};
+ unsigned int resmask;
+
+ // Search loop. Unrolling it has been tested to NOT increase performance (apparently mem bound)
+ for (i = 0; i < items; ++i) {
+ // equal / not-equal
+ if (std::is_same<cond, Equal>::value || std::is_same<cond, NotEqual>::value) {
+ if (width == 8)
+ compare_result = _mm_cmpeq_epi8(action_data[i], *data);
+ if (width == 16)
+ compare_result = _mm_cmpeq_epi16(action_data[i], *data);
+ if (width == 32)
+ compare_result = _mm_cmpeq_epi32(action_data[i], *data);
+ if (width == 64) {
+ compare_result = _mm_cmpeq_epi64(action_data[i], *data); // SSE 4.2 only
+ }
+ }
+
+ // greater
+ else if (std::is_same<cond, Greater>::value) {
+ if (width == 8)
+ compare_result = _mm_cmpgt_epi8(action_data[i], *data);
+ if (width == 16)
+ compare_result = _mm_cmpgt_epi16(action_data[i], *data);
+ if (width == 32)
+ compare_result = _mm_cmpgt_epi32(action_data[i], *data);
+ if (width == 64)
+ compare_result = _mm_cmpgt_epi64(action_data[i], *data);
+ }
+ // less
+ else if (std::is_same<cond, Less>::value) {
+ if (width == 8)
+ compare_result = _mm_cmplt_epi8(action_data[i], *data);
+ else if (width == 16)
+ compare_result = _mm_cmplt_epi16(action_data[i], *data);
+ else if (width == 32)
+ compare_result = _mm_cmplt_epi32(action_data[i], *data);
+ else
+ REALM_ASSERT(false);
+ }
+
+ resmask = _mm_movemask_epi8(compare_result);
+
+ if (std::is_same<cond, NotEqual>::value)
+ resmask = ~resmask & 0x0000ffff;
+
+ size_t s = i * sizeof(__m128i) * 8 / no0(width);
+
+ while (resmask != 0) {
+
+ uint64_t upper = lower_bits<width / 8>() << (no0(width / 8) - 1);
+ uint64_t pattern =
+ resmask &
+ upper; // fixme, bits at wrong offsets. Only OK because we only use them in 'count' aggregate
+ if (find_action_pattern<action, Callback>(s + baseindex, pattern, state, callback))
+ break;
+
+ size_t idx = first_set_bit(resmask) * 8 / no0(width);
+ s += idx;
+ if (!find_action<action, Callback>(
+ s + baseindex, get_universal<width>(reinterpret_cast<char*>(action_data), s), state, callback))
+ return false;
+ resmask >>= (idx + 1) * no0(width) / 8;
+ ++s;
+ }
+ }
+
+ return true;
+}
+#endif // REALM_COMPILER_SSE
+
+template <class cond, Action action, class Callback>
+bool Array::compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex,
+ QueryState<int64_t>* state, Callback callback) const
+{
+ cond c;
+ REALM_ASSERT_3(start, <=, end);
+ if (start == end)
+ return true;
+
+
+ int64_t v;
+
+ // We can compare first element without checking for out-of-range
+ v = get(start);
+ if (c(v, foreign->get(start))) {
+ if (!find_action<action, Callback>(start + baseindex, v, state, callback))
+ return false;
+ }
+
+ start++;
+
+ if (start + 3 < end) {
+ v = get(start);
+ if (c(v, foreign->get(start)))
+ if (!find_action<action, Callback>(start + baseindex, v, state, callback))
+ return false;
+
+ v = get(start + 1);
+ if (c(v, foreign->get(start + 1)))
+ if (!find_action<action, Callback>(start + 1 + baseindex, v, state, callback))
+ return false;
+
+ v = get(start + 2);
+ if (c(v, foreign->get(start + 2)))
+ if (!find_action<action, Callback>(start + 2 + baseindex, v, state, callback))
+ return false;
+
+ start += 3;
+ }
+ else if (start == end) {
+ return true;
+ }
+
+ bool r;
+ REALM_TEMPEX4(r = compare_leafs, cond, action, m_width, Callback,
+ (foreign, start, end, baseindex, state, callback))
+ return r;
+}
+
+
+template <class cond, Action action, size_t width, class Callback>
+bool Array::compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex,
+ QueryState<int64_t>* state, Callback callback) const
+{
+ size_t fw = foreign->m_width;
+ bool r;
+ REALM_TEMPEX5(r = compare_leafs_4, cond, action, width, Callback, fw,
+ (foreign, start, end, baseindex, state, callback))
+ return r;
+}
+
+
+template <class cond, Action action, size_t width, class Callback, size_t foreign_width>
+bool Array::compare_leafs_4(const Array* foreign, size_t start, size_t end, size_t baseindex,
+ QueryState<int64_t>* state, Callback callback) const
+{
+ cond c;
+ char* foreign_m_data = foreign->m_data;
+
+ if (width == 0 && foreign_width == 0) {
+ if (c(0, 0)) {
+ while (start < end) {
+ if (!find_action<action, Callback>(start + baseindex, 0, state, callback))
+ return false;
+ start++;
+ }
+ }
+ else {
+ return true;
+ }
+ }
+
+
+#if defined(REALM_COMPILER_SSE)
+ if (sseavx<42>() && width == foreign_width && (width == 8 || width == 16 || width == 32)) {
+ // We can only use SSE if both bitwidths are equal and above 8 bits and all values are signed
+ while (start < end && (((reinterpret_cast<size_t>(m_data) & 0xf) * 8 + start * width) % (128) != 0)) {
+ int64_t v = get_universal<width>(m_data, start);
+ int64_t fv = get_universal<foreign_width>(foreign_m_data, start);
+ if (c(v, fv)) {
+ if (!find_action<action, Callback>(start + baseindex, v, state, callback))
+ return false;
+ }
+ start++;
+ }
+ if (start == end)
+ return true;
+
+
+ size_t sse_items = (end - start) * width / 128;
+ size_t sse_end = start + sse_items * 128 / no0(width);
+
+ while (start < sse_end) {
+ __m128i* a = reinterpret_cast<__m128i*>(m_data + start * width / 8);
+ __m128i* b = reinterpret_cast<__m128i*>(foreign_m_data + start * width / 8);
+
+ bool continue_search =
+ find_sse_intern<cond, action, width, Callback>(a, b, 1, state, baseindex + start, callback);
+
+ if (!continue_search)
+ return false;
+
+ start += 128 / no0(width);
+ }
+ }
+#endif
+
+ while (start < end) {
+ int64_t v = get_universal<width>(m_data, start);
+ int64_t fv = get_universal<foreign_width>(foreign_m_data, start);
+
+ if (c(v, fv)) {
+ if (!find_action<action, Callback>(start + baseindex, v, state, callback))
+ return false;
+ }
+
+ start++;
+ }
+
+ return true;
+}
+
+
+template <class cond, Action action, size_t bitwidth, class Callback>
+bool Array::compare(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+ Callback callback) const
+{
+ bool ret = false;
+
+ if (std::is_same<cond, Equal>::value)
+ ret = compare_equality<true, action, bitwidth, Callback>(value, start, end, baseindex, state, callback);
+ else if (std::is_same<cond, NotEqual>::value)
+ ret = compare_equality<false, action, bitwidth, Callback>(value, start, end, baseindex, state, callback);
+ else if (std::is_same<cond, Greater>::value)
+ ret = compare_relation<true, action, bitwidth, Callback>(value, start, end, baseindex, state, callback);
+ else if (std::is_same<cond, Less>::value)
+ ret = compare_relation<false, action, bitwidth, Callback>(value, start, end, baseindex, state, callback);
+ else
+ REALM_ASSERT_DEBUG(false);
+
+ return ret;
+}
+
+template <bool gt, Action action, size_t bitwidth, class Callback>
+bool Array::compare_relation(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+ Callback callback) const
+{
+ REALM_ASSERT(start <= m_size && (end <= m_size || end == size_t(-1)) && start <= end);
+ uint64_t mask = (bitwidth == 64 ? ~0ULL : ((1ULL << (bitwidth == 64 ? 0 : bitwidth)) -
+ 1ULL)); // Warning free way of computing (1ULL << width) - 1
+
+ size_t ee = round_up(start, 64 / no0(bitwidth));
+ ee = ee > end ? end : ee;
+ for (; start < ee; start++) {
+ if (gt ? (get<bitwidth>(start) > value) : (get<bitwidth>(start) < value)) {
+ if (!find_action<action, Callback>(start + baseindex, get<bitwidth>(start), state, callback))
+ return false;
+ }
+ }
+
+ if (start >= end)
+ return true; // none found, continue (return true) regardless what find_action() would have returned on match
+
+ const int64_t* p = reinterpret_cast<const int64_t*>(m_data + (start * bitwidth / 8));
+ const int64_t* const e = reinterpret_cast<int64_t*>(m_data + (end * bitwidth / 8)) - 1;
+
+ // Matches are rare enough to setup fast linear search for remaining items. We use
+ // bit hacks from http://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord
+
+ if (bitwidth == 1 || bitwidth == 2 || bitwidth == 4 || bitwidth == 8 || bitwidth == 16) {
+ uint64_t magic = find_gtlt_magic<gt, bitwidth>(value);
+
+ // Bit hacks only work if searched item has its most significant bit clear for 'greater than' or
+ // 'item <= 1 << bitwidth' for 'less than'
+ if (value != int64_t((magic & mask)) && value >= 0 && bitwidth >= 2 &&
+ value <= static_cast<int64_t>((mask >> 1) - (gt ? 1 : 0))) {
+ // 15 ms
+ while (p < e) {
+ uint64_t upper = lower_bits<bitwidth>() << (no0(bitwidth) - 1);
+
+ const int64_t v = *p;
+ size_t idx;
+
+ // Bit hacks only works if all items in chunk have their most significant bit clear. Test this:
+ upper = upper & v;
+
+ if (!upper) {
+ idx = find_gtlt_fast<gt, action, bitwidth, Callback>(
+ v, magic, state, (p - reinterpret_cast<int64_t*>(m_data)) * 8 * 8 / no0(bitwidth) + baseindex,
+ callback);
+ }
+ else
+ idx = find_gtlt<gt, action, bitwidth, Callback>(
+ value, v, state, (p - reinterpret_cast<int64_t*>(m_data)) * 8 * 8 / no0(bitwidth) + baseindex,
+ callback);
+
+ if (!idx)
+ return false;
+ ++p;
+ }
+ }
+ else {
+ // 24 ms
+ while (p < e) {
+ int64_t v = *p;
+ if (!find_gtlt<gt, action, bitwidth, Callback>(
+ value, v, state, (p - reinterpret_cast<int64_t*>(m_data)) * 8 * 8 / no0(bitwidth) + baseindex,
+ callback))
+ return false;
+ ++p;
+ }
+ }
+ start = (p - reinterpret_cast<int64_t*>(m_data)) * 8 * 8 / no0(bitwidth);
+ }
+
+ // matchcount logic in SIMD no longer pays off for 32/64 bit ints because we have just 4/2 elements
+
+ // Test unaligned end and/or values of width > 16 manually
+ while (start < end) {
+ if (gt ? get<bitwidth>(start) > value : get<bitwidth>(start) < value) {
+ if (!find_action<action, Callback>(start + baseindex, get<bitwidth>(start), state, callback))
+ return false;
+ }
+ ++start;
+ }
+ return true;
+}
+
+template <class cond>
+size_t Array::find_first(int64_t value, size_t start, size_t end) const
+{
+ REALM_ASSERT(start <= m_size && (end <= m_size || end == size_t(-1)) && start <= end);
+ QueryState<int64_t> state;
+ state.init(act_ReturnFirst, nullptr,
+ 1); // todo, would be nice to avoid this in order to speed up find_first loops
+ Finder finder = m_vtable->finder[cond::condition];
+ (this->*finder)(value, start, end, 0, &state);
+
+ return static_cast<size_t>(state.m_state);
+}
+
+//*************************************************************************************
+// Finding code ends *
+//*************************************************************************************
+
+
+} // namespace realm
+
+#endif // REALM_ARRAY_HPP