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};
122 std::function<void(Buffer&)> m_full;
124 static std::size_t calculate_capacity(std::size_t capacity) noexcept {
130 if (capacity < min_capacity) {
136 void grow_internal() {
137 assert(m_data &&
"This must be a valid buffer");
139 throw std::logic_error{
"Can't grow Buffer if it doesn't use internal memory management."};
142 std::unique_ptr<Buffer> old{
new Buffer{std::move(m_memory), m_capacity, m_committed}};
143 m_memory = std::unique_ptr<unsigned char[]>{
new unsigned char[m_capacity]};
144 m_data = m_memory.get();
146 m_written -= m_committed;
147 std::copy_n(old->data() + m_committed, m_written, m_data);
150 old->m_next_buffer = std::move(m_next_buffer);
151 m_next_buffer = std::move(old);
164 Buffer() noexcept = default;
176 explicit Buffer(
unsigned char* data,
std::
size_t size) :
182 throw std::invalid_argument{
"buffer size needs to be multiple of alignment"};
198 explicit Buffer(
unsigned char* data, std::size_t capacity, std::size_t committed) :
200 m_capacity(capacity),
201 m_written(committed),
202 m_committed(committed) {
204 throw std::invalid_argument{
"buffer capacity needs to be multiple of alignment"};
207 throw std::invalid_argument{
"buffer parameter 'committed' needs to be multiple of alignment"};
209 if (committed > capacity) {
210 throw std::invalid_argument{
"buffer parameter 'committed' can not be larger than capacity"};
227 explicit Buffer(std::unique_ptr<
unsigned char[]> data, std::size_t capacity, std::size_t committed) :
228 m_memory(
std::move(data)),
229 m_data(m_memory.get()),
230 m_capacity(capacity),
231 m_written(committed),
232 m_committed(committed) {
234 throw std::invalid_argument{
"buffer capacity needs to be multiple of alignment"};
237 throw std::invalid_argument{
"buffer parameter 'committed' needs to be multiple of alignment"};
239 if (committed > capacity) {
240 throw std::invalid_argument{
"buffer parameter 'committed' can not be larger than capacity"};
256 explicit Buffer(std::size_t capacity, auto_grow auto_grow = auto_grow::yes) :
257 m_memory(new unsigned char[calculate_capacity(capacity)]),
258 m_data(m_memory.get()),
259 m_capacity(calculate_capacity(capacity)),
260 m_auto_grow(auto_grow) {
264 Buffer(
const Buffer&) =
delete;
265 Buffer& operator=(
const Buffer&) =
delete;
268 Buffer(Buffer&& other) noexcept :
269 m_next_buffer(std::move(other.m_next_buffer)),
270 m_memory(std::move(other.m_memory)),
271 m_data(other.m_data),
272 m_capacity(other.m_capacity),
273 m_written(other.m_written),
274 m_committed(other.m_committed),
276 m_builder_count(other.m_builder_count),
278 m_auto_grow(other.m_auto_grow),
279 m_full(std::move(other.m_full)) {
280 other.m_data =
nullptr;
281 other.m_capacity = 0;
283 other.m_committed = 0;
285 other.m_builder_count = 0;
289 Buffer& operator=(Buffer&& other) noexcept {
290 m_next_buffer = std::move(other.m_next_buffer);
291 m_memory = std::move(other.m_memory);
292 m_data = other.m_data;
293 m_capacity = other.m_capacity;
294 m_written = other.m_written;
295 m_committed = other.m_committed;
297 m_builder_count = other.m_builder_count;
299 m_auto_grow = other.m_auto_grow;
300 m_full = std::move(other.m_full);
301 other.m_data =
nullptr;
302 other.m_capacity = 0;
304 other.m_committed = 0;
306 other.m_builder_count = 0;
311 ~Buffer() noexcept = default;
314 void increment_builder_count() noexcept {
318 void decrement_builder_count() noexcept {
319 assert(m_builder_count > 0);
323 uint8_t builder_count() const noexcept {
324 return m_builder_count;
333 unsigned char* data() const noexcept {
334 assert(m_data &&
"This must be a valid buffer");
342 std::size_t capacity() const noexcept {
350 std::size_t committed() const noexcept {
359 std::size_t written() const noexcept {
369 bool is_aligned() const noexcept {
370 assert(m_data &&
"This must be a valid buffer");
389 OSMIUM_DEPRECATED void set_full_callback(
const std::function<
void(Buffer&)>& full) {
390 assert(m_data &&
"This must be a valid buffer");
409 void grow(std::size_t size) {
410 assert(m_data &&
"This must be a valid buffer");
412 throw std::logic_error{
"Can't grow Buffer if it doesn't use internal memory management."};
414 size = calculate_capacity(size);
415 if (m_capacity < size) {
416 std::unique_ptr<unsigned char[]> memory{
new unsigned char[size]};
417 std::copy_n(m_memory.get(), m_capacity, memory.get());
419 swap(m_memory, memory);
420 m_data = m_memory.get();
431 bool has_nested_buffers() const noexcept {
432 return m_next_buffer !=
nullptr;
441 std::unique_ptr<Buffer> get_last_nested() {
442 assert(has_nested_buffers());
443 Buffer* buffer =
this;
444 while (buffer->m_next_buffer->has_nested_buffers()) {
445 buffer = buffer->m_next_buffer.get();
447 return std::move(buffer->m_next_buffer);
462 std::size_t commit() {
463 assert(m_data &&
"This must be a valid buffer");
464 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
465 assert(is_aligned());
467 const std::size_t offset = m_committed;
468 m_committed = m_written;
479 assert(m_data &&
"This must be a valid buffer");
480 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
481 m_written = m_committed;
493 std::size_t clear() {
494 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
495 const std::size_t num_used_bytes = m_committed;
498 return num_used_bytes;
511 template <
typename T>
512 T& get(
const std::size_t offset)
const {
513 assert(m_data &&
"This must be a valid buffer");
514 assert(offset %
alignof(T) == 0 &&
"Wrong alignment");
515 return *
reinterpret_cast<T*
>(&m_data[offset]);
551 unsigned char* reserve_space(
const std::size_t size) {
552 assert(m_data &&
"This must be a valid buffer");
554 if (m_written + size > m_capacity && m_full) {
558 if (m_written + size > m_capacity) {
559 if (!m_memory || m_auto_grow == auto_grow::no) {
560 throw osmium::buffer_is_full{};
562 if (m_auto_grow == auto_grow::internal && m_committed != 0) {
565 if (m_written + size > m_capacity) {
567 std::size_t new_capacity = m_capacity * 2;
568 while (m_written + size > new_capacity) {
574 unsigned char* reserved_space = &m_data[m_written];
576 return reserved_space;
594 template <
typename T>
595 T& add_item(
const T& item) {
596 assert(m_data &&
"This must be a valid buffer");
597 unsigned char* target = reserve_space(item.padded_size());
598 std::copy_n(
reinterpret_cast<const unsigned char*
>(&item), item.padded_size(), target);
599 return *
reinterpret_cast<T*
>(target);
613 void add_buffer(
const Buffer& buffer) {
614 assert(m_data &&
"This must be a valid buffer");
615 assert(buffer &&
"Buffer parameter must be a valid buffer");
616 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
617 unsigned char* target = reserve_space(buffer.committed());
618 std::copy_n(buffer.data(), buffer.committed(), target);
631 assert(m_data &&
"This must be a valid buffer");
632 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
641 template <
typename T>
648 template <
typename T>
655 using iterator = t_iterator<osmium::OSMEntity>;
661 using const_iterator = t_const_iterator<osmium::OSMEntity>;
663 template <
typename T>
664 ItemIteratorRange<T> select() {
665 return ItemIteratorRange<T>{m_data, m_data + m_committed};
668 template <
typename T>
669 ItemIteratorRange<const T> select()
const {
670 return ItemIteratorRange<const T>{m_data, m_data + m_committed};
681 template <
typename T>
682 t_iterator<T>
begin() {
683 assert(m_data &&
"This must be a valid buffer");
684 return t_iterator<T>(m_data, m_data + m_committed);
696 assert(m_data &&
"This must be a valid buffer");
697 return {m_data, m_data + m_committed};
709 template <
typename T>
710 t_iterator<T> get_iterator(std::size_t offset) {
711 assert(m_data &&
"This must be a valid buffer");
712 assert(offset %
alignof(T) == 0 &&
"Wrong alignment");
713 return {m_data + offset, m_data + m_committed};
725 iterator get_iterator(std::size_t offset) {
726 assert(m_data &&
"This must be a valid buffer");
727 assert(offset %
alignof(OSMEntity) == 0 &&
"Wrong alignment");
728 return {m_data + offset, m_data + m_committed};
739 template <
typename T>
740 t_iterator<T>
end() {
741 assert(m_data &&
"This must be a valid buffer");
742 return {m_data + m_committed, m_data + m_committed};
754 assert(m_data &&
"This must be a valid buffer");
755 return {m_data + m_committed, m_data + m_committed};
758 template <
typename T>
759 t_const_iterator<T> cbegin()
const {
760 assert(m_data &&
"This must be a valid buffer");
761 return {m_data, m_data + m_committed};
764 const_iterator cbegin()
const {
765 assert(m_data &&
"This must be a valid buffer");
766 return {m_data, m_data + m_committed};
769 template <
typename T>
770 t_const_iterator<T> get_iterator(std::size_t offset)
const {
771 assert(m_data &&
"This must be a valid buffer");
772 assert(offset %
alignof(T) == 0 &&
"Wrong alignment");
773 return {m_data + offset, m_data + m_committed};
776 const_iterator get_iterator(std::size_t offset)
const {
777 assert(m_data &&
"This must be a valid buffer");
778 assert(offset %
alignof(OSMEntity) == 0 &&
"Wrong alignment");
779 return {m_data + offset, m_data + m_committed};
782 template <
typename T>
783 t_const_iterator<T> cend()
const {
784 assert(m_data &&
"This must be a valid buffer");
785 return {m_data + m_committed, m_data + m_committed};
788 const_iterator cend()
const {
789 assert(m_data &&
"This must be a valid buffer");
790 return {m_data + m_committed, m_data + m_committed};
793 template <
typename T>
794 t_const_iterator<T>
begin()
const {
798 const_iterator
begin()
const {
802 template <
typename T>
803 t_const_iterator<T>
end()
const {
807 const_iterator
end()
const {
814 explicit operator bool() const noexcept {
815 return m_data !=
nullptr;
818 void swap(Buffer& other) {
821 swap(m_next_buffer, other.m_next_buffer);
822 swap(m_memory, other.m_memory);
823 swap(m_data, other.m_data);
824 swap(m_capacity, other.m_capacity);
825 swap(m_written, other.m_written);
826 swap(m_committed, other.m_committed);
827 swap(m_auto_grow, other.m_auto_grow);
828 swap(m_full, other.m_full);
848 template <
typename TCallbackClass>
849 void purge_removed(TCallbackClass* callback) {
850 assert(m_data &&
"This must be a valid buffer");
857 iterator it_write =
begin();
860 for (iterator it_read =
begin(); it_read !=
end(); it_read = next) {
861 next = std::next(it_read);
862 if (!it_read->removed()) {
863 if (it_read != it_write) {
864 assert(it_read.data() >= data());
865 assert(it_write.data() >= data());
866 const auto old_offset =
static_cast<std::size_t
>(it_read.data() - data());
867 const auto new_offset =
static_cast<std::size_t
>(it_write.data() - data());
868 callback->moving_in_buffer(old_offset, new_offset);
869 std::memmove(it_write.data(), it_read.data(), it_read->padded_size());
871 it_write.advance_once();
875 assert(it_write.data() >= data());
876 m_written =
static_cast<std::size_t
>(it_write.data() - data());
877 m_committed = m_written;
890 void purge_removed() {
891 assert(m_data &&
"This must be a valid buffer");
896 iterator it_write =
begin();
899 for (iterator it_read =
begin(); it_read !=
end(); it_read = next) {
900 next = std::next(it_read);
901 if (!it_read->removed()) {
902 if (it_read != it_write) {
903 assert(it_read.data() >= data());
904 assert(it_write.data() >= data());
905 std::memmove(it_write.data(), it_read.data(), it_read->padded_size());
907 it_write.advance_once();
911 assert(it_write.data() >= data());
912 m_written =
static_cast<std::size_t
>(it_write.data() - data());
913 m_committed = m_written;
918 inline void swap(Buffer& lhs, Buffer& rhs) {
929 inline bool operator==(
const Buffer& lhs,
const Buffer& rhs) noexcept {
933 return lhs.data() == rhs.data() && lhs.capacity() == rhs.capacity() && lhs.committed() == rhs.committed();
936 inline bool operator!=(
const Buffer& lhs,
const Buffer& rhs) noexcept {
937 return !(lhs == rhs);
Definition: item_iterator.hpp:59
#define OSMIUM_DEPRECATED
Definition: compatibility.hpp:51
#define OSMIUM_EXPORT
Definition: compatibility.hpp:63
InputIterator< Reader > begin(Reader &reader)
Definition: reader_iterator.hpp:43
InputIterator< Reader > end(Reader &)
Definition: reader_iterator.hpp:47
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