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"} {
102 using value_type = Item;
104 enum class auto_grow {
112 std::unique_ptr<Buffer> m_next_buffer;
113 std::unique_ptr<unsigned char[]> m_memory{};
114 unsigned char* m_data =
nullptr;
115 std::size_t m_capacity = 0;
116 std::size_t m_written = 0;
117 std::size_t m_committed = 0;
119 uint8_t m_builder_count = 0;
121 auto_grow m_auto_grow{auto_grow::no};
123 static std::size_t calculate_capacity(std::size_t capacity)
noexcept {
129 if (capacity < min_capacity) {
135 void grow_internal() {
136 assert(m_data &&
"This must be a valid buffer");
138 throw std::logic_error{
"Can't grow Buffer if it doesn't use internal memory management."};
141 std::unique_ptr<Buffer> old{
new Buffer{std::move(m_memory), m_capacity, m_committed}};
142 m_memory = std::unique_ptr<unsigned char[]>{
new unsigned char[m_capacity]};
143 m_data = m_memory.get();
145 m_written -= m_committed;
146 std::copy_n(old->data() + m_committed, m_written, m_data);
149 old->m_next_buffer = std::move(m_next_buffer);
150 m_next_buffer = std::move(old);
163 Buffer() noexcept = default;
175 explicit Buffer(
unsigned char* data,
std::
size_t size) :
180 if (size % align_bytes != 0) {
181 throw std::invalid_argument{
"buffer size needs to be multiple of alignment"};
197 explicit Buffer(
unsigned char* data, std::size_t capacity, std::size_t committed) :
199 m_capacity(capacity),
200 m_written(committed),
201 m_committed(committed) {
202 if (capacity % align_bytes != 0) {
203 throw std::invalid_argument{
"buffer capacity needs to be multiple of alignment"};
205 if (committed % align_bytes != 0) {
206 throw std::invalid_argument{
"buffer parameter 'committed' needs to be multiple of alignment"};
208 if (committed > capacity) {
209 throw std::invalid_argument{
"buffer parameter 'committed' can not be larger than capacity"};
226 explicit Buffer(std::unique_ptr<
unsigned char[]> data, std::size_t capacity, std::size_t committed) :
227 m_memory(
std::move(data)),
228 m_data(m_memory.get()),
229 m_capacity(capacity),
230 m_written(committed),
231 m_committed(committed) {
232 if (capacity % align_bytes != 0) {
233 throw std::invalid_argument{
"buffer capacity needs to be multiple of alignment"};
235 if (committed % align_bytes != 0) {
236 throw std::invalid_argument{
"buffer parameter 'committed' needs to be multiple of alignment"};
238 if (committed > capacity) {
239 throw std::invalid_argument{
"buffer parameter 'committed' can not be larger than capacity"};
255 explicit Buffer(std::size_t capacity, auto_grow auto_grow = auto_grow::yes) :
256 m_memory(new unsigned char[calculate_capacity(capacity)]),
257 m_data(m_memory.get()),
258 m_capacity(calculate_capacity(capacity)),
259 m_auto_grow(auto_grow) {
263 Buffer(
const Buffer&) =
delete;
264 Buffer& operator=(
const Buffer&) =
delete;
267 Buffer(Buffer&& other) noexcept :
268 m_next_buffer(std::move(other.m_next_buffer)),
269 m_memory(std::move(other.m_memory)),
270 m_data(other.m_data),
271 m_capacity(other.m_capacity),
272 m_written(other.m_written),
273 m_committed(other.m_committed),
275 m_builder_count(other.m_builder_count),
277 m_auto_grow(other.m_auto_grow) {
278 other.m_data =
nullptr;
279 other.m_capacity = 0;
281 other.m_committed = 0;
283 other.m_builder_count = 0;
287 Buffer& operator=(Buffer&& other)
noexcept {
288 m_next_buffer = std::move(other.m_next_buffer);
289 m_memory = std::move(other.m_memory);
290 m_data = other.m_data;
291 m_capacity = other.m_capacity;
292 m_written = other.m_written;
293 m_committed = other.m_committed;
295 m_builder_count = other.m_builder_count;
297 m_auto_grow = other.m_auto_grow;
298 other.m_data =
nullptr;
299 other.m_capacity = 0;
301 other.m_committed = 0;
303 other.m_builder_count = 0;
308 ~Buffer() noexcept = default;
311 void increment_builder_count() noexcept {
315 void decrement_builder_count() noexcept {
316 assert(m_builder_count > 0);
320 uint8_t builder_count() const noexcept {
321 return m_builder_count;
330 unsigned char* data() const noexcept {
331 assert(m_data &&
"This must be a valid buffer");
339 std::size_t capacity() const noexcept {
347 std::size_t committed() const noexcept {
356 std::size_t written() const noexcept {
366 bool is_aligned() const noexcept {
367 assert(m_data &&
"This must be a valid buffer");
368 return (m_written % align_bytes == 0) && (m_committed %
align_bytes == 0);
386 void grow(std::size_t size) {
387 assert(m_data &&
"This must be a valid buffer");
389 throw std::logic_error{
"Can't grow Buffer if it doesn't use internal memory management."};
391 size = calculate_capacity(size);
392 if (m_capacity < size) {
393 std::unique_ptr<unsigned char[]> memory{
new unsigned char[size]};
394 std::copy_n(m_memory.get(), m_capacity, memory.get());
396 swap(m_memory, memory);
397 m_data = m_memory.get();
408 bool has_nested_buffers() const noexcept {
409 return m_next_buffer !=
nullptr;
418 std::unique_ptr<Buffer> get_last_nested() {
419 assert(has_nested_buffers());
420 Buffer* buffer =
this;
421 while (buffer->m_next_buffer->has_nested_buffers()) {
422 buffer = buffer->m_next_buffer.get();
424 return std::move(buffer->m_next_buffer);
439 std::size_t commit() {
440 assert(m_data &&
"This must be a valid buffer");
441 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
442 assert(is_aligned());
444 const std::size_t offset = m_committed;
445 m_committed = m_written;
456 assert(m_data &&
"This must be a valid buffer");
457 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
458 m_written = m_committed;
470 std::size_t clear() {
471 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
472 const std::size_t num_used_bytes = m_committed;
475 return num_used_bytes;
488 template <
typename T>
489 T& get(
const std::size_t offset)
const {
490 assert(m_data &&
"This must be a valid buffer");
491 assert(offset %
alignof(T) == 0 &&
"Wrong alignment");
492 return *
reinterpret_cast<T*
>(&m_data[offset]);
528 unsigned char* reserve_space(
const std::size_t size) {
529 assert(m_data &&
"This must be a valid buffer");
531 if (m_written + size > m_capacity) {
532 if (!m_memory || m_auto_grow == auto_grow::no) {
533 throw osmium::buffer_is_full{};
535 if (m_auto_grow == auto_grow::internal && m_committed != 0) {
538 if (m_written + size > m_capacity) {
540 std::size_t new_capacity = m_capacity * 2;
541 while (m_written + size > new_capacity) {
547 unsigned char* reserved_space = &m_data[m_written];
549 return reserved_space;
567 template <
typename T>
568 T& add_item(
const T& item) {
569 assert(m_data &&
"This must be a valid buffer");
570 unsigned char* target = reserve_space(item.padded_size());
571 std::copy_n(
reinterpret_cast<const unsigned char*
>(&item), item.padded_size(), target);
572 return *
reinterpret_cast<T*
>(target);
586 void add_buffer(
const Buffer& buffer) {
587 assert(m_data &&
"This must be a valid buffer");
588 assert(buffer &&
"Buffer parameter must be a valid buffer");
589 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
590 unsigned char* target = reserve_space(buffer.committed());
591 std::copy_n(buffer.data(), buffer.committed(), target);
604 assert(m_data &&
"This must be a valid buffer");
605 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
614 template <
typename T>
621 template <
typename T>
628 using iterator = t_iterator<osmium::OSMEntity>;
634 using const_iterator = t_const_iterator<osmium::OSMEntity>;
636 template <
typename T>
637 ItemIteratorRange<T> select() {
638 return ItemIteratorRange<T>{m_data, m_data + m_committed};
641 template <
typename T>
642 ItemIteratorRange<const T> select()
const {
643 return ItemIteratorRange<const T>{m_data, m_data + m_committed};
654 template <
typename T>
655 t_iterator<T>
begin() {
656 assert(m_data &&
"This must be a valid buffer");
657 return t_iterator<T>(m_data, m_data + m_committed);
669 assert(m_data &&
"This must be a valid buffer");
670 return {m_data, m_data + m_committed};
682 template <
typename T>
683 t_iterator<T> get_iterator(std::size_t offset) {
684 assert(m_data &&
"This must be a valid buffer");
685 assert(offset %
alignof(T) == 0 &&
"Wrong alignment");
686 return {m_data + offset, m_data + m_committed};
698 iterator get_iterator(std::size_t offset) {
699 assert(m_data &&
"This must be a valid buffer");
700 assert(offset %
alignof(OSMEntity) == 0 &&
"Wrong alignment");
701 return {m_data + offset, m_data + m_committed};
712 template <
typename T>
713 t_iterator<T>
end() {
714 assert(m_data &&
"This must be a valid buffer");
715 return {m_data + m_committed, m_data + m_committed};
727 assert(m_data &&
"This must be a valid buffer");
728 return {m_data + m_committed, m_data + m_committed};
731 template <
typename T>
732 t_const_iterator<T> cbegin()
const {
733 assert(m_data &&
"This must be a valid buffer");
734 return {m_data, m_data + m_committed};
737 const_iterator cbegin()
const {
738 assert(m_data &&
"This must be a valid buffer");
739 return {m_data, m_data + m_committed};
742 template <
typename T>
743 t_const_iterator<T> get_iterator(std::size_t offset)
const {
744 assert(m_data &&
"This must be a valid buffer");
745 assert(offset %
alignof(T) == 0 &&
"Wrong alignment");
746 return {m_data + offset, m_data + m_committed};
749 const_iterator get_iterator(std::size_t offset)
const {
750 assert(m_data &&
"This must be a valid buffer");
751 assert(offset %
alignof(OSMEntity) == 0 &&
"Wrong alignment");
752 return {m_data + offset, m_data + m_committed};
755 template <
typename T>
756 t_const_iterator<T> cend()
const {
757 assert(m_data &&
"This must be a valid buffer");
758 return {m_data + m_committed, m_data + m_committed};
761 const_iterator cend()
const {
762 assert(m_data &&
"This must be a valid buffer");
763 return {m_data + m_committed, m_data + m_committed};
766 template <
typename T>
767 t_const_iterator<T>
begin()
const {
771 const_iterator
begin()
const {
775 template <
typename T>
776 t_const_iterator<T>
end()
const {
780 const_iterator
end()
const {
787 explicit operator bool() const noexcept {
788 return m_data !=
nullptr;
791 void swap(Buffer& other) {
794 swap(m_next_buffer, other.m_next_buffer);
795 swap(m_memory, other.m_memory);
796 swap(m_data, other.m_data);
797 swap(m_capacity, other.m_capacity);
798 swap(m_written, other.m_written);
799 swap(m_committed, other.m_committed);
800 swap(m_auto_grow, other.m_auto_grow);
820 template <
typename TCallbackClass>
821 void purge_removed(TCallbackClass* callback) {
822 assert(m_data &&
"This must be a valid buffer");
829 iterator it_write =
begin();
832 for (iterator it_read =
begin(); it_read !=
end(); it_read = next) {
833 next = std::next(it_read);
834 if (!it_read->removed()) {
835 if (it_read != it_write) {
836 assert(it_read.data() >= data());
837 assert(it_write.data() >= data());
838 const auto old_offset =
static_cast<std::size_t
>(it_read.data() - data());
839 const auto new_offset =
static_cast<std::size_t
>(it_write.data() - data());
840 callback->moving_in_buffer(old_offset, new_offset);
841 std::memmove(it_write.data(), it_read.data(), it_read->padded_size());
843 it_write.advance_once();
847 assert(it_write.data() >= data());
848 m_written =
static_cast<std::size_t
>(it_write.data() - data());
849 m_committed = m_written;
862 void purge_removed() {
863 assert(m_data &&
"This must be a valid buffer");
868 iterator it_write =
begin();
871 for (iterator it_read =
begin(); it_read !=
end(); it_read = next) {
872 next = std::next(it_read);
873 if (!it_read->removed()) {
874 if (it_read != it_write) {
875 assert(it_read.data() >= data());
876 assert(it_write.data() >= data());
877 std::memmove(it_write.data(), it_read.data(), it_read->padded_size());
879 it_write.advance_once();
883 assert(it_write.data() >= data());
884 m_written =
static_cast<std::size_t
>(it_write.data() - data());
885 m_committed = m_written;
890 inline void swap(Buffer& lhs, Buffer& rhs) {
901 inline bool operator==(
const Buffer& lhs,
const Buffer& rhs)
noexcept {
905 return lhs.data() == rhs.data() && lhs.capacity() == rhs.capacity() && lhs.committed() == rhs.committed();
908 inline bool operator!=(
const Buffer& lhs,
const Buffer& rhs)
noexcept {
909 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:442
bool operator!=(const Changeset &lhs, const Changeset &rhs)
Definition: changeset.hpp:446
Definition: location.hpp:555