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_UTIL_BUFFER_HPP
20 #define REALM_UTIL_BUFFER_HPP
28 #include <realm/util/features.h>
29 #include <realm/utilities.hpp>
30 #include <realm/util/safe_int_ops.hpp>
37 /// A simple buffer concept that owns a region of memory and knows its
46 explicit Buffer(size_t initial_size);
47 Buffer(Buffer<T>&&) noexcept = default;
52 Buffer<T>& operator=(Buffer<T>&&) noexcept = default;
54 T& operator[](size_t i) noexcept
58 const T& operator[](size_t i) const noexcept
67 const T* data() const noexcept
71 size_t size() const noexcept
76 /// False iff the data() returns null.
77 explicit operator bool() const noexcept
82 /// Discards the original contents.
83 void set_size(size_t new_size);
85 /// \param new_size Specifies the new buffer size.
86 /// \param copy_begin, copy_end Specifies a range of element
87 /// values to be retained. \a copy_end must be less than, or equal
90 /// \param copy_to Specifies where the retained range should be
91 /// copied to. `\a copy_to + \a copy_end - \a copy_begin` must be
92 /// less than, or equal to \a new_size.
93 void resize(size_t new_size, size_t copy_begin, size_t copy_end, size_t copy_to);
95 void reserve(size_t used_size, size_t min_capacity);
97 void reserve_extra(size_t used_size, size_t min_extra_capacity);
99 T* release() noexcept;
101 friend void swap(Buffer& a, Buffer& b) noexcept
104 swap(a.m_data, b.m_data);
105 swap(a.m_size, b.m_size);
109 std::unique_ptr<T[]> m_data;
114 /// A buffer that can be efficiently resized. It acheives this by
115 /// using an underlying buffer that may be larger than the logical
116 /// size, and is automatically expanded in progressively larger steps.
120 AppendBuffer() noexcept;
121 ~AppendBuffer() noexcept
125 AppendBuffer(AppendBuffer&&) noexcept = default;
126 AppendBuffer& operator=(AppendBuffer&&) noexcept = default;
128 /// Returns the current size of the buffer.
129 size_t size() const noexcept;
131 /// Gives read and write access to the elements.
134 /// Gives read access the elements.
135 const T* data() const noexcept;
137 /// Append the specified elements. This increases the size of this
138 /// buffer by \a append_data_size. If the caller has previously requested
139 /// a minimum capacity that is greater than, or equal to the
140 /// resulting size, this function is guaranteed to not throw.
141 void append(const T* append_data, size_t append_data_size);
143 /// If the specified size is less than the current size, then the
144 /// buffer contents is truncated accordingly. If the specified
145 /// size is greater than the current size, then the extra elements
146 /// will have undefined values. If the caller has previously
147 /// requested a minimum capacity that is greater than, or equal to
148 /// the specified size, this function is guaranteed to not throw.
149 void resize(size_t new_size);
151 /// This operation does not change the size of the buffer as
152 /// returned by size(). If the specified capacity is less than the
153 /// current capacity, this operation has no effect.
154 void reserve(size_t min_capacity);
156 /// Set the size to zero. The capacity remains unchanged.
157 void clear() noexcept;
159 /// Release the underlying buffer and reset the size. Note: The returned
160 /// buffer may be larger than the amount of data appended to this buffer.
161 /// Callers should call `size()` prior to releasing the buffer to know the
162 /// usable/logical size.
163 Buffer<T> release() noexcept;
166 util::Buffer<T> m_buffer;
173 class BufferSizeOverflow : public std::exception {
175 const char* what() const noexcept override
177 return "Buffer size overflow";
182 inline Buffer<T>::Buffer(size_t initial_size)
183 : m_data(new T[initial_size]) // Throws
184 , m_size(initial_size)
189 inline void Buffer<T>::set_size(size_t new_size)
191 m_data.reset(new T[new_size]); // Throws
196 inline void Buffer<T>::resize(size_t new_size, size_t copy_begin, size_t copy_end, size_t copy_to)
198 std::unique_ptr<T[]> new_data(new T[new_size]); // Throws
199 realm::safe_copy_n(m_data.get() + copy_begin, copy_end - copy_begin, new_data.get() + copy_to);
200 m_data.reset(new_data.release());
205 inline void Buffer<T>::reserve(size_t used_size, size_t min_capacity)
207 size_t current_capacity = m_size;
208 if (REALM_LIKELY(current_capacity >= min_capacity))
210 size_t new_capacity = current_capacity;
212 // Use growth factor 1.5.
213 if (REALM_UNLIKELY(int_multiply_with_overflow_detect(new_capacity, 3)))
214 new_capacity = std::numeric_limits<size_t>::max();
217 if (REALM_UNLIKELY(new_capacity < min_capacity))
218 new_capacity = min_capacity;
219 resize(new_capacity, 0, used_size, 0); // Throws
223 inline void Buffer<T>::reserve_extra(size_t used_size, size_t min_extra_capacity)
225 size_t min_capacity = used_size;
226 if (REALM_UNLIKELY(int_add_with_overflow_detect(min_capacity, min_extra_capacity)))
227 throw BufferSizeOverflow();
228 reserve(used_size, min_capacity); // Throws
232 inline T* Buffer<T>::release() noexcept
235 return m_data.release();
240 inline AppendBuffer<T>::AppendBuffer() noexcept
246 inline size_t AppendBuffer<T>::size() const noexcept
252 inline T* AppendBuffer<T>::data() noexcept
254 return m_buffer.data();
258 inline const T* AppendBuffer<T>::data() const noexcept
260 return m_buffer.data();
264 inline void AppendBuffer<T>::append(const T* append_data, size_t append_data_size)
266 m_buffer.reserve_extra(m_size, append_data_size); // Throws
267 realm::safe_copy_n(append_data, append_data_size, m_buffer.data() + m_size);
268 m_size += append_data_size;
272 inline void AppendBuffer<T>::reserve(size_t min_capacity)
274 m_buffer.reserve(m_size, min_capacity);
278 inline void AppendBuffer<T>::resize(size_t new_size)
285 inline void AppendBuffer<T>::clear() noexcept
291 inline Buffer<T> AppendBuffer<T>::release() noexcept
294 return std::move(m_buffer);
301 #endif // REALM_UTIL_BUFFER_HPP