123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- /*************************************************************************
- *
- * 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_UTIL_BUFFER_HPP
- #define REALM_UTIL_BUFFER_HPP
- #include <cstddef>
- #include <algorithm>
- #include <exception>
- #include <limits>
- #include <utility>
- #include <realm/util/features.h>
- #include <realm/utilities.hpp>
- #include <realm/util/safe_int_ops.hpp>
- #include <memory>
- namespace realm {
- namespace util {
- /// A simple buffer concept that owns a region of memory and knows its
- /// size.
- template <class T>
- class Buffer {
- public:
- using value_type = T;
- Buffer() noexcept = default;
- explicit Buffer(size_t initial_size);
- Buffer(Buffer&&) noexcept = default;
- Buffer<T>& operator=(Buffer&&) noexcept = default;
- T& operator[](size_t i) noexcept
- {
- return m_data[i];
- }
- const T& operator[](size_t i) const noexcept
- {
- return m_data[i];
- }
- T* data() noexcept
- {
- return m_data.get();
- }
- const T* data() const noexcept
- {
- return m_data.get();
- }
- size_t size() const noexcept
- {
- return m_size;
- }
- /// False iff the data() returns null.
- explicit operator bool() const noexcept
- {
- return bool(m_data);
- }
- /// Discards the original contents.
- void set_size(size_t new_size);
- /// \param new_size Specifies the new buffer size.
- /// \param copy_begin copy_end Specifies a range of element
- /// values to be retained. \a copy_end must be less than, or equal
- /// to size().
- ///
- /// \param copy_to Specifies where the retained range should be
- /// copied to. `\a copy_to + \a copy_end - \a copy_begin` must be
- /// less than, or equal to \a new_size.
- void resize(size_t new_size, size_t copy_begin, size_t copy_end, size_t copy_to);
- void reserve(size_t used_size, size_t min_capacity);
- void reserve_extra(size_t used_size, size_t min_extra_capacity);
- /// Release the internal buffer to the caller.
- REALM_NODISCARD std::unique_ptr<T[]> release() noexcept;
- friend void swap(Buffer& a, Buffer& b) noexcept
- {
- using std::swap;
- swap(a.m_data, b.m_data);
- swap(a.m_size, b.m_size);
- }
- private:
- std::unique_ptr<T[]> m_data;
- size_t m_size = 0;
- };
- /// A buffer that can be efficiently resized. It acheives this by
- /// using an underlying buffer that may be larger than the logical
- /// size, and is automatically expanded in progressively larger steps.
- template <class T>
- class AppendBuffer {
- public:
- using value_type = T;
- AppendBuffer() noexcept = default;
- AppendBuffer(AppendBuffer&&) noexcept = default;
- AppendBuffer& operator=(AppendBuffer&&) noexcept = default;
- /// Returns the current size of the buffer.
- size_t size() const noexcept;
- /// Returns the current capcity of the buffer.
- size_t capacity() const noexcept;
- /// Gives read and write access to the elements.
- T* data() noexcept;
- /// Gives read access the elements.
- const T* data() const noexcept;
- /// Append the specified elements. This increases the size of this
- /// buffer by \a append_data_size. If the caller has previously requested
- /// a minimum capacity that is greater than, or equal to the
- /// resulting size, this function is guaranteed to not throw.
- void append(const T* append_data, size_t append_data_size);
- /// If the specified size is less than the current size, then the
- /// buffer contents is truncated accordingly. If the specified
- /// size is greater than the current size, then the extra elements
- /// will have undefined values. If the caller has previously
- /// requested a minimum capacity that is greater than, or equal to
- /// the specified size, this function is guaranteed to not throw.
- void resize(size_t new_size);
- /// This operation does not change the size of the buffer as
- /// returned by size(). If the specified capacity is less than the
- /// current capacity, this operation has no effect.
- void reserve(size_t min_capacity);
- /// Set the size to zero. The capacity remains unchanged.
- void clear() noexcept;
- /// Release the underlying buffer and reset the size. Note: The returned
- /// buffer may be larger than the amount of data appended to this buffer.
- /// Callers should call `size()` prior to releasing the buffer to know the
- /// usable/logical size.
- REALM_NODISCARD Buffer<T> release() noexcept;
- friend void swap(AppendBuffer& a, AppendBuffer& b) noexcept
- {
- using std::swap;
- swap(a.m_buffer, b.m_buffer);
- swap(a.m_size, b.m_size);
- }
- private:
- util::Buffer<T> m_buffer;
- size_t m_size = 0;
- };
- // Implementation:
- class BufferSizeOverflow : public std::exception {
- public:
- const char* what() const noexcept override
- {
- return "Buffer size overflow";
- }
- };
- template <class T>
- inline Buffer<T>::Buffer(size_t initial_size)
- : m_data(std::make_unique<T[]>(initial_size)) // Throws
- , m_size(initial_size)
- {
- }
- template <class T>
- inline void Buffer<T>::set_size(size_t new_size)
- {
- m_data = std::make_unique<T[]>(new_size); // Throws
- m_size = new_size;
- }
- template <class T>
- inline void Buffer<T>::resize(size_t new_size, size_t copy_begin, size_t copy_end, size_t copy_to)
- {
- auto new_data = std::make_unique<T[]>(new_size); // Throws
- realm::safe_copy_n(m_data.get() + copy_begin, copy_end - copy_begin, new_data.get() + copy_to);
- m_data = std::move(new_data);
- m_size = new_size;
- }
- template <class T>
- inline void Buffer<T>::reserve(size_t used_size, size_t min_capacity)
- {
- size_t current_capacity = m_size;
- if (REALM_LIKELY(current_capacity >= min_capacity))
- return;
- size_t new_capacity = current_capacity;
- // Use growth factor 1.5.
- if (REALM_UNLIKELY(int_multiply_with_overflow_detect(new_capacity, 3)))
- new_capacity = std::numeric_limits<size_t>::max();
- new_capacity /= 2;
- if (REALM_UNLIKELY(new_capacity < min_capacity))
- new_capacity = min_capacity;
- resize(new_capacity, 0, used_size, 0); // Throws
- }
- template <class T>
- inline void Buffer<T>::reserve_extra(size_t used_size, size_t min_extra_capacity)
- {
- size_t min_capacity = used_size;
- if (REALM_UNLIKELY(int_add_with_overflow_detect(min_capacity, min_extra_capacity)))
- throw BufferSizeOverflow();
- reserve(used_size, min_capacity); // Throws
- }
- template <class T>
- inline std::unique_ptr<T[]> Buffer<T>::release() noexcept
- {
- m_size = 0;
- return std::move(m_data);
- }
- template <class T>
- inline size_t AppendBuffer<T>::size() const noexcept
- {
- return m_size;
- }
- template <class T>
- inline size_t AppendBuffer<T>::capacity() const noexcept
- {
- return m_buffer.size();
- }
- template <class T>
- inline T* AppendBuffer<T>::data() noexcept
- {
- return m_buffer.data();
- }
- template <class T>
- inline const T* AppendBuffer<T>::data() const noexcept
- {
- return m_buffer.data();
- }
- template <class T>
- inline void AppendBuffer<T>::append(const T* append_data, size_t append_data_size)
- {
- m_buffer.reserve_extra(m_size, append_data_size); // Throws
- realm::safe_copy_n(append_data, append_data_size, m_buffer.data() + m_size);
- m_size += append_data_size;
- }
- template <class T>
- inline void AppendBuffer<T>::reserve(size_t min_capacity)
- {
- m_buffer.reserve(m_size, min_capacity);
- }
- template <class T>
- inline void AppendBuffer<T>::resize(size_t new_size)
- {
- reserve(new_size);
- m_size = new_size;
- }
- template <class T>
- inline void AppendBuffer<T>::clear() noexcept
- {
- m_size = 0;
- }
- template <class T>
- inline Buffer<T> AppendBuffer<T>::release() noexcept
- {
- m_size = 0;
- return std::move(m_buffer);
- }
- } // namespace util
- } // namespace realm
- #endif // REALM_UTIL_BUFFER_HPP
|