From 31d22967f89a3c63ba2be5a4c9c73770bef69449 Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Sat, 16 Jun 2018 17:30:07 -0500 Subject: [PATCH 1/4] Linked list --- library/include/BitArray.h | 299 ++++++++++++++++++++++++++++++++++++- library/include/DataDefs.h | 1 + library/xml | 2 +- 3 files changed, 300 insertions(+), 2 deletions(-) diff --git a/library/include/BitArray.h b/library/include/BitArray.h index 1469091d1..42f6fd431 100644 --- a/library/include/BitArray.h +++ b/library/include/BitArray.h @@ -25,12 +25,14 @@ distribution. #pragma once #include "Pragma.h" #include "Export.h" +#include "Error.h" #include #include #include #include #include -//#include +#include +#include namespace DFHack { template @@ -232,4 +234,299 @@ namespace DFHack m_data[idx] = item; } }; + + template + struct DfLinkedList + { + class iterator; + class const_iterator; + + class proxy + { + L *root; + L *prev; + friend class iterator; + proxy(L *root, L *prev) : root(root), prev(prev) + { + CHECK_NULL_POINTER(root); + CHECK_NULL_POINTER(prev); + } + + public: + operator I * & () + { + CHECK_NULL_POINTER(prev->next); + return prev->next->item; + } + operator I *() const + { + CHECK_NULL_POINTER(prev->next); + return prev->next->item; + } + I *operator->() const + { + CHECK_NULL_POINTER(prev->next); + return prev->next->item; + } + proxy & operator=(I *item) + { + if (!prev->next) + { + prev->next = df::allocate(); + if (prev != root) + prev->next->prev = prev; + } + prev->next->item = item; + return *this; + } + }; + + class iterator + { + L *root; + L *prev; + bool next; + friend struct DfLinkedList; + friend class const_iterator; + iterator() : root(nullptr), prev(nullptr), next(false) {} + public: + using difference_type = void; + using value_type = I *; + using pointer = I **; + using reference = proxy; + using iterator_category = std::output_iterator_tag; + + iterator(L *root, L *prev, bool next = false) : root(root), prev(prev), next(next) {} + iterator(const iterator & other) : root(other.root), prev(other.prev), next(other.next) {} + + iterator & operator++() + { + CHECK_NULL_POINTER(root); + + CHECK_NULL_POINTER(prev); + CHECK_NULL_POINTER(prev->next); + + if (!next && !prev->next->next) + { + next = true; + return *this; + } + + CHECK_NULL_POINTER(prev->next->next); + + prev = prev->next; + return *this; + } + iterator & operator--() + { + CHECK_NULL_POINTER(root); + + if (next) + { + next = false; + return *this; + } + + CHECK_NULL_POINTER(prev); + if (prev == root) + { + throw DFHack::Error::NullPointer("prev->prev", DFHACK_FUNCTION_SIG); + } + + if (root->next == prev) + { + prev = root; + return *this; + } + + if (prev->prev) + { + prev = prev->prev; + return *this; + } + + for (L *it = root; it->next; it = it->next) + { + if (it->next == prev) + { + prev = it; + return *this; + } + } + + throw DFHack::Error::NullPointer("prev->prev", DFHACK_FUNCTION_SIG); + } + iterator operator++(int) + { + iterator copy(*this); + ++*this; + return copy; + } + iterator operator--(int) + { + iterator copy(*this); + --*this; + return copy; + } + iterator & operator=(const iterator & other) + { + root = other.root; + prev = other.prev; + next = other.next; + return *this; + } + + proxy operator*() + { + CHECK_NULL_POINTER(prev); + CHECK_NULL_POINTER(prev->next); + + if (next) + return proxy(root, prev->next); + + return proxy(root, prev); + } + + const proxy operator*() const + { + CHECK_NULL_POINTER(prev); + CHECK_NULL_POINTER(prev->next); + + if (next) + return proxy(root, prev->next); + + return proxy(root, prev); + } + + I * operator->() const + { + return **this; + } + + operator const_iterator() const + { + return const_iterator(*this); + } + bool operator==(const iterator & other) const + { + if (root != other.root) + return false; + + if (other.next && !next) + return prev && other.prev && prev->next == other.prev; + if (next && !other.next) + return prev && other.prev && other.prev->next == prev; + return other.prev == prev; + } + bool operator!=(const iterator & other) const + { + return !(*this == other); + } + }; + class const_iterator + { + iterator iter; + friend struct DfLinkedList; + public: + using difference_type = void; + using value_type = I * ; + using pointer = I * const *; + using reference = const proxy; + using iterator_category = std::bidirectional_iterator_tag; + + const_iterator(const iterator & iter) : iter(iter) {} + const_iterator(const const_iterator & other) : iter(other.iter) {} + + const_iterator & operator++() + { + ++iter; + return *this; + } + const_iterator & operator--() + { + --iter; + return *this; + } + const_iterator operator++(int) + { + const_iterator copy(*this); + ++*this; + return copy; + } + const_iterator operator--(int) + { + const_iterator copy(*this); + --*this; + return copy; + } + const_iterator & operator=(const const_iterator & other) + { + iter = other.iter; + return *this; + } + const proxy operator*() const + { + return *iter; + } + I *operator->() const + { + return *iter; + } + bool operator==(const const_iterator & other) const + { + return iter == other.iter; + } + bool operator!=(const const_iterator & other) const + { + return iter != other.iter; + } + }; + + typedef I *value_type; + typedef I * & reference_type; + typedef void difference_type; + typedef size_t size_type; + + bool empty() const + { + return static_cast(this)->next == nullptr; + } + size_t size() const + { + size_t n = 0; + for (I *i : *this) + n++; + return n; + } + + iterator begin() + { + return iterator(static_cast(this), static_cast(this)); + } + const_iterator begin() const + { + return const_iterator(const_cast *>(this)->begin()); + } + const_iterator cbegin() const + { + return begin(); + } + iterator end() + { + L *it = static_cast(this); + while (it->next && it->next->next) + { + it = it->next; + } + return iterator(static_cast(this), it, true); + } + const_iterator end() const + { + return const_iterator(const_cast *>(this)->end()); + } + const_iterator cend() const + { + return end(); + } + }; } diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 31a949df1..149ac47a0 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -439,6 +439,7 @@ namespace df using DFHack::enum_list_attr; using DFHack::BitArray; using DFHack::DfArray; + using DFHack::DfLinkedList; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" diff --git a/library/xml b/library/xml index 8e19a41d9..83142467d 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 8e19a41d946cc1a82339da669ab7c4f6042afa8f +Subproject commit 83142467d76f0e642455bcb083ea661fbf44098f From 0b9d46712f9c95fcf1ba30a7b572e4e47ce63549 Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Mon, 18 Jun 2018 12:57:36 -0500 Subject: [PATCH 2/4] Run DfLinkedList::end's computations only when needed. --- library/include/BitArray.h | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/library/include/BitArray.h b/library/include/BitArray.h index 42f6fd431..c703a2554 100644 --- a/library/include/BitArray.h +++ b/library/include/BitArray.h @@ -272,7 +272,7 @@ namespace DFHack { if (!prev->next) { - prev->next = df::allocate(); + prev->next = new L(); if (prev != root) prev->next->prev = prev; } @@ -288,6 +288,17 @@ namespace DFHack bool next; friend struct DfLinkedList; friend class const_iterator; + void ensure_prev() + { + CHECK_NULL_POINTER(root); + if (!prev && !next) + { + for (prev = root; prev->next && prev->next->next; prev = prev->next) + { + next = true; + } + } + } iterator() : root(nullptr), prev(nullptr), next(false) {} public: using difference_type = void; @@ -321,6 +332,8 @@ namespace DFHack { CHECK_NULL_POINTER(root); + ensure_prev(); + if (next) { next = false; @@ -412,6 +425,18 @@ namespace DFHack if (root != other.root) return false; + if (!next && !prev && !other.next && !other.prev) + return true; + + if ((!next && !prev) || (!other.next && !other.prev)) + { + iterator this_copy = *this; + this_copy.ensure_prev(); + iterator other_copy = other; + other_copy.ensure_prev(); + return this_copy == other_copy; + } + if (other.next && !next) return prev && other.prev && prev->next == other.prev; if (next && !other.next) @@ -505,7 +530,7 @@ namespace DFHack } const_iterator begin() const { - return const_iterator(const_cast *>(this)->begin()); + return const_iterator(static_cast(this), static_cast(this)); } const_iterator cbegin() const { @@ -513,16 +538,11 @@ namespace DFHack } iterator end() { - L *it = static_cast(this); - while (it->next && it->next->next) - { - it = it->next; - } - return iterator(static_cast(this), it, true); + return iterator(static_cast(this), nullptr, false); } const_iterator end() const { - return const_iterator(const_cast *>(this)->end()); + return const_iterator(static_cast(this), nullptr, false); } const_iterator cend() const { From 1606483e7e54c455deaacb1f617644e1460ddb16 Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Mon, 18 Jun 2018 13:36:57 -0500 Subject: [PATCH 3/4] perf improvement for DfLinkedList::end(), erase/insert/insert_after/push_front implementations --- library/include/BitArray.h | 289 +++++++++++++++++-------------------- 1 file changed, 134 insertions(+), 155 deletions(-) diff --git a/library/include/BitArray.h b/library/include/BitArray.h index c703a2554..71c7af25f 100644 --- a/library/include/BitArray.h +++ b/library/include/BitArray.h @@ -241,133 +241,49 @@ namespace DFHack class iterator; class const_iterator; - class proxy - { - L *root; - L *prev; - friend class iterator; - proxy(L *root, L *prev) : root(root), prev(prev) - { - CHECK_NULL_POINTER(root); - CHECK_NULL_POINTER(prev); - } - - public: - operator I * & () - { - CHECK_NULL_POINTER(prev->next); - return prev->next->item; - } - operator I *() const - { - CHECK_NULL_POINTER(prev->next); - return prev->next->item; - } - I *operator->() const - { - CHECK_NULL_POINTER(prev->next); - return prev->next->item; - } - proxy & operator=(I *item) - { - if (!prev->next) - { - prev->next = new L(); - if (prev != root) - prev->next->prev = prev; - } - prev->next->item = item; - return *this; - } - }; - class iterator { L *root; - L *prev; - bool next; + L *cur; friend struct DfLinkedList; friend class const_iterator; - void ensure_prev() - { - CHECK_NULL_POINTER(root); - if (!prev && !next) - { - for (prev = root; prev->next && prev->next->next; prev = prev->next) - { - next = true; - } - } - } - iterator() : root(nullptr), prev(nullptr), next(false) {} + iterator(L *root, L *cur) : root(root), cur(cur) {} public: using difference_type = void; using value_type = I *; using pointer = I **; - using reference = proxy; - using iterator_category = std::output_iterator_tag; + using reference = I * &; + using iterator_category = std::bidirectional_iterator_tag; - iterator(L *root, L *prev, bool next = false) : root(root), prev(prev), next(next) {} - iterator(const iterator & other) : root(other.root), prev(other.prev), next(other.next) {} + iterator() : root(nullptr), cur(nullptr) {} + iterator(const iterator & other) : root(other.root), cur(other.cur) {} iterator & operator++() { CHECK_NULL_POINTER(root); + CHECK_NULL_POINTER(cur); - CHECK_NULL_POINTER(prev); - CHECK_NULL_POINTER(prev->next); - - if (!next && !prev->next->next) - { - next = true; - return *this; - } - - CHECK_NULL_POINTER(prev->next->next); - - prev = prev->next; + cur = cur->next; return *this; } iterator & operator--() { CHECK_NULL_POINTER(root); - ensure_prev(); - - if (next) + if (!cur) { - next = false; - return *this; - } - - CHECK_NULL_POINTER(prev); - if (prev == root) - { - throw DFHack::Error::NullPointer("prev->prev", DFHACK_FUNCTION_SIG); - } - - if (root->next == prev) - { - prev = root; - return *this; - } - - if (prev->prev) - { - prev = prev->prev; - return *this; - } - - for (L *it = root; it->next; it = it->next) - { - if (it->next == prev) + // find end() - 1 + for (cur = root->next; cur && cur->next; cur = cur->next) { - prev = it; - return *this; } + return *this; } - throw DFHack::Error::NullPointer("prev->prev", DFHACK_FUNCTION_SIG); + CHECK_NULL_POINTER(cur); + CHECK_NULL_POINTER(cur->prev); + + cur = cur->prev; + return *this; } iterator operator++(int) { @@ -384,36 +300,24 @@ namespace DFHack iterator & operator=(const iterator & other) { root = other.root; - prev = other.prev; - next = other.next; + cur = other.cur; return *this; } - proxy operator*() + I * & operator*() { - CHECK_NULL_POINTER(prev); - CHECK_NULL_POINTER(prev->next); - - if (next) - return proxy(root, prev->next); + CHECK_NULL_POINTER(root); + CHECK_NULL_POINTER(cur); - return proxy(root, prev); + return cur->item; } - const proxy operator*() const + I * const & operator*() const { - CHECK_NULL_POINTER(prev); - CHECK_NULL_POINTER(prev->next); - - if (next) - return proxy(root, prev->next); - - return proxy(root, prev); - } + CHECK_NULL_POINTER(root); + CHECK_NULL_POINTER(cur); - I * operator->() const - { - return **this; + return cur->item; } operator const_iterator() const @@ -422,26 +326,7 @@ namespace DFHack } bool operator==(const iterator & other) const { - if (root != other.root) - return false; - - if (!next && !prev && !other.next && !other.prev) - return true; - - if ((!next && !prev) || (!other.next && !other.prev)) - { - iterator this_copy = *this; - this_copy.ensure_prev(); - iterator other_copy = other; - other_copy.ensure_prev(); - return this_copy == other_copy; - } - - if (other.next && !next) - return prev && other.prev && prev->next == other.prev; - if (next && !other.next) - return prev && other.prev && other.prev->next == prev; - return other.prev == prev; + return root == other.root && cur == other.cur; } bool operator!=(const iterator & other) const { @@ -454,9 +339,9 @@ namespace DFHack friend struct DfLinkedList; public: using difference_type = void; - using value_type = I * ; + using value_type = I *; using pointer = I * const *; - using reference = const proxy; + using reference = I * const &; using iterator_category = std::bidirectional_iterator_tag; const_iterator(const iterator & iter) : iter(iter) {} @@ -475,13 +360,13 @@ namespace DFHack const_iterator operator++(int) { const_iterator copy(*this); - ++*this; + ++iter; return copy; } const_iterator operator--(int) { const_iterator copy(*this); - --*this; + --iter; return copy; } const_iterator & operator=(const const_iterator & other) @@ -489,11 +374,7 @@ namespace DFHack iter = other.iter; return *this; } - const proxy operator*() const - { - return *iter; - } - I *operator->() const + I * const & operator*() const { return *iter; } @@ -526,11 +407,11 @@ namespace DFHack iterator begin() { - return iterator(static_cast(this), static_cast(this)); + return iterator(static_cast(this), static_cast(this)->next); } const_iterator begin() const { - return const_iterator(static_cast(this), static_cast(this)); + return const_iterator(static_cast(this), static_cast(this)->next); } const_iterator cbegin() const { @@ -538,15 +419,113 @@ namespace DFHack } iterator end() { - return iterator(static_cast(this), nullptr, false); + return iterator(static_cast(this), nullptr); } const_iterator end() const { - return const_iterator(static_cast(this), nullptr, false); + return const_iterator(static_cast(this), nullptr); } const_iterator cend() const { return end(); } + + iterator erase(const_iterator pos) + { + auto root = static_cast(this); + CHECK_INVALID_ARGUMENT(pos.iter.root == root); + CHECK_NULL_POINTER(pos.iter.cur); + + auto link = pos.iter.cur; + auto next = link->next; + if (link->next && link->prev) + { + link->next->prev = link->prev; + link->prev->next = link->next; + } + else if (link->next) + { + link->next->prev = nullptr; + } + else if (link->prev) + { + link->prev->next = nullptr; + } + + if (root->next == link) + { + root->next = next; + } + + delete link; + + return iterator(root, next); + } + iterator insert(const_iterator pos, I * const & item) + { + auto root = static_cast(this); + CHECK_INVALID_ARGUMENT(pos.iter.root == root); + + auto link = pos.iter.cur; + if (!link || !link->prev) + { + if (!link && root->next) + { + pos--; + return insert_after(pos, item); + } + + CHECK_INVALID_ARGUMENT(root->next == link); + push_front(item); + return begin(); + } + + auto newlink = new L(); + newlink->prev = link->prev; + newlink->next = link; + link->prev = newlink; + if (newlink->prev) + { + newlink->prev->next = newlink; + } + else if (link == root->next) + { + root->next = newlink; + } + newlink->item = item; + return iterator(root, newlink); + } + iterator insert_after(const_iterator pos, I * const & item) + { + auto root = static_cast(this); + CHECK_INVALID_ARGUMENT(pos.iter.root == root); + CHECK_NULL_POINTER(pos.iter.cur); + + auto link = pos.iter.cur; + auto next = link->next; + auto newlink = new L(); + newlink->prev = link; + newlink->next = next; + link->next = newlink; + if (next) + { + next->prev = newlink; + } + newlink->item = item; + return iterator(root, newlink); + } + void push_front(I * const & item) + { + auto root = static_cast(this); + auto link = new L(); + link->prev = nullptr; + if (root->next) + { + root->next->prev = link; + link->next = root->next; + } + link->item = item; + root->next = link; + } }; } From b5eb541fd34c44c6268d9464f58d3392ffa8394e Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Mon, 18 Jun 2018 15:04:54 -0500 Subject: [PATCH 4/4] Handle pointers back to the list link. --- library/include/BitArray.h | 100 ++++++++++++++++++++++++++----------- 1 file changed, 70 insertions(+), 30 deletions(-) diff --git a/library/include/BitArray.h b/library/include/BitArray.h index 71c7af25f..cfb859bd0 100644 --- a/library/include/BitArray.h +++ b/library/include/BitArray.h @@ -241,6 +241,41 @@ namespace DFHack class iterator; class const_iterator; + class proxy + { + L *cur; + friend struct DfLinkedList; + friend class iterator; + proxy(L *cur) : cur(cur) + { + CHECK_NULL_POINTER(cur); + } + public: + operator I *const &() const + { + return cur->item; + } + I *operator->() const + { + return cur->item; + } + proxy & operator=(I *const & item) + { + if (item) + { + CHECK_INVALID_ARGUMENT(item->dfhack_get_list_link() == nullptr); + item->dfhack_set_list_link(cur); + } + if (cur->item) + { + cur->item->dfhack_set_list_link(nullptr); + } + cur->item = item; + + return *this; + } + }; + class iterator { L *root; @@ -252,7 +287,7 @@ namespace DFHack using difference_type = void; using value_type = I *; using pointer = I **; - using reference = I * &; + using reference = proxy; using iterator_category = std::bidirectional_iterator_tag; iterator() : root(nullptr), cur(nullptr) {} @@ -304,15 +339,15 @@ namespace DFHack return *this; } - I * & operator*() + proxy operator*() { CHECK_NULL_POINTER(root); CHECK_NULL_POINTER(cur); - return cur->item; + return proxy(cur); } - I * const & operator*() const + I *const & operator*() const { CHECK_NULL_POINTER(root); CHECK_NULL_POINTER(cur); @@ -340,8 +375,8 @@ namespace DFHack public: using difference_type = void; using value_type = I *; - using pointer = I * const *; - using reference = I * const &; + using pointer = I *const *; + using reference = I *const &; using iterator_category = std::bidirectional_iterator_tag; const_iterator(const iterator & iter) : iter(iter) {} @@ -374,7 +409,7 @@ namespace DFHack iter = other.iter; return *this; } - I * const & operator*() const + I *const & operator*() const { return *iter; } @@ -388,10 +423,10 @@ namespace DFHack } }; - typedef I *value_type; - typedef I * & reference_type; - typedef void difference_type; - typedef size_t size_type; + using value_type = I *; + using reference_type = proxy; + using difference_type = void; + using size_type = size_t; bool empty() const { @@ -400,7 +435,7 @@ namespace DFHack size_t size() const { size_t n = 0; - for (I *i : *this) + for (value_type const & i : *this) n++; return n; } @@ -411,7 +446,7 @@ namespace DFHack } const_iterator begin() const { - return const_iterator(static_cast(this), static_cast(this)->next); + return const_iterator(const_cast *>(this)->begin()); } const_iterator cbegin() const { @@ -423,7 +458,7 @@ namespace DFHack } const_iterator end() const { - return const_iterator(static_cast(this), nullptr); + return const_iterator(const_cast *>(this)->end()); } const_iterator cend() const { @@ -438,30 +473,29 @@ namespace DFHack auto link = pos.iter.cur; auto next = link->next; - if (link->next && link->prev) + + if (link->prev) { - link->next->prev = link->prev; link->prev->next = link->next; } - else if (link->next) - { - link->next->prev = nullptr; - } - else if (link->prev) + else { - link->prev->next = nullptr; + root->next = link->next; } - if (root->next == link) + if (link->next) { - root->next = next; + link->next->prev = link->prev; } + proxy p(link); + p = nullptr; + delete link; return iterator(root, next); } - iterator insert(const_iterator pos, I * const & item) + iterator insert(const_iterator pos, I *const & item) { auto root = static_cast(this); CHECK_INVALID_ARGUMENT(pos.iter.root == root); @@ -492,10 +526,12 @@ namespace DFHack { root->next = newlink; } - newlink->item = item; + newlink->item = nullptr; + proxy p(newlink); + p = item; return iterator(root, newlink); } - iterator insert_after(const_iterator pos, I * const & item) + iterator insert_after(const_iterator pos, I *const & item) { auto root = static_cast(this); CHECK_INVALID_ARGUMENT(pos.iter.root == root); @@ -511,10 +547,12 @@ namespace DFHack { next->prev = newlink; } - newlink->item = item; + newlink->item = nullptr; + proxy p(newlink); + p= item; return iterator(root, newlink); } - void push_front(I * const & item) + void push_front(I *const & item) { auto root = static_cast(this); auto link = new L(); @@ -524,7 +562,9 @@ namespace DFHack root->next->prev = link; link->next = root->next; } - link->item = item; + link->item = nullptr; + proxy p(link); + p = item; root->next = link; } };