buffer.hpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /*************************************************************************
  2. *
  3. * Copyright 2016 Realm Inc.
  4. *
  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
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  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.
  16. *
  17. **************************************************************************/
  18. #ifndef REALM_UTIL_BUFFER_HPP
  19. #define REALM_UTIL_BUFFER_HPP
  20. #include <cstddef>
  21. #include <algorithm>
  22. #include <exception>
  23. #include <limits>
  24. #include <utility>
  25. #include <realm/util/features.h>
  26. #include <realm/utilities.hpp>
  27. #include <realm/util/safe_int_ops.hpp>
  28. #include <realm/util/allocator.hpp>
  29. #include <memory>
  30. namespace realm {
  31. namespace util {
  32. /// A simple buffer concept that owns a region of memory and knows its
  33. /// size.
  34. template <class T, class Allocator = DefaultAllocator>
  35. class Buffer {
  36. public:
  37. Buffer(Allocator& alloc = Allocator::get_default()) noexcept
  38. : m_data(nullptr, STLDeleter<T[], Allocator>{alloc})
  39. , m_size(0)
  40. {
  41. }
  42. explicit Buffer(size_t initial_size, Allocator& alloc = Allocator::get_default());
  43. Buffer(Buffer&&) noexcept = default;
  44. Buffer<T, Allocator>& operator=(Buffer&&) noexcept = default;
  45. T& operator[](size_t i) noexcept
  46. {
  47. return m_data[i];
  48. }
  49. const T& operator[](size_t i) const noexcept
  50. {
  51. return m_data[i];
  52. }
  53. T* data() noexcept
  54. {
  55. return m_data.get();
  56. }
  57. const T* data() const noexcept
  58. {
  59. return m_data.get();
  60. }
  61. size_t size() const noexcept
  62. {
  63. return m_size;
  64. }
  65. /// False iff the data() returns null.
  66. explicit operator bool() const noexcept
  67. {
  68. return bool(m_data);
  69. }
  70. /// Discards the original contents.
  71. void set_size(size_t new_size);
  72. /// \param new_size Specifies the new buffer size.
  73. /// \param copy_begin copy_end Specifies a range of element
  74. /// values to be retained. \a copy_end must be less than, or equal
  75. /// to size().
  76. ///
  77. /// \param copy_to Specifies where the retained range should be
  78. /// copied to. `\a copy_to + \a copy_end - \a copy_begin` must be
  79. /// less than, or equal to \a new_size.
  80. void resize(size_t new_size, size_t copy_begin, size_t copy_end, size_t copy_to);
  81. void reserve(size_t used_size, size_t min_capacity);
  82. void reserve_extra(size_t used_size, size_t min_extra_capacity);
  83. /// Release the internal buffer to the caller.
  84. REALM_NODISCARD std::unique_ptr<T[], STLDeleter<T[], Allocator>> release() noexcept;
  85. friend void swap(Buffer& a, Buffer& b) noexcept
  86. {
  87. using std::swap;
  88. swap(a.m_data, b.m_data);
  89. swap(a.m_size, b.m_size);
  90. }
  91. Allocator& get_allocator() const noexcept
  92. {
  93. return m_data.get_deleter().get_allocator();
  94. }
  95. private:
  96. std::unique_ptr<T[], STLDeleter<T[], Allocator>> m_data;
  97. size_t m_size;
  98. };
  99. /// A buffer that can be efficiently resized. It acheives this by
  100. /// using an underlying buffer that may be larger than the logical
  101. /// size, and is automatically expanded in progressively larger steps.
  102. template <class T, class Allocator = DefaultAllocator>
  103. class AppendBuffer {
  104. public:
  105. AppendBuffer(Allocator& alloc = Allocator::get_default()) noexcept;
  106. AppendBuffer(AppendBuffer&&) noexcept = default;
  107. AppendBuffer& operator=(AppendBuffer&&) noexcept = default;
  108. /// Returns the current size of the buffer.
  109. size_t size() const noexcept;
  110. /// Gives read and write access to the elements.
  111. T* data() noexcept;
  112. /// Gives read access the elements.
  113. const T* data() const noexcept;
  114. /// Append the specified elements. This increases the size of this
  115. /// buffer by \a append_data_size. If the caller has previously requested
  116. /// a minimum capacity that is greater than, or equal to the
  117. /// resulting size, this function is guaranteed to not throw.
  118. void append(const T* append_data, size_t append_data_size);
  119. /// If the specified size is less than the current size, then the
  120. /// buffer contents is truncated accordingly. If the specified
  121. /// size is greater than the current size, then the extra elements
  122. /// will have undefined values. If the caller has previously
  123. /// requested a minimum capacity that is greater than, or equal to
  124. /// the specified size, this function is guaranteed to not throw.
  125. void resize(size_t new_size);
  126. /// This operation does not change the size of the buffer as
  127. /// returned by size(). If the specified capacity is less than the
  128. /// current capacity, this operation has no effect.
  129. void reserve(size_t min_capacity);
  130. /// Set the size to zero. The capacity remains unchanged.
  131. void clear() noexcept;
  132. /// Release the underlying buffer and reset the size. Note: The returned
  133. /// buffer may be larger than the amount of data appended to this buffer.
  134. /// Callers should call `size()` prior to releasing the buffer to know the
  135. /// usable/logical size.
  136. REALM_NODISCARD Buffer<T, Allocator> release() noexcept;
  137. private:
  138. util::Buffer<T, Allocator> m_buffer;
  139. size_t m_size;
  140. };
  141. // Implementation:
  142. class BufferSizeOverflow : public std::exception {
  143. public:
  144. const char* what() const noexcept override
  145. {
  146. return "Buffer size overflow";
  147. }
  148. };
  149. template <class T, class A>
  150. inline Buffer<T, A>::Buffer(size_t initial_size, A& alloc)
  151. : m_data(util::make_unique<T[]>(alloc, initial_size)) // Throws
  152. , m_size(initial_size)
  153. {
  154. }
  155. template <class T, class A>
  156. inline void Buffer<T, A>::set_size(size_t new_size)
  157. {
  158. m_data = util::make_unique<T[]>(get_allocator(), new_size); // Throws
  159. m_size = new_size;
  160. }
  161. template <class T, class A>
  162. inline void Buffer<T, A>::resize(size_t new_size, size_t copy_begin, size_t copy_end, size_t copy_to)
  163. {
  164. auto new_data = util::make_unique<T[]>(get_allocator(), new_size); // Throws
  165. realm::safe_copy_n(m_data.get() + copy_begin, copy_end - copy_begin, new_data.get() + copy_to);
  166. m_data = std::move(new_data);
  167. m_size = new_size;
  168. }
  169. template <class T, class A>
  170. inline void Buffer<T, A>::reserve(size_t used_size, size_t min_capacity)
  171. {
  172. size_t current_capacity = m_size;
  173. if (REALM_LIKELY(current_capacity >= min_capacity))
  174. return;
  175. size_t new_capacity = current_capacity;
  176. // Use growth factor 1.5.
  177. if (REALM_UNLIKELY(int_multiply_with_overflow_detect(new_capacity, 3)))
  178. new_capacity = std::numeric_limits<size_t>::max();
  179. new_capacity /= 2;
  180. if (REALM_UNLIKELY(new_capacity < min_capacity))
  181. new_capacity = min_capacity;
  182. resize(new_capacity, 0, used_size, 0); // Throws
  183. }
  184. template <class T, class A>
  185. inline void Buffer<T, A>::reserve_extra(size_t used_size, size_t min_extra_capacity)
  186. {
  187. size_t min_capacity = used_size;
  188. if (REALM_UNLIKELY(int_add_with_overflow_detect(min_capacity, min_extra_capacity)))
  189. throw BufferSizeOverflow();
  190. reserve(used_size, min_capacity); // Throws
  191. }
  192. template <class T, class A>
  193. inline std::unique_ptr<T[], STLDeleter<T[], A>> Buffer<T, A>::release() noexcept
  194. {
  195. m_size = 0;
  196. return std::move(m_data);
  197. }
  198. template <class T, class A>
  199. inline AppendBuffer<T, A>::AppendBuffer(A& alloc) noexcept
  200. : m_buffer(alloc)
  201. , m_size(0)
  202. {
  203. }
  204. template <class T, class A>
  205. inline size_t AppendBuffer<T, A>::size() const noexcept
  206. {
  207. return m_size;
  208. }
  209. template <class T, class A>
  210. inline T* AppendBuffer<T, A>::data() noexcept
  211. {
  212. return m_buffer.data();
  213. }
  214. template <class T, class A>
  215. inline const T* AppendBuffer<T, A>::data() const noexcept
  216. {
  217. return m_buffer.data();
  218. }
  219. template <class T, class A>
  220. inline void AppendBuffer<T, A>::append(const T* append_data, size_t append_data_size)
  221. {
  222. m_buffer.reserve_extra(m_size, append_data_size); // Throws
  223. realm::safe_copy_n(append_data, append_data_size, m_buffer.data() + m_size);
  224. m_size += append_data_size;
  225. }
  226. template <class T, class A>
  227. inline void AppendBuffer<T, A>::reserve(size_t min_capacity)
  228. {
  229. m_buffer.reserve(m_size, min_capacity);
  230. }
  231. template <class T, class A>
  232. inline void AppendBuffer<T, A>::resize(size_t new_size)
  233. {
  234. reserve(new_size);
  235. m_size = new_size;
  236. }
  237. template <class T, class A>
  238. inline void AppendBuffer<T, A>::clear() noexcept
  239. {
  240. m_size = 0;
  241. }
  242. template <class T, class A>
  243. inline Buffer<T, A> AppendBuffer<T, A>::release() noexcept
  244. {
  245. m_size = 0;
  246. return std::move(m_buffer);
  247. }
  248. } // namespace util
  249. } // namespace realm
  250. #endif // REALM_UTIL_BUFFER_HPP