1#ifndef OSMIUM_MEMORY_BUFFER_HPP
2#define OSMIUM_MEMORY_BUFFER_HPP
58 struct OSMIUM_EXPORT buffer_is_full :
public std::runtime_error {
61 std::runtime_error{
"Osmium buffer is full"} {
96 using value_type = Item;
98 enum class auto_grow {
106 std::unique_ptr<Buffer> m_next_buffer;
107 std::unique_ptr<unsigned char[]> m_memory{};
108 unsigned char* m_data =
nullptr;
109 std::size_t m_capacity = 0;
110 std::size_t m_written = 0;
111 std::size_t m_committed = 0;
113 uint8_t m_builder_count = 0;
115 auto_grow m_auto_grow{auto_grow::no};
117 static std::size_t calculate_capacity(std::size_t capacity)
noexcept {
123 if (capacity < min_capacity) {
129 void grow_internal() {
130 assert(m_data &&
"This must be a valid buffer");
132 throw std::logic_error{
"Can't grow Buffer if it doesn't use internal memory management."};
135 std::unique_ptr<Buffer> old{
new Buffer{std::move(m_memory), m_capacity, m_committed}};
136 m_memory = std::unique_ptr<unsigned char[]>{
new unsigned char[m_capacity]};
137 m_data = m_memory.get();
139 m_written -= m_committed;
140 std::copy_n(old->data() + m_committed, m_written, m_data);
143 old->m_next_buffer = std::move(m_next_buffer);
144 m_next_buffer = std::move(old);
157 Buffer() noexcept = default;
169 explicit Buffer(
unsigned char* data,
std::
size_t size) :
175 throw std::invalid_argument{
"buffer size needs to be multiple of alignment"};
191 explicit Buffer(
unsigned char* data, std::size_t capacity, std::size_t committed) :
193 m_capacity(capacity),
194 m_written(committed),
195 m_committed(committed) {
197 throw std::invalid_argument{
"buffer capacity needs to be multiple of alignment"};
200 throw std::invalid_argument{
"buffer parameter 'committed' needs to be multiple of alignment"};
202 if (committed > capacity) {
203 throw std::invalid_argument{
"buffer parameter 'committed' can not be larger than capacity"};
220 explicit Buffer(std::unique_ptr<
unsigned char[]> data, std::size_t capacity, std::size_t committed) :
221 m_memory(
std::move(data)),
222 m_data(m_memory.get()),
223 m_capacity(capacity),
224 m_written(committed),
225 m_committed(committed) {
227 throw std::invalid_argument{
"buffer capacity needs to be multiple of alignment"};
230 throw std::invalid_argument{
"buffer parameter 'committed' needs to be multiple of alignment"};
232 if (committed > capacity) {
233 throw std::invalid_argument{
"buffer parameter 'committed' can not be larger than capacity"};
249 explicit Buffer(std::size_t capacity, auto_grow auto_grow = auto_grow::yes) :
250 m_memory(new unsigned char[calculate_capacity(capacity)]),
251 m_data(m_memory.get()),
252 m_capacity(calculate_capacity(capacity)),
253 m_auto_grow(auto_grow) {
257 Buffer(
const Buffer&) =
delete;
258 Buffer& operator=(
const Buffer&) =
delete;
261 Buffer(Buffer&& other) noexcept :
262 m_next_buffer(std::move(other.m_next_buffer)),
263 m_memory(std::move(other.m_memory)),
264 m_data(other.m_data),
265 m_capacity(other.m_capacity),
266 m_written(other.m_written),
267 m_committed(other.m_committed),
269 m_builder_count(other.m_builder_count),
271 m_auto_grow(other.m_auto_grow) {
272 other.m_data =
nullptr;
273 other.m_capacity = 0;
275 other.m_committed = 0;
277 other.m_builder_count = 0;
281 Buffer& operator=(Buffer&& other)
noexcept {
282 m_next_buffer = std::move(other.m_next_buffer);
283 m_memory = std::move(other.m_memory);
284 m_data = other.m_data;
285 m_capacity = other.m_capacity;
286 m_written = other.m_written;
287 m_committed = other.m_committed;
289 m_builder_count = other.m_builder_count;
291 m_auto_grow = other.m_auto_grow;
292 other.m_data =
nullptr;
293 other.m_capacity = 0;
295 other.m_committed = 0;
297 other.m_builder_count = 0;
302 ~Buffer() noexcept = default;
305 void increment_builder_count() noexcept {
309 void decrement_builder_count() noexcept {
310 assert(m_builder_count > 0);
314 uint8_t builder_count() const noexcept {
315 return m_builder_count;
324 unsigned char* data() const noexcept {
325 assert(m_data &&
"This must be a valid buffer");
333 std::size_t capacity() const noexcept {
341 std::size_t committed() const noexcept {
350 std::size_t written() const noexcept {
360 bool is_aligned() const noexcept {
361 assert(m_data &&
"This must be a valid buffer");
380 void grow(std::size_t size) {
381 assert(m_data &&
"This must be a valid buffer");
383 throw std::logic_error{
"Can't grow Buffer if it doesn't use internal memory management."};
385 size = calculate_capacity(size);
386 if (m_capacity < size) {
387 std::unique_ptr<unsigned char[]> memory{
new unsigned char[size]};
388 std::copy_n(m_memory.get(), m_capacity, memory.get());
390 swap(m_memory, memory);
391 m_data = m_memory.get();
402 bool has_nested_buffers() const noexcept {
403 return m_next_buffer !=
nullptr;
412 std::unique_ptr<Buffer> get_last_nested() {
413 assert(has_nested_buffers());
414 Buffer* buffer =
this;
415 while (buffer->m_next_buffer->has_nested_buffers()) {
416 buffer = buffer->m_next_buffer.get();
418 return std::move(buffer->m_next_buffer);
433 std::size_t commit() {
434 assert(m_data &&
"This must be a valid buffer");
435 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
436 assert(is_aligned());
438 const std::size_t offset = m_committed;
439 m_committed = m_written;
450 assert(m_data &&
"This must be a valid buffer");
451 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
452 m_written = m_committed;
464 std::size_t clear() {
465 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
466 const std::size_t num_used_bytes = m_committed;
469 return num_used_bytes;
482 template <
typename T>
483 T& get(
const std::size_t offset)
const {
484 assert(m_data &&
"This must be a valid buffer");
485 assert(offset %
alignof(T) == 0 &&
"Wrong alignment");
486 return *
reinterpret_cast<T*
>(&m_data[offset]);
515 unsigned char* reserve_space(
const std::size_t size) {
516 assert(m_data &&
"This must be a valid buffer");
518 if (m_written + size > m_capacity) {
519 if (!m_memory || m_auto_grow == auto_grow::no) {
520 throw osmium::buffer_is_full{};
522 if (m_auto_grow == auto_grow::internal && m_committed != 0) {
525 if (m_written + size > m_capacity) {
527 std::size_t new_capacity = m_capacity * 2;
528 while (m_written + size > new_capacity) {
534 unsigned char* reserved_space = &m_data[m_written];
536 return reserved_space;
554 template <
typename T>
555 T& add_item(
const T& item) {
556 assert(m_data &&
"This must be a valid buffer");
557 unsigned char* target = reserve_space(item.padded_size());
558 std::copy_n(
reinterpret_cast<const unsigned char*
>(&item), item.padded_size(), target);
559 return *
reinterpret_cast<T*
>(target);
573 void add_buffer(
const Buffer& buffer) {
574 assert(m_data &&
"This must be a valid buffer");
575 assert(buffer &&
"Buffer parameter must be a valid buffer");
576 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
577 unsigned char* target = reserve_space(buffer.committed());
578 std::copy_n(buffer.data(), buffer.committed(), target);
591 assert(m_data &&
"This must be a valid buffer");
592 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
601 template <
typename T>
608 template <
typename T>
615 using iterator = t_iterator<osmium::OSMEntity>;
621 using const_iterator = t_const_iterator<osmium::OSMEntity>;
623 template <
typename T>
624 ItemIteratorRange<T> select() {
625 return ItemIteratorRange<T>{m_data, m_data + m_committed};
628 template <
typename T>
629 ItemIteratorRange<const T> select()
const {
630 return ItemIteratorRange<const T>{m_data, m_data + m_committed};
641 template <
typename T>
642 t_iterator<T>
begin() {
643 assert(m_data &&
"This must be a valid buffer");
644 return t_iterator<T>(m_data, m_data + m_committed);
656 assert(m_data &&
"This must be a valid buffer");
657 return {m_data, m_data + m_committed};
669 template <
typename T>
670 t_iterator<T> get_iterator(std::size_t offset) {
671 assert(m_data &&
"This must be a valid buffer");
672 assert(offset %
alignof(T) == 0 &&
"Wrong alignment");
673 return {m_data + offset, m_data + m_committed};
685 iterator get_iterator(std::size_t offset) {
686 assert(m_data &&
"This must be a valid buffer");
687 assert(offset %
alignof(OSMEntity) == 0 &&
"Wrong alignment");
688 return {m_data + offset, m_data + m_committed};
699 template <
typename T>
700 t_iterator<T>
end() {
701 assert(m_data &&
"This must be a valid buffer");
702 return {m_data + m_committed, m_data + m_committed};
714 assert(m_data &&
"This must be a valid buffer");
715 return {m_data + m_committed, m_data + m_committed};
718 template <
typename T>
719 t_const_iterator<T> cbegin()
const {
720 assert(m_data &&
"This must be a valid buffer");
721 return {m_data, m_data + m_committed};
724 const_iterator cbegin()
const {
725 assert(m_data &&
"This must be a valid buffer");
726 return {m_data, m_data + m_committed};
729 template <
typename T>
730 t_const_iterator<T> get_iterator(std::size_t offset)
const {
731 assert(m_data &&
"This must be a valid buffer");
732 assert(offset %
alignof(T) == 0 &&
"Wrong alignment");
733 return {m_data + offset, m_data + m_committed};
736 const_iterator get_iterator(std::size_t offset)
const {
737 assert(m_data &&
"This must be a valid buffer");
738 assert(offset %
alignof(OSMEntity) == 0 &&
"Wrong alignment");
739 return {m_data + offset, m_data + m_committed};
742 template <
typename T>
743 t_const_iterator<T> cend()
const {
744 assert(m_data &&
"This must be a valid buffer");
745 return {m_data + m_committed, m_data + m_committed};
748 const_iterator cend()
const {
749 assert(m_data &&
"This must be a valid buffer");
750 return {m_data + m_committed, m_data + m_committed};
753 template <
typename T>
754 t_const_iterator<T>
begin()
const {
758 const_iterator
begin()
const {
762 template <
typename T>
763 t_const_iterator<T>
end()
const {
767 const_iterator
end()
const {
774 explicit operator bool() const noexcept {
775 return m_data !=
nullptr;
778 void swap(Buffer& other) {
781 swap(m_next_buffer, other.m_next_buffer);
782 swap(m_memory, other.m_memory);
783 swap(m_data, other.m_data);
784 swap(m_capacity, other.m_capacity);
785 swap(m_written, other.m_written);
786 swap(m_committed, other.m_committed);
787 swap(m_auto_grow, other.m_auto_grow);
807 template <
typename TCallbackClass>
808 void purge_removed(TCallbackClass* callback) {
809 assert(m_data &&
"This must be a valid buffer");
816 iterator it_write =
begin();
819 for (iterator it_read =
begin(); it_read !=
end(); it_read = next) {
820 next = std::next(it_read);
821 if (!it_read->removed()) {
822 if (it_read != it_write) {
823 assert(it_read.data() >= data());
824 assert(it_write.data() >= data());
825 const auto old_offset =
static_cast<std::size_t
>(it_read.data() - data());
826 const auto new_offset =
static_cast<std::size_t
>(it_write.data() - data());
827 callback->moving_in_buffer(old_offset, new_offset);
828 std::memmove(it_write.data(), it_read.data(), it_read->padded_size());
830 it_write.advance_once();
834 assert(it_write.data() >= data());
835 m_written =
static_cast<std::size_t
>(it_write.data() - data());
836 m_committed = m_written;
849 void purge_removed() {
850 assert(m_data &&
"This must be a valid buffer");
855 iterator it_write =
begin();
858 for (iterator it_read =
begin(); it_read !=
end(); it_read = next) {
859 next = std::next(it_read);
860 if (!it_read->removed()) {
861 if (it_read != it_write) {
862 assert(it_read.data() >= data());
863 assert(it_write.data() >= data());
864 std::memmove(it_write.data(), it_read.data(), it_read->padded_size());
866 it_write.advance_once();
870 assert(it_write.data() >= data());
871 m_written =
static_cast<std::size_t
>(it_write.data() - data());
872 m_committed = m_written;
877 inline void swap(Buffer& lhs, Buffer& rhs) {
888 inline bool operator==(
const Buffer& lhs,
const Buffer& rhs)
noexcept {
892 return lhs.data() == rhs.data() && lhs.capacity() == rhs.capacity() && lhs.committed() == rhs.committed();
895 inline bool operator!=(
const Buffer& lhs,
const Buffer& rhs)
noexcept {
896 return !(lhs == rhs);
Definition: item_iterator.hpp:59
#define OSMIUM_EXPORT
Definition: compatibility.hpp:54
InputIterator< Reader > end(Reader &)
Definition: reader_iterator.hpp:47
InputIterator< Reader > begin(Reader &reader)
Definition: reader_iterator.hpp:43
constexpr std::size_t padded_length(std::size_t length) noexcept
Definition: item.hpp:64
@ align_bytes
Definition: item.hpp:61
Namespace for everything in the Osmium library.
Definition: assembler.hpp:53
bool operator==(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:440
bool operator!=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:444
Definition: location.hpp:555