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);
178 explicit Buffer(
unsigned char* data, std::size_t size) :
185 throw std::invalid_argument{
"buffer size needs to be multiple of alignment"};
201 explicit Buffer(
unsigned char* data, std::size_t capacity, std::size_t committed) :
204 m_capacity(capacity),
205 m_written(committed),
206 m_committed(committed) {
208 throw std::invalid_argument{
"buffer capacity needs to be multiple of alignment"};
211 throw std::invalid_argument{
"buffer parameter 'committed' needs to be multiple of alignment"};
213 if (committed > capacity) {
214 throw std::invalid_argument{
"buffer parameter 'committed' can not be larger than capacity"};
231 explicit Buffer(std::unique_ptr<
unsigned char[]> data, std::size_t capacity, std::size_t committed) :
233 m_memory(
std::move(data)),
234 m_data(m_memory.get()),
235 m_capacity(capacity),
236 m_written(committed),
237 m_committed(committed) {
239 throw std::invalid_argument{
"buffer capacity needs to be multiple of alignment"};
242 throw std::invalid_argument{
"buffer parameter 'committed' needs to be multiple of alignment"};
244 if (committed > capacity) {
245 throw std::invalid_argument{
"buffer parameter 'committed' can not be larger than capacity"};
261 explicit Buffer(std::size_t capacity, auto_grow auto_grow = auto_grow::yes) :
263 m_memory(new unsigned char[calculate_capacity(capacity)]),
264 m_data(m_memory.get()),
265 m_capacity(calculate_capacity(capacity)),
266 m_auto_grow(auto_grow) {
270 Buffer(
const Buffer&) =
delete;
271 Buffer& operator=(
const Buffer&) =
delete;
274 Buffer(Buffer&& other) noexcept :
275 m_next_buffer(std::move(other.m_next_buffer)),
276 m_memory(std::move(other.m_memory)),
277 m_data(other.m_data),
278 m_capacity(other.m_capacity),
279 m_written(other.m_written),
280 m_committed(other.m_committed),
282 m_builder_count(other.m_builder_count),
284 m_auto_grow(other.m_auto_grow),
285 m_full(std::move(other.m_full)) {
286 other.m_data =
nullptr;
287 other.m_capacity = 0;
289 other.m_committed = 0;
291 other.m_builder_count = 0;
295 Buffer& operator=(Buffer&& other) noexcept {
296 m_next_buffer = std::move(other.m_next_buffer);
297 m_memory = std::move(other.m_memory);
298 m_data = other.m_data;
299 m_capacity = other.m_capacity;
300 m_written = other.m_written;
301 m_committed = other.m_committed;
303 m_builder_count = other.m_builder_count;
305 m_auto_grow = other.m_auto_grow;
306 m_full = std::move(other.m_full);
307 other.m_data =
nullptr;
308 other.m_capacity = 0;
310 other.m_committed = 0;
312 other.m_builder_count = 0;
317 ~Buffer() noexcept = default;
320 void increment_builder_count() noexcept {
324 void decrement_builder_count() noexcept {
325 assert(m_builder_count > 0);
329 uint8_t builder_count() const noexcept {
330 return m_builder_count;
339 unsigned char* data() const noexcept {
340 assert(m_data &&
"This must be a valid buffer");
348 std::size_t capacity() const noexcept {
356 std::size_t committed() const noexcept {
365 std::size_t written() const noexcept {
375 bool is_aligned() const noexcept {
376 assert(m_data &&
"This must be a valid buffer");
395 OSMIUM_DEPRECATED void set_full_callback(
const std::function<
void(Buffer&)>& full) {
396 assert(m_data &&
"This must be a valid buffer");
415 void grow(std::size_t size) {
416 assert(m_data &&
"This must be a valid buffer");
418 throw std::logic_error{
"Can't grow Buffer if it doesn't use internal memory management."};
420 size = calculate_capacity(size);
421 if (m_capacity < size) {
422 std::unique_ptr<unsigned char[]> memory{
new unsigned char[size]};
423 std::copy_n(m_memory.get(), m_capacity, memory.get());
425 swap(m_memory, memory);
426 m_data = m_memory.get();
437 bool has_nested_buffers() const noexcept {
438 return m_next_buffer !=
nullptr;
447 std::unique_ptr<Buffer> get_last_nested() {
448 assert(has_nested_buffers());
449 Buffer* buffer =
this;
450 while (buffer->m_next_buffer->has_nested_buffers()) {
451 buffer = buffer->m_next_buffer.get();
453 return std::move(buffer->m_next_buffer);
468 std::size_t commit() {
469 assert(m_data &&
"This must be a valid buffer");
470 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
471 assert(is_aligned());
473 const std::size_t offset = m_committed;
474 m_committed = m_written;
485 assert(m_data &&
"This must be a valid buffer");
486 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
487 m_written = m_committed;
499 std::size_t clear() {
500 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
501 const std::size_t num_used_bytes = m_committed;
504 return num_used_bytes;
517 template <
typename T>
518 T& get(
const std::size_t offset)
const {
519 assert(m_data &&
"This must be a valid buffer");
520 assert(offset %
alignof(T) == 0 &&
"Wrong alignment");
521 return *
reinterpret_cast<T*
>(&m_data[offset]);
557 unsigned char* reserve_space(
const std::size_t size) {
558 assert(m_data &&
"This must be a valid buffer");
560 if (m_written + size > m_capacity && m_full) {
564 if (m_written + size > m_capacity) {
565 if (!m_memory || m_auto_grow == auto_grow::no) {
566 throw osmium::buffer_is_full{};
568 if (m_auto_grow == auto_grow::internal && m_committed != 0) {
571 if (m_written + size > m_capacity) {
573 std::size_t new_capacity = m_capacity * 2;
574 while (m_written + size > new_capacity) {
580 unsigned char* reserved_space = &m_data[m_written];
582 return reserved_space;
600 template <
typename T>
601 T& add_item(
const T& item) {
602 assert(m_data &&
"This must be a valid buffer");
603 unsigned char* target = reserve_space(item.padded_size());
604 std::copy_n(
reinterpret_cast<const unsigned char*
>(&item), item.padded_size(), target);
605 return *
reinterpret_cast<T*
>(target);
619 void add_buffer(
const Buffer& buffer) {
620 assert(m_data &&
"This must be a valid buffer");
621 assert(buffer &&
"Buffer parameter must be a valid buffer");
622 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
623 unsigned char* target = reserve_space(buffer.committed());
624 std::copy_n(buffer.data(), buffer.committed(), target);
637 assert(m_data &&
"This must be a valid buffer");
638 assert(m_builder_count == 0 &&
"Make sure there are no Builder objects still in scope");
647 template <
typename T>
654 template <
typename T>
661 using iterator = t_iterator<osmium::OSMEntity>;
667 using const_iterator = t_const_iterator<osmium::OSMEntity>;
669 template <
typename T>
670 ItemIteratorRange<T> select() {
671 return ItemIteratorRange<T>{m_data, m_data + m_committed};
674 template <
typename T>
675 ItemIteratorRange<const T> select()
const {
676 return ItemIteratorRange<const T>{m_data, m_data + m_committed};
687 template <
typename T>
688 t_iterator<T>
begin() {
689 assert(m_data &&
"This must be a valid buffer");
690 return t_iterator<T>(m_data, m_data + m_committed);
702 assert(m_data &&
"This must be a valid buffer");
703 return {m_data, m_data + m_committed};
715 template <
typename T>
716 t_iterator<T> get_iterator(std::size_t offset) {
717 assert(m_data &&
"This must be a valid buffer");
718 assert(offset %
alignof(T) == 0 &&
"Wrong alignment");
719 return {m_data + offset, m_data + m_committed};
731 iterator get_iterator(std::size_t offset) {
732 assert(m_data &&
"This must be a valid buffer");
733 assert(offset %
alignof(OSMEntity) == 0 &&
"Wrong alignment");
734 return {m_data + offset, m_data + m_committed};
745 template <
typename T>
746 t_iterator<T>
end() {
747 assert(m_data &&
"This must be a valid buffer");
748 return {m_data + m_committed, m_data + m_committed};
760 assert(m_data &&
"This must be a valid buffer");
761 return {m_data + m_committed, m_data + m_committed};
764 template <
typename T>
765 t_const_iterator<T> cbegin()
const {
766 assert(m_data &&
"This must be a valid buffer");
767 return {m_data, m_data + m_committed};
770 const_iterator cbegin()
const {
771 assert(m_data &&
"This must be a valid buffer");
772 return {m_data, m_data + m_committed};
775 template <
typename T>
776 t_const_iterator<T> get_iterator(std::size_t offset)
const {
777 assert(m_data &&
"This must be a valid buffer");
778 assert(offset %
alignof(T) == 0 &&
"Wrong alignment");
779 return {m_data + offset, m_data + m_committed};
782 const_iterator get_iterator(std::size_t offset)
const {
783 assert(m_data &&
"This must be a valid buffer");
784 assert(offset %
alignof(OSMEntity) == 0 &&
"Wrong alignment");
785 return {m_data + offset, m_data + m_committed};
788 template <
typename T>
789 t_const_iterator<T> cend()
const {
790 assert(m_data &&
"This must be a valid buffer");
791 return {m_data + m_committed, m_data + m_committed};
794 const_iterator cend()
const {
795 assert(m_data &&
"This must be a valid buffer");
796 return {m_data + m_committed, m_data + m_committed};
799 template <
typename T>
800 t_const_iterator<T>
begin()
const {
804 const_iterator
begin()
const {
808 template <
typename T>
809 t_const_iterator<T>
end()
const {
813 const_iterator
end()
const {
820 explicit operator bool() const noexcept {
821 return m_data !=
nullptr;
824 void swap(Buffer& other) {
827 swap(m_next_buffer, other.m_next_buffer);
828 swap(m_memory, other.m_memory);
829 swap(m_data, other.m_data);
830 swap(m_capacity, other.m_capacity);
831 swap(m_written, other.m_written);
832 swap(m_committed, other.m_committed);
833 swap(m_auto_grow, other.m_auto_grow);
834 swap(m_full, other.m_full);
854 template <
typename TCallbackClass>
855 void purge_removed(TCallbackClass* callback) {
856 assert(m_data &&
"This must be a valid buffer");
863 iterator it_write =
begin();
866 for (iterator it_read =
begin(); it_read !=
end(); it_read = next) {
867 next = std::next(it_read);
868 if (!it_read->removed()) {
869 if (it_read != it_write) {
870 assert(it_read.data() >= data());
871 assert(it_write.data() >= data());
872 const auto old_offset =
static_cast<std::size_t
>(it_read.data() - data());
873 const auto new_offset =
static_cast<std::size_t
>(it_write.data() - data());
874 callback->moving_in_buffer(old_offset, new_offset);
875 std::memmove(it_write.data(), it_read.data(), it_read->padded_size());
877 it_write.advance_once();
881 assert(it_write.data() >= data());
882 m_written =
static_cast<std::size_t
>(it_write.data() - data());
883 m_committed = m_written;
896 void purge_removed() {
897 assert(m_data &&
"This must be a valid buffer");
902 iterator it_write =
begin();
905 for (iterator it_read =
begin(); it_read !=
end(); it_read = next) {
906 next = std::next(it_read);
907 if (!it_read->removed()) {
908 if (it_read != it_write) {
909 assert(it_read.data() >= data());
910 assert(it_write.data() >= data());
911 std::memmove(it_write.data(), it_read.data(), it_read->padded_size());
913 it_write.advance_once();
917 assert(it_write.data() >= data());
918 m_written =
static_cast<std::size_t
>(it_write.data() - data());
919 m_committed = m_written;
924 inline void swap(Buffer& lhs, Buffer& rhs) {
935 inline bool operator==(
const Buffer& lhs,
const Buffer& rhs) noexcept {
939 return lhs.data() == rhs.data() && lhs.capacity() == rhs.capacity() && lhs.committed() == rhs.committed();
942 inline bool operator!=(
const Buffer& lhs,
const Buffer& rhs) noexcept {
943 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:551