added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / core / realm / alloc.hpp
diff --git a/iOS/Pods/Realm/include/core/realm/alloc.hpp b/iOS/Pods/Realm/include/core/realm/alloc.hpp
new file mode 100644 (file)
index 0000000..6e25168
--- /dev/null
@@ -0,0 +1,387 @@
+/*************************************************************************
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef REALM_ALLOC_HPP
+#define REALM_ALLOC_HPP
+
+#include <cstdint>
+#include <cstddef>
+#include <atomic>
+
+#include <realm/util/features.h>
+#include <realm/util/terminate.hpp>
+#include <realm/util/assert.hpp>
+
+namespace realm {
+
+class Allocator;
+
+class Replication;
+
+using ref_type = size_t;
+
+int_fast64_t from_ref(ref_type) noexcept;
+ref_type to_ref(int_fast64_t) noexcept;
+int64_t to_int64(size_t value) noexcept;
+
+class MemRef {
+public:
+    MemRef() noexcept;
+    ~MemRef() noexcept;
+
+    MemRef(char* addr, ref_type ref, Allocator& alloc) noexcept;
+    MemRef(ref_type ref, Allocator& alloc) noexcept;
+
+    char* get_addr();
+    ref_type get_ref();
+    void set_ref(ref_type ref);
+    void set_addr(char* addr);
+
+private:
+    char* m_addr;
+    ref_type m_ref;
+#if REALM_ENABLE_MEMDEBUG
+    // Allocator that created m_ref. Used to verify that the ref is valid whenever you call
+    // get_ref()/get_addr and that it e.g. has not been free'ed
+    const Allocator* m_alloc = nullptr;
+#endif
+};
+
+
+/// The common interface for Realm allocators.
+///
+/// A Realm allocator must associate a 'ref' to each allocated
+/// object and be able to efficiently map any 'ref' to the
+/// corresponding memory address. The 'ref' is an integer and it must
+/// always be divisible by 8. Also, a value of zero is used to
+/// indicate a null-reference, and must therefore never be returned by
+/// Allocator::alloc().
+///
+/// The purpose of the 'refs' is to decouple the memory reference from
+/// the actual address and thereby allowing objects to be relocated in
+/// memory without having to modify stored references.
+///
+/// \sa SlabAlloc
+class Allocator {
+public:
+    /// The specified size must be divisible by 8, and must not be
+    /// zero.
+    ///
+    /// \throw std::bad_alloc If insufficient memory was available.
+    MemRef alloc(size_t size);
+
+    /// Calls do_realloc().
+    ///
+    /// Note: The underscore has been added because the name `realloc`
+    /// would conflict with a macro on the Windows platform.
+    MemRef realloc_(ref_type, const char* addr, size_t old_size, size_t new_size);
+
+    /// Calls do_free().
+    ///
+    /// Note: The underscore has been added because the name `free
+    /// would conflict with a macro on the Windows platform.
+    void free_(ref_type, const char* addr) noexcept;
+
+    /// Shorthand for free_(mem.get_ref(), mem.get_addr()).
+    void free_(MemRef mem) noexcept;
+
+    /// Calls do_translate().
+    char* translate(ref_type ref) const noexcept;
+
+    /// Returns true if, and only if the object at the specified 'ref'
+    /// is in the immutable part of the memory managed by this
+    /// allocator. The method by which some objects become part of the
+    /// immuatble part is entirely up to the class that implements
+    /// this interface.
+    bool is_read_only(ref_type) const noexcept;
+
+    /// Returns a simple allocator that can be used with free-standing
+    /// Realm objects (such as a free-standing table). A
+    /// free-standing object is one that is not part of a Group, and
+    /// therefore, is not part of an actual database.
+    static Allocator& get_default() noexcept;
+
+    virtual ~Allocator() noexcept;
+
+    // Disable copying. Copying an allocator can produce double frees.
+    Allocator(const Allocator&) = delete;
+    Allocator& operator=(const Allocator&) = delete;
+
+    virtual void verify() const = 0;
+
+#ifdef REALM_DEBUG
+    /// Terminate the program precisely when the specified 'ref' is
+    /// freed (or reallocated). You can use this to detect whether the
+    /// ref is freed (or reallocated), and even to get a stacktrace at
+    /// the point where it happens. Call watch(0) to stop watching
+    /// that ref.
+    void watch(ref_type ref)
+    {
+        m_debug_watch = ref;
+    }
+#endif
+
+    Replication* get_replication() noexcept;
+
+protected:
+    size_t m_baseline = 0; // Separation line between immutable and mutable refs.
+
+    Replication* m_replication = nullptr;
+
+    ref_type m_debug_watch = 0;
+
+    /// The specified size must be divisible by 8, and must not be
+    /// zero.
+    ///
+    /// \throw std::bad_alloc If insufficient memory was available.
+    virtual MemRef do_alloc(const size_t size) = 0;
+
+    /// The specified size must be divisible by 8, and must not be
+    /// zero.
+    ///
+    /// The default version of this function simply allocates a new
+    /// chunk of memory, copies over the old contents, and then frees
+    /// the old chunk.
+    ///
+    /// \throw std::bad_alloc If insufficient memory was available.
+    virtual MemRef do_realloc(ref_type, const char* addr, size_t old_size, size_t new_size) = 0;
+
+    /// Release the specified chunk of memory.
+    virtual void do_free(ref_type, const char* addr) noexcept = 0;
+
+    /// Map the specified \a ref to the corresponding memory
+    /// address. Note that if is_read_only(ref) returns true, then the
+    /// referenced object is to be considered immutable, and it is
+    /// then entirely the responsibility of the caller that the memory
+    /// is not modified by way of the returned memory pointer.
+    virtual char* do_translate(ref_type ref) const noexcept = 0;
+
+    Allocator() noexcept;
+
+    // FIXME: This really doesn't belong in an allocator, but it is the best
+    // place for now, because every table has a pointer leading here. It would
+    // be more obvious to place it in Group, but that would add a runtime overhead,
+    // and access is time critical.
+    //
+    // This means that multiple threads that allocate Realm objects through the
+    // default allocator will share this variable, which is a logical design flaw
+    // that can make sync_if_needed() re-run queries even though it is not required.
+    // It must be atomic because it's shared.
+    std::atomic<uint_fast64_t> m_table_versioning_counter;
+    std::atomic<uint_fast64_t> m_latest_observed_counter;
+
+    /// Bump the global version counter. This method should be called when
+    /// version bumping is initiated. Then following calls to should_propagate_version()
+    /// can be used to prune the version bumping.
+    void bump_global_version() noexcept;
+
+    /// Determine if the "local_version" is out of sync, so that it should
+    /// be updated. In that case: also update it. Called from Table::bump_version
+    /// to control propagation of version updates on tables within the group.
+    bool should_propagate_version(uint_fast64_t& local_version) noexcept;
+
+    /// Note the current global version has been observed.
+    void observe_version() noexcept;
+
+    friend class Table;
+    friend class Group;
+};
+
+inline void Allocator::bump_global_version() noexcept
+{
+    if (m_latest_observed_counter == m_table_versioning_counter)
+        m_table_versioning_counter += 1;
+}
+
+
+inline void Allocator::observe_version() noexcept
+{
+    if (m_latest_observed_counter != m_table_versioning_counter)
+        m_latest_observed_counter.store(m_table_versioning_counter, std::memory_order_relaxed);
+}
+
+
+inline bool Allocator::should_propagate_version(uint_fast64_t& local_version) noexcept
+{
+    if (local_version != m_table_versioning_counter) {
+        local_version = m_table_versioning_counter;
+        return true;
+    }
+    else {
+        return false;
+    }
+}
+
+
+// Implementation:
+
+inline int_fast64_t from_ref(ref_type v) noexcept
+{
+    // Check that v is divisible by 8 (64-bit aligned).
+    REALM_ASSERT_DEBUG(v % 8 == 0);
+
+    static_assert(std::is_same<ref_type, size_t>::value,
+                  "If ref_type changes, from_ref and to_ref should probably be updated");
+
+    // Make sure that we preserve the bit pattern of the ref_type (without sign extension).
+    return util::from_twos_compl<int_fast64_t>(uint_fast64_t(v));
+}
+
+inline ref_type to_ref(int_fast64_t v) noexcept
+{
+    // Check that v is divisible by 8 (64-bit aligned).
+    REALM_ASSERT_DEBUG(v % 8 == 0);
+
+    // C++11 standard, paragraph 4.7.2 [conv.integral]:
+    // If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source
+    // integer (modulo 2n where n is the number of bits used to represent the unsigned type). [ Note: In a two's
+    // complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is
+    // no truncation). - end note ]
+    static_assert(std::is_unsigned<ref_type>::value,
+                  "If ref_type changes, from_ref and to_ref should probably be updated");
+    return ref_type(v);
+}
+
+inline int64_t to_int64(size_t value) noexcept
+{
+    //    FIXME: Enable once we get clang warning flags correct
+    //    REALM_ASSERT_DEBUG(value <= std::numeric_limits<int64_t>::max());
+    return static_cast<int64_t>(value);
+}
+
+
+inline MemRef::MemRef() noexcept
+    : m_addr(nullptr)
+    , m_ref(0)
+{
+}
+
+inline MemRef::~MemRef() noexcept
+{
+}
+
+inline MemRef::MemRef(char* addr, ref_type ref, Allocator& alloc) noexcept
+    : m_addr(addr)
+    , m_ref(ref)
+{
+    static_cast<void>(alloc);
+#if REALM_ENABLE_MEMDEBUG
+    m_alloc = &alloc;
+#endif
+}
+
+inline MemRef::MemRef(ref_type ref, Allocator& alloc) noexcept
+    : m_addr(alloc.translate(ref))
+    , m_ref(ref)
+{
+    static_cast<void>(alloc);
+#if REALM_ENABLE_MEMDEBUG
+    m_alloc = &alloc;
+#endif
+}
+
+inline char* MemRef::get_addr()
+{
+#if REALM_ENABLE_MEMDEBUG
+    // Asserts if the ref has been freed
+    m_alloc->translate(m_ref);
+#endif
+    return m_addr;
+}
+
+inline ref_type MemRef::get_ref()
+{
+#if REALM_ENABLE_MEMDEBUG
+    // Asserts if the ref has been freed
+    m_alloc->translate(m_ref);
+#endif
+    return m_ref;
+}
+
+inline void MemRef::set_ref(ref_type ref)
+{
+#if REALM_ENABLE_MEMDEBUG
+    // Asserts if the ref has been freed
+    m_alloc->translate(ref);
+#endif
+    m_ref = ref;
+}
+
+inline void MemRef::set_addr(char* addr)
+{
+    m_addr = addr;
+}
+
+inline MemRef Allocator::alloc(size_t size)
+{
+    return do_alloc(size);
+}
+
+inline MemRef Allocator::realloc_(ref_type ref, const char* addr, size_t old_size, size_t new_size)
+{
+#ifdef REALM_DEBUG
+    if (ref == m_debug_watch)
+        REALM_TERMINATE("Allocator watch: Ref was reallocated");
+#endif
+    return do_realloc(ref, addr, old_size, new_size);
+}
+
+inline void Allocator::free_(ref_type ref, const char* addr) noexcept
+{
+#ifdef REALM_DEBUG
+    if (ref == m_debug_watch)
+        REALM_TERMINATE("Allocator watch: Ref was freed");
+#endif
+    return do_free(ref, addr);
+}
+
+inline void Allocator::free_(MemRef mem) noexcept
+{
+    free_(mem.get_ref(), mem.get_addr());
+}
+
+inline char* Allocator::translate(ref_type ref) const noexcept
+{
+    return do_translate(ref);
+}
+
+inline bool Allocator::is_read_only(ref_type ref) const noexcept
+{
+    REALM_ASSERT_DEBUG(ref != 0);
+    REALM_ASSERT_DEBUG(m_baseline != 0); // Attached SlabAlloc
+    return ref < m_baseline;
+}
+
+inline Allocator::Allocator() noexcept
+{
+    m_table_versioning_counter = 0;
+    m_latest_observed_counter = 0;
+}
+
+inline Allocator::~Allocator() noexcept
+{
+}
+
+inline Replication* Allocator::get_replication() noexcept
+{
+    return m_replication;
+}
+
+} // namespace realm
+
+#endif // REALM_ALLOC_HPP