1 /*************************************************************************
3 * Copyright 2016 Realm Inc.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 **************************************************************************/
19 #ifndef REALM_GROUP_WRITER_HPP
20 #define REALM_GROUP_WRITER_HPP
22 #include <cstdint> // unint8_t etc
25 #include <realm/util/file.hpp>
26 #include <realm/alloc.hpp>
27 #include <realm/impl/array_writer.hpp>
28 #include <realm/array_integer.hpp>
38 /// This class is not supposed to be reused for multiple write sessions. In
39 /// particular, do not reuse it in case any of the functions throw.
41 /// FIXME: Move this class to namespace realm::_impl and to subdir src/realm/impl.
42 class GroupWriter : public _impl::ArrayWriterBase {
44 // For groups in transactional mode (Group::m_is_shared), this constructor
45 // must be called while a write transaction is in progress.
47 // The constructor adds free-space tracking information to the specified
48 // group, if it is not already present (4th and 5th entry in
49 // Group::m_top). If the specified group is in transactional mode
50 // (Group::m_is_shared), the constructor also adds version tracking
51 // information to the group, if it is not already present (6th and 7th entry
56 void set_versions(uint64_t current, uint64_t read_lock) noexcept;
58 /// Write all changed array nodes into free space.
60 /// Returns the new top ref. When in full durability mode, call
61 /// commit() with the returned top ref.
62 ref_type write_group();
64 /// Flush changes to physical medium, then write the new top ref
65 /// to the file header, then flush again. Pass the top ref
66 /// returned by write_group().
67 void commit(ref_type new_top_ref);
69 size_t get_file_size() const noexcept;
71 /// Write the specified chunk into free space.
72 void write(const char* data, size_t size);
74 ref_type write_array(const char*, size_t, uint32_t) override;
80 size_t get_free_space();
85 ArrayInteger m_free_positions; // 4th slot in Group::m_top
86 ArrayInteger m_free_lengths; // 5th slot in Group::m_top
87 ArrayInteger m_free_versions; // 6th slot in Group::m_top
88 uint64_t m_current_version;
89 uint64_t m_readlock_version;
90 size_t m_alloc_position;
92 // Currently cached memory mappings. We keep as many as 16 1MB windows
93 // open for writing. The allocator will favor sequential allocation
94 // from a modest number of windows, depending upon fragmentation, so
95 // 16 windows should be more than enough. If more than 16 windows are
96 // needed, the least recently used is sync'ed and closed to make room
97 // for a new one. The windows are kept in MRU (most recently used) order.
98 const static int num_map_windows = 16;
99 std::vector<std::unique_ptr<MapWindow>> m_map_windows;
101 // Get a suitable memory mapping for later access:
102 // potentially adding it to the cache, potentially closing
103 // the least recently used and sync'ing it to disk
104 MapWindow* get_window(ref_type start_ref, size_t size);
106 // Sync all cached memory mappings
107 void sync_all_mappings();
109 // Merge adjacent chunks
110 void merge_free_space();
112 /// Allocate a chunk of free space of the specified size. The
113 /// specified size must be 8-byte aligned. Extend the file if
114 /// required. The returned chunk is removed from the amount of
115 /// remaing free space. The returned chunk is guaranteed to be
116 /// within a single contiguous memory mapping.
118 /// \return The position within the database file of the allocated
120 size_t get_free_space(size_t size);
122 /// Find a block of free space that is at least as big as the
123 /// specified size and which will allow an allocation that is mapped
124 /// inside a contiguous address range. The specified size does not
125 /// need to be 8-byte aligned. Extend the file if required.
126 /// The returned chunk is not removed from the amount of remaing
129 /// \return A pair (`chunk_ndx`, `chunk_size`) where `chunk_ndx`
130 /// is the index of a chunk whose size is at least the requestd
131 /// size, and `chunk_size` is the size of that chunk.
132 std::pair<size_t, size_t> reserve_free_space(size_t size);
134 /// Search only a range of the free list for a block as big as the
135 /// specified size. Return a pair with index and size of the found chunk.
136 /// \param found indicates whether a suitable block was found.
137 std::pair<size_t, size_t> search_free_space_in_part_of_freelist(size_t size, size_t begin, size_t end,
140 /// Extend the file to ensure that a chunk of free space of the
141 /// specified size is available. The specified size does not need
142 /// to be 8-byte aligned. This function guarantees that it will
143 /// add at most one entry to the free-lists.
145 /// \return A pair (`chunk_ndx`, `chunk_size`) where `chunk_ndx`
146 /// is the index of a chunk whose size is at least the requestd
147 /// size, and `chunk_size` is the size of that chunk.
148 std::pair<size_t, size_t> extend_free_space(size_t requested_size);
150 void write_array_at(MapWindow* window, ref_type, const char* data, size_t size);
151 size_t split_freelist_chunk(size_t index, size_t start_pos, size_t alloc_pos, size_t chunk_size, bool is_shared);
157 inline void GroupWriter::set_versions(uint64_t current, uint64_t read_lock) noexcept
159 REALM_ASSERT(read_lock <= current);
160 m_current_version = current;
161 m_readlock_version = read_lock;
166 #endif // REALM_GROUP_WRITER_HPP