buffer.hpp 8.2 KB

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