From 728d9f05e948839f4d537079cd11c4fcc6fe34f5 Mon Sep 17 00:00:00 2001 From: Stepan Usatiuk Date: Fri, 12 Apr 2024 13:18:36 +0200 Subject: [PATCH] Less crappy skiplist --- .../newlib-4.4.0.20231231/.idea/editor.xml | 103 ++++ .../newlib-4.4.0.20231231/.idea/misc.xml | 20 + .../newlib-4.4.0.20231231/.idea/vcs.xml | 6 + src/CMakeLists.txt | 1 + src/arch/x86/syscalls.cpp | 30 +- src/arch/x86/task.cpp | 49 +- src/arch/x86/task.hpp | 2 +- src/kernel/TestTemplates.cpp | 36 +- src/kernel/VMA.cpp | 24 +- src/kernel/VMA.hpp | 2 +- src/kernel/templates/SkipList.hpp | 522 ++++++++++++------ src/kernel/vfs/FDT.cpp | 19 +- src/kernel/vfs/FDT.hpp | 2 +- src/kernel/vfs/MemFs.cpp | 6 +- src/kernel/vfs/MemFs.hpp | 2 +- .../templates/SkipListDetailedTest.cpp | 268 ++++----- src/unit-tests/templates/SkipListTest.cpp | 88 ++- 17 files changed, 772 insertions(+), 408 deletions(-) create mode 100644 ficus-toolchain/newlib/newlib-4.4.0.20231231/.idea/editor.xml create mode 100644 ficus-toolchain/newlib/newlib-4.4.0.20231231/.idea/misc.xml create mode 100644 ficus-toolchain/newlib/newlib-4.4.0.20231231/.idea/vcs.xml diff --git a/ficus-toolchain/newlib/newlib-4.4.0.20231231/.idea/editor.xml b/ficus-toolchain/newlib/newlib-4.4.0.20231231/.idea/editor.xml new file mode 100644 index 000000000..855412d8d --- /dev/null +++ b/ficus-toolchain/newlib/newlib-4.4.0.20231231/.idea/editor.xml @@ -0,0 +1,103 @@ + + + + + \ No newline at end of file diff --git a/ficus-toolchain/newlib/newlib-4.4.0.20231231/.idea/misc.xml b/ficus-toolchain/newlib/newlib-4.4.0.20231231/.idea/misc.xml new file mode 100644 index 000000000..4190e51a2 --- /dev/null +++ b/ficus-toolchain/newlib/newlib-4.4.0.20231231/.idea/misc.xml @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/ficus-toolchain/newlib/newlib-4.4.0.20231231/.idea/vcs.xml b/ficus-toolchain/newlib/newlib-4.4.0.20231231/.idea/vcs.xml new file mode 100644 index 000000000..c2365ab11 --- /dev/null +++ b/ficus-toolchain/newlib/newlib-4.4.0.20231231/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1ce744839..0618f5425 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,3 +1,4 @@ +set(CMAKE_CXX_STANDARD 20) add_executable(kernel.elf) diff --git a/src/arch/x86/syscalls.cpp b/src/arch/x86/syscalls.cpp index 20db4874a..fff3aff46 100644 --- a/src/arch/x86/syscalls.cpp +++ b/src/arch/x86/syscalls.cpp @@ -107,36 +107,36 @@ uint64_t syscall_lseek(uint64_t fd, uint64_t off, uint64_t whence) { } uint64_t syscall_print_tasks() { - static SkipList> last_times = Scheduler::getTaskTimePerPid(); - static std::atomic last_print_time = micros; + static SkipListMap> last_times = Scheduler::getTaskTimePerPid(); + static std::atomic last_print_time = micros; - uint64_t prev_print_time = last_print_time; - last_print_time = micros; - SkipList> prev_times = std::move(last_times); - last_times = Scheduler::getTaskTimePerPid(); + uint64_t prev_print_time = last_print_time; + last_print_time = micros; + SkipListMap> prev_times = std::move(last_times); + last_times = Scheduler::getTaskTimePerPid(); - uint64_t slice = last_print_time - prev_print_time; + uint64_t slice = last_print_time - prev_print_time; if (slice == 0) return 0; for (const auto &t: prev_times) { - auto f = last_times.find(t.key); - if (!f->end && f->key == t.key) { - assert(f->data.second >= t.data.second); + auto f = last_times.find(t.first); + if (f != last_times.end()) { + assert(f->second.second >= t.second.second); String buf; buf += "PID: "; - buf += t.key; + buf += t.first; buf += " "; - buf += t.data.first; + buf += t.second.first; buf += " usage: "; - buf += (((f->data.second - t.data.second) * 100ULL) / slice); + buf += (((f->second.second - t.second.second) * 100ULL) / slice); buf += "%\n"; GlobalTtyManager.all_tty_putstr(buf.c_str()); } else { String buf; buf += "PID: "; - buf += t.key; + buf += t.first; buf += " "; - buf += t.data.first; + buf += t.second.first; buf += " dead \n"; GlobalTtyManager.all_tty_putstr(buf.c_str()); } diff --git a/src/arch/x86/task.cpp b/src/arch/x86/task.cpp index 869ff7b25..bdef7ac69 100644 --- a/src/arch/x86/task.cpp +++ b/src/arch/x86/task.cpp @@ -37,14 +37,14 @@ void sanity_check_frame(Arch::TaskFrame *cur_frame) { assert2((cur_frame->cs == Arch::GDT::gdt_code.selector() || (cur_frame->ss == Arch::GDT::gdt_code_user.selector()) | 0x3), "CS wrong!"); } -std::atomic max_pid = 0; -Mutex AllTasks_lock; -SkipList> AllTasks; +std::atomic max_pid = 0; +Mutex AllTasks_lock; +SkipListMap> AllTasks; -static List::Node *RunningTask; +static List::Node *RunningTask; -static Spinlock NextTasks_lock; -static List NextTasks; +static Spinlock NextTasks_lock; +static List NextTasks; // Task freer Mutex TasksToFree_lock; @@ -52,11 +52,11 @@ CV TasksToFree_cv; List::Node *> TasksToFree; // Waiting -Mutex WaitingTasks_mlock; -CV WaitingTasks_cv; -SkipList::Node *> WaitingTasks; +Mutex WaitingTasks_mlock; +CV WaitingTasks_cv; +SkipListMultiMap::Node *> WaitingTasks; -static std::atomic initialized = false; +static std::atomic initialized = false; // void Scheduler::remove_self() { @@ -147,7 +147,7 @@ Task::Task(Task::TaskMode mode, void (*entrypoint)(), const char *name) { sanity_check_frame(&_frame); { LockGuard l(AllTasks_lock); - AllTasks.add(_pid, UniquePtr(this)); + AllTasks.emplace(_pid, UniquePtr(this)); } } @@ -155,12 +155,12 @@ Task::~Task() { assert(_state != TaskState::TS_RUNNING); } -SkipList> Scheduler::getTaskTimePerPid() { - SkipList> ret; +SkipListMap> Scheduler::getTaskTimePerPid() { + SkipListMap> ret; { LockGuard l(AllTasks_lock); for (const auto &t: AllTasks) { - ret.add(t.data->pid(), std::make_pair(t.data->name(), t.data->used_time())); + ret.emplace(t.second->pid(), std::make_pair(t.second->name(), t.second->used_time())); } } return ret; @@ -216,7 +216,7 @@ void Scheduler::sleep_self(uint64_t diff) { { WaitingTasks_mlock.lock(); assert(cur_task() != nullptr); - assert(WaitingTasks.add(wake_time, extract_running_task_node()) != nullptr); + WaitingTasks.emplace(wake_time, extract_running_task_node()); Scheduler::self_block(WaitingTasks_mlock); } } @@ -233,22 +233,17 @@ static void task_waker() { { WaitingTasks_mlock.lock(); - while (WaitingTasks.begin() != WaitingTasks.end() && WaitingTasks.begin()->key <= micros && WaitingTasks.begin()->data->val->state() != Task::TaskState::TS_RUNNING) { - auto *node = &*WaitingTasks.begin(); - auto task = WaitingTasks.begin()->data; + while (WaitingTasks.begin() != WaitingTasks.end() && WaitingTasks.begin()->first <= micros && WaitingTasks.begin()->second->val->state() != Task::TaskState::TS_RUNNING) { + auto node = WaitingTasks.begin(); + // FIXME: + auto node2 = node; + ++node2; + auto task = WaitingTasks.begin()->second; - // TODO this is all ugly - uint64_t l1 = 0; - for (auto cur = node; !cur->end; cur = cur->next[0]) l1++; - - WaitingTasks.erase(node, node->next[0], false); - - uint64_t l2 = 0; - for (auto *cur = &*WaitingTasks.begin(); !cur->end; cur = cur->next[0]) l2++; + WaitingTasks.erase(node, node2); WaitingTasks_mlock.unlock(); - assert(l1 - l2 == 1); task->val->_sleep_until = 0; task->val->_state = Task::TaskState::TS_RUNNING; diff --git a/src/arch/x86/task.hpp b/src/arch/x86/task.hpp index bf5b81683..e08838001 100644 --- a/src/arch/x86/task.hpp +++ b/src/arch/x86/task.hpp @@ -106,7 +106,7 @@ namespace Scheduler { extern "C" void switch_task(Arch::TaskFrame *cur_frame); // TODO: that's quite inefficient! - SkipList> getTaskTimePerPid(); + SkipListMap> getTaskTimePerPid(); void yield_self(); } // namespace Scheduler diff --git a/src/kernel/TestTemplates.cpp b/src/kernel/TestTemplates.cpp index d668b25d9..34dd7f247 100644 --- a/src/kernel/TestTemplates.cpp +++ b/src/kernel/TestTemplates.cpp @@ -5,13 +5,13 @@ #include "TestTemplates.hpp" -#include "assert.h" #include "List.hpp" #include "PointersCollection.hpp" #include "SkipList.hpp" #include "SkipListSet.hpp" #include "String.hpp" #include "Vector.hpp" +#include "assert.h" #include "TtyManager.hpp" @@ -153,28 +153,28 @@ public: class SkipListTester { public: bool test() { - SkipList test1; + SkipListMap test1; - test1.add(5, "test5", false); - test1.add(999, "test999", false); - test1.add(5, "test5", false); - test1.add(1, "test1", false); - test1.add(999, "test999", false); + test1.emplace(5, "test5"); + test1.emplace(999, "test999"); + test1.emplace(5, "test5"); + test1.emplace(1, "test1"); + test1.emplace(999, "test999"); - assert(test1.find(5)->data == "test5"); - assert(test1.find(1)->data == "test1"); - assert(test1.find(999)->data == "test999"); + assert(test1.find(5)->second == "test5"); + assert(test1.find(1)->second == "test1"); + assert(test1.find(999)->second == "test999"); + + typename decltype(test1)::iterator tit = test1.begin(); + typename decltype(test1)::const_iterator tcit = tit; test1.erase(1); - assert(test1.find(1)->data != "test1"); - test1.add(87, "test87", false); - assert(test1.find(87)->data == "test87"); - - auto p2 = test1.lower_bound_update(78); - assert(p2->data == "test87"); - test1.add(78, "test78", true); - assert(test1.find(78)->data == "test78"); + assert(test1.find(1) == test1.cend()); + test1.emplace(87, "test87"); + assert(test1.find(87)->second == "test87"); + test1.emplace(78, "test78"); + assert(test1.find(78)->second == "test78"); // GlobalTtyManager.all_tty_putstr("SkipList tests ok!\n"); return true; } diff --git a/src/kernel/VMA.cpp b/src/kernel/VMA.cpp index d8af856ef..248db1c75 100644 --- a/src/kernel/VMA.cpp +++ b/src/kernel/VMA.cpp @@ -14,7 +14,7 @@ VMA::VMA(AddressSpace *space) : space(space) { LockGuard l(regions_lock); - regions.add(0x1000, {0x1000, 0xFFF8000000000000ULL - 0x20000, EntryType::FREE}); + regions.emplace(std::make_pair(0x1000, {0x1000, 0xFFF8000000000000ULL - 0x20000, EntryType::FREE})); } VMA::ListEntry *VMA::get_entry(uintptr_t v_addr, size_t length) { @@ -24,7 +24,9 @@ VMA::ListEntry *VMA::get_entry(uintptr_t v_addr, size_t length) { // Find the region with start before or at v_addr if (v_addr) { - found = ®ions.find(v_addr)->data; + auto ub = regions.upper_bound(v_addr); + --ub; + found = &ub->second; // Check if it fits if (found->length < length || found->type != EntryType::FREE) found = nullptr; } @@ -32,8 +34,8 @@ VMA::ListEntry *VMA::get_entry(uintptr_t v_addr, size_t length) { // Otherwise try to find something else if (!found) for (auto &n: regions) { - if (n.data.type == EntryType::FREE && n.data.length >= length) { - found = &n.data; + if (n.second.type == EntryType::FREE && n.second.length >= length) { + found = &n.second; break; } } @@ -46,13 +48,13 @@ VMA::ListEntry *VMA::get_entry(uintptr_t v_addr, size_t length) { // If our region actually starts before what we requested, then cut it up if ((tmpFound.begin < v_addr) && (tmpFound.length > ((v_addr - tmpFound.begin) + length))) { - regions.add(tmpFound.begin, {tmpFound.begin, v_addr - tmpFound.begin, EntryType::FREE}); + regions.emplace(std::make_pair((uintptr_t) tmpFound.begin, {tmpFound.begin, v_addr - tmpFound.begin, EntryType::FREE})); tmpFound.begin = v_addr; tmpFound.length -= v_addr - tmpFound.begin; } - regions.add(tmpFound.begin + length, {tmpFound.begin + length, tmpFound.length - length, EntryType::FREE}); - found = ®ions.add(tmpFound.begin, {tmpFound.begin, length, EntryType::ANON})->data; + regions.emplace(std::make_pair(tmpFound.begin + length, {tmpFound.begin + length, tmpFound.length - length, EntryType::FREE})); + found = ®ions.emplace(std::make_pair((uintptr_t) tmpFound.begin, {tmpFound.begin, length, EntryType::ANON})).first->second; } return found; } @@ -106,11 +108,11 @@ int VMA::munmap(void *addr, size_t length) { } VMA::~VMA() { for (const auto &e: regions) { - if (e.data.type == EntryType::ANON) { - assert((e.data.length & (PAGE_SIZE - 1)) == 0); - uint64_t page_len = e.data.length / PAGE_SIZE; + if (e.second.type == EntryType::ANON) { + assert((e.second.length & (PAGE_SIZE - 1)) == 0); + uint64_t page_len = e.second.length / PAGE_SIZE; for (int i = 0; i < page_len; i++) { - free4k((void *) HHDM_P2V(space->virt2real(reinterpret_cast(e.data.begin + i * PAGE_SIZE)))); + free4k((void *) HHDM_P2V(space->virt2real(reinterpret_cast(e.second.begin + i * PAGE_SIZE)))); } } } diff --git a/src/kernel/VMA.hpp b/src/kernel/VMA.hpp index f62217438..786aa3e9e 100644 --- a/src/kernel/VMA.hpp +++ b/src/kernel/VMA.hpp @@ -56,7 +56,7 @@ private: ListEntry *get_entry(uintptr_t v_addr, size_t length); // - SkipList regions; + SkipListMap regions; Mutex regions_lock; }; diff --git a/src/kernel/templates/SkipList.hpp b/src/kernel/templates/SkipList.hpp index 2a368fda5..cf893f944 100644 --- a/src/kernel/templates/SkipList.hpp +++ b/src/kernel/templates/SkipList.hpp @@ -1,8 +1,12 @@ #ifndef SKIPLIST_H #define SKIPLIST_H +#include +#include #include #include +#include +#include #include #include @@ -10,21 +14,34 @@ extern "C" int rand(void); -template -class SkipList { +template +class SkipListBase { +protected: static constexpr size_t maxL{31}; -public: struct Node { Node *next[maxL + 1] = {nullptr}; Node *before = nullptr; bool end = false; - K key = K(); - V data = V(); + alignas(Data) std::array data; + + Data &get() { + assert(!end); + return *std::launder(reinterpret_cast(&data[0])); + } + const Data &get() const { + assert(!end); + return *std::launder(reinterpret_cast(&data[0])); + } }; -private: + // static_assert(std::is_trivially_constructible::value); + // static_assert(std::is_trivially_destructible::value); + // static_assert(std::is_trivially_copyable::value); + // static_assert(std::is_trivially_move_assignable::value); + // static_assert(std::is_trivially_move_constructible::value); + class NodeAllocator { static constexpr int size{64}; Node *nodes[size]; @@ -39,6 +56,12 @@ private: } } + NodeAllocator(const NodeAllocator &) = delete; + // NodeAllocator(NodeAllocator &&) = delete; + NodeAllocator &operator=(const NodeAllocator &) = delete; + // NodeAllocator &operator=(NodeAllocator &&) = delete; + + // void push(Node *&e) { if (top >= size - 1) { delete e; @@ -58,8 +81,6 @@ private: node->end = false; node->before = nullptr; node->next[0] = nullptr; - node->key = K(); - // node->data = V(); return node; } @@ -79,8 +100,7 @@ private: mutable Node *toUpdate[maxL + 1]; size_t curL = 0; -public: - SkipList() noexcept { + SkipListBase() noexcept { root = (Node *) nodeAllocator.get(); root->end = true; endnode = (Node *) nodeAllocator.get(); @@ -92,16 +112,21 @@ public: } }; - ~SkipList() noexcept { + // FIXME: Should this be protected? +public: + ~SkipListBase() noexcept { auto cur = root; while (cur != nullptr) { + if (!cur->end) + std::destroy_at(&cur->get()); + auto prev = cur; cur = cur->next[0]; nodeAllocator.push(prev); } } - SkipList(SkipList const &l) noexcept : SkipList() { + SkipListBase(SkipListBase const &l) noexcept : SkipListBase() { toUpdate[0] = root; for (auto n = l.root->next[0]; n != nullptr && !n->end; n = n->next[0]) { @@ -114,7 +139,6 @@ public: } auto newNode = (Node *) nodeAllocator.get(); - newNode->key = n->key; newNode->data = n->data; newNode->before = toUpdate[0]; if (toUpdate[0]->next[0] != nullptr) toUpdate[0]->next[0]->before = newNode; @@ -127,7 +151,7 @@ public: } } - SkipList(SkipList &&l) noexcept { + SkipListBase(SkipListBase &&l) noexcept { this->root = l.root; l.root = nullptr; this->endnode = l.endnode; @@ -136,62 +160,129 @@ public: l.curL = 0; } - SkipList &operator=(SkipList l) noexcept { + SkipListBase &operator=(SkipListBase l) noexcept { std::swap(l.root, root); std::swap(l.endnode, endnode); std::swap(l.curL, curL); return *this; } - void add(V *p, size_t n, bool reuseUpdate = false) { - if (!reuseUpdate) { - Node *cur = root; - for (int i = curL; i >= 0; i--) { - while (cur->next[i]->key < p->l && !cur->next[i]->end) - cur = cur->next[i]; - toUpdate[i] = cur; - } +protected: + // void add(V *p, size_t n, bool reuseUpdate = false) { + // if (!reuseUpdate) { + // Node *cur = root; + // for (int i = curL; i >= 0; i--) { + // while (cur->next[i]->key < p->l && !cur->next[i]->end) + // cur = cur->next[i]; + // toUpdate[i] = cur; + // } + // } + // + // for (size_t i = 0; i < n; i++, p++) { + // size_t newLevel = randomL(); + // + // if (newLevel > curL) { + // for (size_t j = curL + 1; j <= newLevel; j++) + // toUpdate[j] = root; + // + // curL = newLevel; + // } + // + // auto newNode = (Node *) nodeAllocator.get(); + // newNode->key = p->l; + // newNode->data = *p; + // + // newNode->before = toUpdate[0]; + // if (toUpdate[0]->next[0] != nullptr) toUpdate[0]->next[0]->before = newNode; + // + // for (size_t j = 0; j <= newLevel; j++) { + // newNode->next[j] = toUpdate[j]->next[j]; + // toUpdate[j]->next[j] = newNode; + // toUpdate[j] = newNode; + // } + // } + // } + + + template + struct SkipListBaseIteratorBase { + using iterator_category = std::bidirectional_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = value_type_arg; + using pointer = value_type *; + using reference = value_type &; + + explicit SkipListBaseIteratorBase(Node *n) : n(std::move(n)){}; + + reference operator*() const { return n->get(); } + + pointer operator->() const { return &n->get(); } + + SkipListBaseIteratorBase &operator--() { + if (n->before) + n = n->before; + return *this; } - for (size_t i = 0; i < n; i++, p++) { - size_t newLevel = randomL(); - - if (newLevel > curL) { - for (size_t j = curL + 1; j <= newLevel; j++) - toUpdate[j] = root; - - curL = newLevel; - } - - auto newNode = (Node *) nodeAllocator.get(); - newNode->key = p->l; - newNode->data = *p; - - newNode->before = toUpdate[0]; - if (toUpdate[0]->next[0] != nullptr) toUpdate[0]->next[0]->before = newNode; - - for (size_t j = 0; j <= newLevel; j++) { - newNode->next[j] = toUpdate[j]->next[j]; - toUpdate[j]->next[j] = newNode; - toUpdate[j] = newNode; - } + SkipListBaseIteratorBase &operator++() { + if (n->next[0]) + n = n->next[0]; + return *this; } - } - bool erase(Node *begin, Node *end, bool reuseUpdate = false) { - if (begin == end) return false; + SkipListBaseIteratorBase operator++(int) { + SkipListBaseIteratorBase tmp = *this; + ++(*this); + return tmp; + } - if (!reuseUpdate) { - Node *cur = root; - for (int i = curL; i >= 0; i--) { - while (cur->next[i]->key < begin->key && !cur->next[i]->end) - cur = cur->next[i]; - toUpdate[i] = cur; - } + template + friend bool operator==(const SkipListBaseIteratorBase &a, const SkipListBaseIteratorBase &b); + + template + friend bool operator!=(const SkipListBaseIteratorBase &a, const SkipListBaseIteratorBase &b); + + friend SkipListBase; + + protected: + Node *n; + }; + +public: + template + friend bool operator==(const SkipListBaseIteratorBase &a, const SkipListBaseIteratorBase &b) { + if (a.n->end && b.n->end) return true; + return a.n == b.n; + }; + + template + friend bool operator!=(const SkipListBaseIteratorBase &a, const SkipListBaseIteratorBase &b) { + if (a.n->end && (a.n->end == b.n->end)) return false; + return a.n != b.n; + }; + + using iterator = SkipListBaseIteratorBase; + + struct const_iterator : public SkipListBaseIteratorBase { + using SkipListBaseIteratorBase::SkipListBaseIteratorBase; + const_iterator(const iterator &i) : SkipListBaseIteratorBase(i.n) {} + }; + +protected: + // Comparator true if less than, 0 if equal + template + std::pair erase(const_iterator begin, const_iterator end) { + if (begin == end) return {root->next[0], false}; + + Node *cur = root; + for (int i = curL; i >= 0; i--) { + while (!cur->next[i]->end && Comparator()(cur->next[i]->get(), *begin)) + cur = cur->next[i]; + toUpdate[i] = cur; } Node *prev = nullptr; - for (auto cur = begin; cur != end; cur = cur->next[0]) { + for (auto cur = begin.n; cur != end.n; cur = cur->next[0]) { if (prev) nodeAllocator.push(prev); @@ -210,60 +301,24 @@ public: prev = cur; } + auto ret = std::make_pair(prev->next[0], true); if (prev) nodeAllocator.push(prev); - return true; + return ret; } - Node *add(K const &k, V v, bool reuseUpdate = false) { - if (!reuseUpdate) { - Node *cur = root; - - for (int i = curL; i >= 0; i--) { - while (cur->next[i]->key < k && !cur->next[i]->end) - cur = cur->next[i]; - toUpdate[i] = cur; - } - cur = cur->next[0]; - // Without this it's a multimap TODO: multiple variants of this and merge it with set - // if (cur->key == k && !cur->end) return nullptr; - } - - size_t newLevel = randomL(); - - if (newLevel > curL) { - for (size_t i = curL + 1; i <= newLevel; i++) - toUpdate[i] = root; - - curL = newLevel; - } - - auto newNode = (Node *) nodeAllocator.get(); - newNode->key = k; - newNode->data = std::move(v); - - newNode->before = toUpdate[0]; - if (toUpdate[0]->next[0] != nullptr) toUpdate[0]->next[0]->before = newNode; - - for (size_t i = 0; i <= newLevel; i++) { - newNode->next[i] = toUpdate[i]->next[i]; - toUpdate[i]->next[i] = newNode; - toUpdate[i] = newNode; - } - return newNode; - } - - bool erase(K const &k) { + // Comparator true if less than, 0 if equal + std::pair erase(const const_iterator &k) { Node *cur = root; for (int i = curL; i >= 0; i--) { - while (cur->next[i]->key < k && !cur->next[i]->end) + while (!cur->next[i]->end && cur->next[i] != k.n) cur = cur->next[i]; toUpdate[i] = cur; } cur = cur->next[0]; - if (cur->end || cur->key != k) return false; + if (cur->end || cur != k.n) return {cur, false}; cur->next[0]->before = toUpdate[0]; @@ -277,71 +332,125 @@ public: while (curL > 0 && root->next[curL] == nullptr) curL--; - cur->data = V(); + + std::destroy_at(&cur->get()); + auto ret = std::make_pair(cur->next[0], true); nodeAllocator.push(cur); - return true; + return ret; }; - // Returns the node PRECEDING the node with a key that is GREATER than k - Node *find(K const &k) const { - Node *cur = root; - - for (int i = curL; i >= 0; i--) - while (cur->next[i]->key <= k && !cur->next[i]->end) - cur = cur->next[i]; - - return cur; - } - - Node *upper_bound(K const &k) const { + // Comparator true if less than, 0 if equal + template + std::pair insert(Node *newNode) { Node *cur = root; for (int i = curL; i >= 0; i--) { - while (cur->next[i]->key <= k && !cur->next[i]->end) - cur = cur->next[i]; - } - - cur = cur->next[0]; - - return cur; - } - - Node *lower_bound_update(K const &k) const { - Node *cur = root; - - for (int i = curL; i >= 0; i--) { - while (cur->next[i]->key < k && !cur->next[i]->end) + while (!cur->next[i]->end && Comparator()(cur->next[i]->get(), newNode->get())) cur = cur->next[i]; toUpdate[i] = cur; } - cur = cur->next[0]; + if constexpr (!Duplicate) + if (!cur->end && Comparator().eq(cur->get(), newNode->get())) return {cur, false}; - return cur; + size_t newLevel = randomL(); + + if (newLevel > curL) { + for (size_t i = curL + 1; i <= newLevel; i++) + toUpdate[i] = root; + + curL = newLevel; + } + + newNode->before = toUpdate[0]; + if (toUpdate[0]->next[0] != nullptr) toUpdate[0]->next[0]->before = newNode; + + for (size_t i = 0; i <= newLevel; i++) { + newNode->next[i] = toUpdate[i]->next[i]; + toUpdate[i]->next[i] = newNode; + toUpdate[i] = newNode; + } + return {newNode, true}; } + template + std::pair insert(Data d) { + Node *n = nodeAllocator.get(); + new (n->data) Data(std::move(d)); + return insert(n); + } - Node *lower_bound(K const &k) const { + template + std::pair emplace(Args &&...args) { + Node *n = nodeAllocator.get(); + new (&n->get()) Data(std::forward(args)...); + return insert(n); + } + + // Comparator true if less than, 0 if equal + template + std::pair erase(const CmpArg &k) { Node *cur = root; for (int i = curL; i >= 0; i--) { - while (cur->next[i]->key < k && !cur->next[i]->end) + while (!cur->next[i]->end && Comparator()(cur->next[i]->get(), k)) cur = cur->next[i]; + toUpdate[i] = cur; } - cur = cur->next[0]; + if (cur->end || !Comparator().eq(cur->get(), k)) return {cur, false}; + + cur->next[0]->before = toUpdate[0]; + + for (size_t i = 0; i <= curL; i++) { + if (toUpdate[i]->next[i] != cur) + break; + + toUpdate[i]->next[i] = cur->next[i]; + } + + while (curL > 0 && + root->next[curL] == nullptr) + curL--; + + std::destroy_at(&cur->get()); + auto ret = std::make_pair(cur->next[0], true); + nodeAllocator.push(cur); + return ret; + }; + + template + Node *upper_bound(const CmpArg &k) const { + Node *cur = root; + + for (int i = curL; i >= 0; i--) + while (!cur->next[i]->end && (Comparator()(cur->next[i]->get(), k) || Comparator().eq(cur->next[i]->get(), k))) + cur = cur->next[i]; + if (!cur->end && (Comparator()(cur->get(), k) || Comparator().eq(cur->get(), k))) + cur = cur->next[0]; return cur; } + template + Node *lower_bound(const CmpArg &k) const { + Node *cur = root; - bool operator==(SkipList const &r) const { + for (int i = curL; i >= 0; i--) + while (!cur->next[i]->end && Comparator()(cur->next[i]->get(), k)) + cur = cur->next[i]; + + return cur->next[0]; + } + +public: + bool operator==(SkipListBase const &r) const { auto n = root->next[0]; auto n2 = r.root->next[0]; while (!n->end && !n2->end) { - if (!(n->data == n2->data)) return false; + if (!(n->get() == n2->get())) return false; n = n->next[0]; n2 = n2->next[0]; } @@ -351,54 +460,16 @@ public: return true; } + iterator begin() { return iterator(root->next[0]); } + iterator end() { return iterator(endnode); } - // TODO: pair - struct SkipListIterator { - // using iterator_category = std::forward_iterator_tag; - using difference_type = std::ptrdiff_t; - using value_type = Node; - using pointer = value_type *; - using reference = value_type &; + const_iterator begin() const { return const_iterator(root->next[0]); } + const_iterator end() const { return const_iterator(endnode); } - explicit SkipListIterator(Node *n) : n(std::move(n)){}; - - reference operator*() const { return *n; } - - pointer operator->() const { return n; } - - SkipListIterator &operator--() { - if (!n->end) - n = n->before; - return *this; - } - - SkipListIterator &operator++() { - if (!n->end) - n = n->next[0]; - return *this; - } - - SkipListIterator operator++(int) { - SkipListIterator tmp = *this; - ++(*this); - return tmp; - } - - friend bool operator==(const SkipListIterator &a, const SkipListIterator &b) { return a.n == b.n; }; - - friend bool operator!=(const SkipListIterator &a, const SkipListIterator &b) { return a.n != b.n; }; - - private: - Node *n; - }; - - using iterator = SkipListIterator; - // using const_iterator = SkipListIterator; - - iterator begin() const { return SkipListIterator(root->next[0]); } - - iterator end() const { return SkipListIterator(endnode); } + const_iterator cbegin() const { return const_iterator(root->next[0]); } + const_iterator cend() const { return const_iterator(endnode); } +protected: // void print() const { // std::cout << "LIST STATUS" << std::endl; // @@ -416,4 +487,99 @@ public: }; +template> +class SkipListMapBase : public SkipListBase> { + using BaseT = SkipListBase>; + +public: + using iterator = typename BaseT::iterator; + using const_iterator = typename BaseT::const_iterator; + using value_type = std::pair; + +private: + struct Cmp { + constexpr bool operator()(const value_type &lhs, const K &rhs) const { + return CmpBase()(lhs.first, rhs); + } + constexpr bool operator()(const K &lhs, const value_type &rhs) const { + return CmpBase()(lhs, rhs.first); + } + constexpr bool eq(const value_type &lhs, const K &rhs) const { + return lhs.first == rhs; + } + constexpr bool eq(const K &rhs, const value_type &lhs) const { + return lhs.first == rhs; + } + constexpr bool operator()(const value_type &lhs, const value_type &rhs) const { + return CmpBase()(lhs.first, rhs.first); + } + constexpr bool eq(const value_type &lhs, const value_type &rhs) const { + return lhs.first == rhs.first; + } + }; + +public: + iterator begin() { return BaseT::begin(); } + iterator end() { return BaseT::end(); } + const_iterator begin() const { return BaseT::begin(); } + const_iterator end() const { return BaseT::end(); } + const_iterator cbegin() const { return BaseT::cbegin(); } + const_iterator cend() const { return BaseT::cend(); } + + template + std::pair emplace(Args &&...args) { + auto r = BaseT::template emplace(std::forward(args)...); + return {iterator(r.first), r.second}; + } + + const_iterator find(const K &k) const { + typename BaseT::Node *n = BaseT::template lower_bound(k); + if (n->end || n->get().first != k) return end(); + return const_iterator(n); + } + + iterator find(const K &k) { + typename BaseT::Node *n = BaseT::template lower_bound(k); + if (n->end || n->get().first != k) return end(); + return iterator(n); + } + + const_iterator upper_bound(const K &k) const { + typename BaseT::Node *n = BaseT::template upper_bound(k); + if (n->end) return end(); + return const_iterator(n); + } + + iterator upper_bound(const K &k) { + typename BaseT::Node *n = BaseT::template upper_bound(k); + if (n->end) return end(); + return iterator(n); + } + + iterator erase(const K &k) { + std::pair n = BaseT::template erase(k); + if (n.first->end) return end(); + return iterator(n.first); + } + + iterator erase(const_iterator first, const_iterator last) { + std::pair n = BaseT::template erase(first, last); + if (n.first->end) return end(); + return iterator(n.first); + } + + iterator erase(const_iterator el) { + std::pair n = BaseT::erase(el); + if (n.first->end) return end(); + return iterator(n.first); + } +}; + +template> +using SkipListMap = SkipListMapBase; + +//FIXME: erase is probably janky with this +template> +using SkipListMultiMap = SkipListMapBase; + #endif \ No newline at end of file diff --git a/src/kernel/vfs/FDT.cpp b/src/kernel/vfs/FDT.cpp index 28ddc4526..f2644cd10 100644 --- a/src/kernel/vfs/FDT.cpp +++ b/src/kernel/vfs/FDT.cpp @@ -15,7 +15,7 @@ FDT::FD FDT::open(const Path &p, FileOpts opts) { if (auto n = VFSGlobals::root.traverse(p); n.get() != nullptr) { LockGuard l(_mtx); - _files.add(_cur_fd++, UniquePtr(new File(n, opts))); + _files.emplace(_cur_fd++, UniquePtr(new File(n, opts))); return _cur_fd - 1; } if (opts & FileOpts::O_CREAT) { @@ -27,18 +27,13 @@ FDT::FD FDT::open(const Path &p, FileOpts opts) { void FDT::close(FDT::FD fd) { LockGuard l(_mtx); - if (auto f = _files.find(fd)) - if (!f->end) - if (f->key == fd) { - _files.erase(fd); - } + if (auto f = _files.find(fd); f != _files.end()) + _files.erase(fd); } File *FDT::get(FDT::FD fd) const { LockGuard l(_mtx); - if (auto f = _files.find(fd)) - if (!f->end) - if (f->key == fd) - return f->data.get(); + if (auto f = _files.find(fd); f != _files.end()) + return f->second.get(); return nullptr; } @@ -46,8 +41,8 @@ FDT *FDT::current() { return Scheduler::cur_task()->_addressSpace->getFdt(); } FDT::FDT() { - _files.add(0, UniquePtr(new File(static_ptr_cast(TtyPipe::create()), O_RDONLY))); - _files.add(1, UniquePtr(new File(static_ptr_cast(TtyPipe::create()), O_RDWR))); + _files.emplace(0, UniquePtr(new File(static_ptr_cast(TtyPipe::create()), O_RDONLY))); + _files.emplace(1, UniquePtr(new File(static_ptr_cast(TtyPipe::create()), O_RDWR))); } FDHandle::FDHandle(FDT::FD fd) : _fd(fd) { } diff --git a/src/kernel/vfs/FDT.hpp b/src/kernel/vfs/FDT.hpp index 0aeb9fe0b..56816d760 100644 --- a/src/kernel/vfs/FDT.hpp +++ b/src/kernel/vfs/FDT.hpp @@ -24,7 +24,7 @@ public: static FDT *current(); private: - SkipList> _files; + SkipListMap> _files; int64_t _cur_fd = 10; mutable Mutex _mtx; }; diff --git a/src/kernel/vfs/MemFs.cpp b/src/kernel/vfs/MemFs.cpp index a15a7aa12..c30ec24e1 100644 --- a/src/kernel/vfs/MemFs.cpp +++ b/src/kernel/vfs/MemFs.cpp @@ -12,7 +12,7 @@ Vector> MemFs::MemFsNodeDir::children() { Vector> out; for (auto c: _children) { - out.emplace_back(c.data); + out.emplace_back(c.second); } return out; } @@ -20,13 +20,13 @@ Vector> MemFs::MemFsNodeDir::children() { SharedPtr MemFs::MemFsNodeDir::mkdir(const String &name) { LockGuard l(_lock); auto newnode = MemFsNodeDir::create(name); - _children.add(name, static_ptr_cast(newnode)); + _children.emplace(name, static_ptr_cast(newnode)); return static_ptr_cast(newnode); } SharedPtr MemFs::MemFsNodeDir::mkfile(const String &name) { LockGuard l(_lock); auto newfile = MemFsNodeFile::create(name); - _children.add(name, static_ptr_cast(newfile)); + _children.emplace(name, static_ptr_cast(newfile)); return static_ptr_cast(newfile); } int64_t MemFs::MemFsNodeFile::read(char *buf, size_t start, size_t num) { diff --git a/src/kernel/vfs/MemFs.hpp b/src/kernel/vfs/MemFs.hpp index f4ea08442..aeb567318 100644 --- a/src/kernel/vfs/MemFs.hpp +++ b/src/kernel/vfs/MemFs.hpp @@ -26,7 +26,7 @@ class MemFs : public Filesystem { private: MemFsNodeDir(const String &name) { _name = name; } - SkipList> _children; + SkipListMap> _children; }; struct MemFsNodeFile : public NodeFile { diff --git a/src/unit-tests/templates/SkipListDetailedTest.cpp b/src/unit-tests/templates/SkipListDetailedTest.cpp index 3c5f71c05..fc6552ee6 100644 --- a/src/unit-tests/templates/SkipListDetailedTest.cpp +++ b/src/unit-tests/templates/SkipListDetailedTest.cpp @@ -38,7 +38,7 @@ public: friend std::ostream &operator<<(std::ostream &out, const CRange &p) { std::ios::fmtflags f(out.flags()); - out << std::dec; + out << std::dec << std::resetiosflags(std::ios_base::hex) << std::resetiosflags(std::ios_base::left); if (p.l == p.r) out << p.l; else out << "<" << p.l << ".." << p.r << ">"; @@ -46,7 +46,7 @@ public: return out; } - bool includes(const IntType &p) const { + bool includes(const IntType p) const { return p >= l && p <= r; } @@ -57,101 +57,109 @@ public: bool operator==(const CRange &p) const { return p.l == l && p.r == r; } + + operator std::pair() const { return {l, r}; } }; - +template class Storage = std::map> class CRangeList { private: - SkipList data; + Storage> data; public: // constructor - CRangeList() = default; + CRangeList() { + auto p = std::make_pair(LLONG_MIN, std::make_pair(CRange(LLONG_MIN, LLONG_MAX), false)); + data.emplace(p.first, p.second); + } CRangeList(std::initializer_list l) { - for (auto const &r: l) + auto p = std::make_pair(LLONG_MIN, std::make_pair(CRange(LLONG_MIN, LLONG_MAX), false)); + data.emplace(p.first, p.second); + for (auto const &r: l) { *this += r; - } - - CRangeList(CRangeList &&l) { - data = std::move(l.data); - } - - CRangeList(CRangeList const &l) { - data = l.data; - } - - CRangeList &operator=(CRangeList l) { - std::swap(l.data, data); - return *this; + } } // includes long long / range bool includes(const IntType l) const { - auto f = findInData(l); - if (!f->end && f->data.includes(l)) return true; - return false; + return findInData(l)->second.second; } bool includes(const CRange &r) const { auto f = findInData(r.l); - if (!f->end && f->data.includes(r)) return true; - return false; + if (!f->second.second) return false; + return f->second.first.includes(r); } CRangeList operator+(const CRange &rhs) const { - return CRangeList(*this) += rhs; + auto nl = CRangeList(); + nl += *this; + nl += rhs; + return nl; } CRangeList operator-(const CRange &rhs) const { - return CRangeList(*this) -= rhs; + auto nl = CRangeList(); + nl += *this; + nl -= rhs; + return nl; } - decltype(data)::Node *findInData(IntType const &l) const { - return data.find(l); + typename decltype(data)::const_iterator findInData(IntType l) const { + typename decltype(data)::const_iterator ret = data.upper_bound(l); + --ret; + return ret; } - void set(CRange r, bool visible) { - auto lb = findInData(r.l); - auto ub = findInData(r.r); - - bool lbHasPoint = !lb->end && lb->data.includes(r.l); - if (visible && !lbHasPoint && !lb->end && r.l != LLONG_MIN && lb->data.includes(r.l - 1)) { - r.l--; - lbHasPoint = true; + auto lb = findInData(r.l); + if (lb != data.begin() && !lb->second.first.includes(r.l - 1)) { + auto lbm = lb; + --lbm; + if (lbm->second.second == visible) { + r.l--; + lb = lbm; + } } - bool ubHasPoint = !ub->end && ub->data.includes(r.r); - if (visible && !ubHasPoint && r.r != LLONG_MAX && ub->next[0]->data.includes(r.r + 1)) { - ub = ub->next[0]; - r.r++; - ubHasPoint = true; + auto ub = findInData(r.r); + if (ub != (--data.end()) && !ub->second.first.includes(r.r + 1)) { + auto ubp = ub; + ++ubp; + if (ubp->second.second == visible) { + r.r++; + ub = ubp; + } } - if (!lbHasPoint) lb = lb->next[0]; + if (lb == data.end() || ub == data.end()) throw; + auto lbc = lb->second; + auto ubc = ub->second; + ub++; - CRange toInsert[3]; + std::map> toInsert; - if (visible) { - if (lbHasPoint) - r.l = std::min(lb->data.l, r.l); - if (ubHasPoint) - r.r = std::max(ub->data.r, r.r); - toInsert[0] = {r}; - data.add(toInsert, 1, data.erase(lb, ub->next[0], false)); - } else { - int inserted = 0; - if (lbHasPoint && lb->data.l != r.l) { - toInsert[0] = {lb->data.l, r.l - 1}; - inserted++; + if (r.l != lbc.first.l) { + if (lbc.second == visible) { + r.l = std::min(lbc.first.l, r.l); + } else { + toInsert.emplace(lbc.first.l, std::make_pair(CRange(lbc.first.l, r.l - 1), lbc.second)); } - if (ubHasPoint && ub->data.r != r.r) { - toInsert[inserted] = {r.r + 1, ub->data.r}; - inserted++; + } + + if (r.r != ubc.first.r) { + if (ubc.second == visible) + r.r = std::max(ubc.first.r, r.r); + else { + toInsert.emplace(r.r + 1, std::make_pair(CRange(r.r + 1, ubc.first.r), ubc.second)); } - data.add(toInsert, inserted, data.erase(lb, ub->next[0], false)); + } + toInsert.emplace(r.l, std::make_pair(r, visible)); + data.erase(lb, ub); + for (auto &r: toInsert) { + data.emplace(std::move(r)); } } @@ -162,8 +170,10 @@ public: } CRangeList &operator+=(const CRangeList &r) { - for (auto const &l: r) - *this += l; + for (auto const &l: r.data) { + if (l.second.second) + *this += l.second.first; + } return *this; } @@ -174,14 +184,17 @@ public: } CRangeList &operator-=(const CRangeList &r) { - for (auto const &l: r) - *this -= l; + for (auto const &l: r.data) { + if (l.second.second) + *this -= l.second.first; + } return *this; } // = range / range list CRangeList &operator=(const CRange &r) { - data = decltype(data)(); + auto p = std::make_pair(LLONG_MIN, std::make_pair(CRange(LLONG_MIN, LLONG_MAX), false)); + data.emplace(p.first, p.second); *this += (r); return *this; } @@ -193,41 +206,47 @@ public: // operator != bool operator!=(const CRangeList &r) const { - return !(*this == r); + return !(this->data == r.data); } + // operator << friend std::ostream &operator<<(std::ostream &out, const CRangeList &rl) { out << "{"; bool first = true; - for (const auto &i: rl) { - if (!first) out << ","; - out << i; - first = false; + for (const auto &i: rl.data) { + if (i.second.second) { + if (!first) out << ","; + out << i.second.first; + first = false; + } } out << "}"; return out; } struct CRangeListIterator { - using iterator_category = std::forward_iterator_tag; - using difference_type = std::ptrdiff_t; - using value_type = const CRange; - using pointer = value_type *; - using reference = value_type &; + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = const CRange; + using pointer = value_type *; + using reference = value_type &; - CRangeListIterator(CRangeListIterator const &it) = default; + CRangeListIterator(CRangeListIterator const &it) : it(it.it){}; - CRangeListIterator(decltype(data)::Node *n) : n(n){}; + CRangeListIterator(decltype(data)::const_iterator it, decltype(data)::const_iterator end) : it(it), end(end) { + if (it != end && !it->second.second) + this->it++; + } - reference operator*() const { return n->data; } - - pointer operator->() const { return &(n->data); } + reference operator*() const { return (it->second.first); } + pointer operator->() const { return &(it->second.first); } CRangeListIterator &operator++() { - if (n != nullptr && !n->end) - n = n->next[0]; + ++it; + if (it == end) return *this; + ++it; return *this; } @@ -237,47 +256,44 @@ public: return tmp; } - friend bool operator==(const CRangeListIterator &a, const CRangeListIterator &b) { return a.n == b.n; }; - - friend bool operator!=(const CRangeListIterator &a, const CRangeListIterator &b) { return !(a == b); }; + friend bool operator==(const CRangeListIterator &a, const CRangeListIterator &b) { return a.it == b.it; }; + friend bool operator!=(const CRangeListIterator &a, const CRangeListIterator &b) { return !(a.it == b.it); }; private: - decltype(data)::Node *n; + decltype(data)::const_iterator it; + decltype(data)::const_iterator end; }; using const_iterator = CRangeListIterator; const_iterator begin() const { - return {data.begin()->before->next[0]}; + return CRangeListIterator(data.begin(), data.end()); } const_iterator end() const { - return {data.end()->before->next[0]}; + return CRangeListIterator(data.end(), data.end()); } }; - -CRangeList operator+(const CRange &lhs, const CRange &rhs) { - return CRangeList{lhs, rhs}; -} - -CRangeList operator-(const CRange &lhs, const CRange &rhs) { - return CRangeList{lhs} - rhs; -} - - -std::string toString(const CRangeList &x) { +template +std::string toString(const T &x) { std::ostringstream oss; oss << x; return oss.str(); } -TEST(SkipListDetailedTest, ItWorks) { +using sl_detailed_test_list = testing::Types, CRangeList>; +template +struct SkipListDetailedTestFixture : testing::Test {}; +TYPED_TEST_SUITE(SkipListDetailedTestFixture, sl_detailed_test_list); +TYPED_TEST(SkipListDetailedTestFixture, ItWorks) { assert(std::is_trivially_copyable::value); - CRangeList t; + using CRangeListTested = TypeParam; + + CRangeListTested t; t = CRange(10, 20); // t.data.print(); @@ -285,7 +301,7 @@ TEST(SkipListDetailedTest, ItWorks) { // std::cout << ts; assert(ts == "{<10..20>}"); // return 0; - t = CRangeList{{10, 15}}; + t = CRangeListTested{{10, 15}}; assert(toString(t) == "{<10..15>}"); t = CRange(10, 20); @@ -293,13 +309,16 @@ TEST(SkipListDetailedTest, ItWorks) { assert(toString(t) == "{<10..20>}"); t += CRange(LLONG_MIN, LLONG_MAX); assert(t.includes(CRange(LLONG_MIN, LLONG_MAX))); + ASSERT_EQ(toString(t), "{<-9223372036854775808..9223372036854775807>}"); assert(t.includes(LLONG_MIN)); assert(t.includes(LLONG_MAX)); t -= CRange(LLONG_MIN, LLONG_MAX); + ASSERT_EQ(toString(t), "{}"); t += CRange(LLONG_MIN, LLONG_MAX - 1); + ASSERT_EQ(toString(t), "{<-9223372036854775808..9223372036854775806>}"); t += CRange(LLONG_MAX, LLONG_MAX); - assert(toString(t) == "{<-9223372036854775808..9223372036854775807>}"); + ASSERT_EQ(toString(t), "{<-9223372036854775808..9223372036854775807>}"); t -= CRange(LLONG_MIN, LLONG_MAX); assert(toString(t) == "{}"); @@ -358,7 +377,7 @@ TEST(SkipListDetailedTest, ItWorks) { t += CRange(50, 55); assert(toString(t) == "{<2..4>,<10..80>,<100..150>}"); - auto t2 = CRange(6, 9) + CRange(151, 160); + auto t2 = CRangeListTested() + CRange(6, 9) + CRange(151, 160); assert(toString(t2) == "{<6..9>,<151..160>}"); t += t2; s = toString(t); @@ -371,9 +390,9 @@ TEST(SkipListDetailedTest, ItWorks) { t -= CRange(1000, 1100); assert(toString(t) == "{<-100..200>}"); - t = CRangeList{{10, 15}, - {20, 25}, - {30, 40}}; + t = CRangeListTested{{10, 15}, + {20, 25}, + {30, 40}}; // t.data.print(); assert(toString(t) == "{<10..15>,<20..25>,<30..40>}"); t -= CRange(40, 9999); @@ -389,7 +408,7 @@ TEST(SkipListDetailedTest, ItWorks) { t -= CRange(22, 22); assert(toString(t) == "{<14..15>,<20..21>,<23..25>,<30..34>}"); - t = CRangeList(); + t = CRangeListTested(); t += CRange(0, 100); assert(toString(t) == "{<0..100>}"); t += CRange(200, 300); @@ -401,19 +420,19 @@ TEST(SkipListDetailedTest, ItWorks) { t -= CRange(170, 170); assert(toString(t) == "{<0..100>,<160..169>,<171..180>,<251..300>}"); - t = CRangeList(); + t = CRangeListTested(); t += CRange(10, 90); t -= CRange(20, 30); t -= CRange(40, 50); t -= CRange(60, 90); t += CRange(70, 80); - t2 = CRange(10, 90) - CRange(20, 30) - CRange(40, 50) - CRange(60, 90) + CRange(70, 80); + t2 = CRangeListTested() + CRange(10, 90) - CRange(20, 30) - CRange(40, 50) - CRange(60, 90) + CRange(70, 80); ts = toString(t); auto ts2 = toString(t2); assert(ts == ts2); - CRangeList a, b; + CRangeListTested a, b; assert(sizeof(CRange) <= 2 * sizeof(long long)); a = CRange(5, 10); @@ -422,22 +441,22 @@ TEST(SkipListDetailedTest, ItWorks) { a += CRange(-5, 0); a += CRange(8, 50); assert(toString(a) == "{<-5..0>,<5..100>}"); - a += CRange(101, 105) + CRange(120, 150) + CRange(160, 180) + CRange(190, 210); + a += CRangeListTested() + CRange(101, 105) + CRange(120, 150) + CRange(160, 180) + CRange(190, 210); assert(toString(a) == "{<-5..0>,<5..105>,<120..150>,<160..180>,<190..210>}"); - a += CRange(106, 119) + CRange(152, 158); + a += CRangeListTested() + CRange(106, 119) + CRange(152, 158); assert(toString(a) == "{<-5..0>,<5..150>,<152..158>,<160..180>,<190..210>}"); a += CRange(-3, 170); a += CRange(-30, 1000); assert(toString(a) == "{<-30..1000>}"); - b = CRange(-500, -300) + CRange(2000, 3000) + CRange(700, 1001); + b = CRangeListTested() + CRange(-500, -300) + CRange(2000, 3000) + CRange(700, 1001); a += b; assert(toString(a) == "{<-500..-300>,<-30..1001>,<2000..3000>}"); a -= CRange(-400, -400); assert(toString(a) == "{<-500..-401>,<-399..-300>,<-30..1001>,<2000..3000>}"); - a -= CRange(10, 20) + CRange(900, 2500) + CRange(30, 40) + CRange(10000, 20000); + a -= CRangeListTested() + CRange(10, 20) + CRange(900, 2500) + CRange(30, 40) + CRange(10000, 20000); assert(toString(a) == "{<-500..-401>,<-399..-300>,<-30..9>,<21..29>,<41..899>,<2501..3000>}"); try { - a += CRange(15, 18) + CRange(10, 0) + CRange(35, 38); + a += CRangeListTested() + CRange(15, 18) + CRange(10, 0) + CRange(35, 38); assert("Exception not thrown" == nullptr); } catch (const std::logic_error &e) { } catch (...) { @@ -449,6 +468,7 @@ TEST(SkipListDetailedTest, ItWorks) { assert(!(a != b)); b += CRange(2600, 2700); assert(toString(b) == "{<-500..-401>,<-399..-300>,<-30..9>,<21..29>,<41..899>,<2501..3000>}"); + assert(toString(a) == "{<-500..-401>,<-399..-300>,<-30..9>,<21..29>,<41..899>,<2501..3000>}"); assert(a == b); assert(!(a != b)); b += CRange(15, 15); @@ -463,28 +483,28 @@ TEST(SkipListDetailedTest, ItWorks) { assert(!b.includes(CRange(800, 900))); assert(!b.includes(CRange(-1000, -450))); assert(!b.includes(CRange(0, 500))); - a += CRange(-10000, 10000) + CRange(10000000, 1000000000); + a += CRangeListTested() + CRange(-10000, 10000) + CRange(10000000, 1000000000); assert(toString(a) == "{<-10000..10000>,<10000000..1000000000>}"); b += a; assert(toString(b) == "{<-10000..10000>,<10000000..1000000000>}"); b -= a; assert(toString(b) == "{}"); - b += CRange(0, 100) + CRange(200, 300) - CRange(150, 250) + CRange(160, 180) - CRange(170, 170); + b += CRangeListTested() + CRange(0, 100) + CRange(200, 300) - CRange(150, 250) + CRange(160, 180) - CRange(170, 170); // b.data.print(); assert(toString(b) == "{<0..100>,<160..169>,<171..180>,<251..300>}"); // b.data.print(); b -= - CRange(10, 90) - + CRangeListTested() + CRange(10, 90) - CRange(20, 30) - CRange(40, 50) - CRange(60, 90) + CRange(70, 80); s = toString(b); assert(toString(b) == "{<0..9>,<20..30>,<40..50>,<60..69>,<81..100>,<160..169>,<171..180>,<251..300>}"); - CRangeList x{{5, 20}, - {150, 200}, - {-9, 12}, - {48, 93}}; + CRangeListTested x{{5, 20}, + {150, 200}, + {-9, 12}, + {48, 93}}; assert(toString(x) == "{<-9..20>,<48..93>,<150..200>}"); std::ostringstream oss; oss << std::setfill('=') << std::hex << std::left; @@ -497,8 +517,8 @@ TEST(SkipListDetailedTest, ItWorks) { assert(oss.str() == "-100\n<-9..20>\n<48..93>\n<150..200>\n400======="); // FIXME: I'll leave it a "stress test" for now - for (long long x = 10; x <= 100; x *= 10) { - CRangeList t; + for (long long x = 25; x <= 250; x *= 10) { + CRangeListTested t; std::cout << x << "====" << std::endl; auto start = std::chrono::high_resolution_clock::now(); auto stop = std::chrono::high_resolution_clock::now(); diff --git a/src/unit-tests/templates/SkipListTest.cpp b/src/unit-tests/templates/SkipListTest.cpp index e0a2ea9e0..543765c64 100644 --- a/src/unit-tests/templates/SkipListTest.cpp +++ b/src/unit-tests/templates/SkipListTest.cpp @@ -10,25 +10,81 @@ struct SkipListTestFixture : testing::Test {}; TYPED_TEST_SUITE(SkipListTestFixture, sl_test_list); TYPED_TEST(SkipListTestFixture, PlaceAdd) { - SkipList test1; + SkipListMap test1; + SkipListMap test2; + SkipListMap test3; + SkipListMap test4; + SkipListMap test5; + SkipListMap test6; + SkipListMap test7; + SkipListMap test8; - test1.add(5, "test5", false); - test1.add(999, "test999", false); - test1.add(5, "test5", false); - test1.add(1, "test1", false); - test1.add(999, "test999", false); + test1.emplace(5, "test5"); + test1.emplace(999, "test999"); + test1.emplace(5, "test5"); + test1.emplace(1, "test1"); + test1.emplace(999, "test999"); - ASSERT_EQ(test1.find(5)->data, "test5"); - ASSERT_EQ(test1.find(1)->data, "test1"); - ASSERT_EQ(test1.find(999)->data, "test999"); + ASSERT_EQ(test1.find(5)->second, "test5"); + ASSERT_EQ(test1.find(1)->second, "test1"); + ASSERT_EQ(test1.find(999)->second, "test999"); + + typename decltype(test1)::iterator tit = test1.begin(); + typename decltype(test1)::const_iterator tcit = tit; test1.erase(1); - ASSERT_NE(test1.find(1)->data, "test1"); - test1.add(87, "test87", false); - ASSERT_EQ(test1.find(87)->data, "test87"); + ASSERT_EQ(test1.find(1), test1.cend()); + test1.emplace(87, "test87"); + ASSERT_EQ(test1.find(87)->second, "test87"); - auto p2 = test1.lower_bound_update(78); - ASSERT_EQ(p2->data, "test87"); - test1.add(78, "test78", true); - ASSERT_EQ(test1.find(78)->data, "test78"); + test1.emplace(78, "test78"); + ASSERT_EQ(test1.find(78)->second, "test78"); +} +TYPED_TEST(SkipListTestFixture, MultiMapTest) { + SkipListMultiMap test1; + + test1.emplace(5, "test5"); + test1.emplace(999, "test999"); + test1.emplace(5, "test5"); + test1.emplace(1, "test1"); + test1.emplace(999, "test999"); + + ASSERT_EQ(test1.find(5)->second, "test5"); + ASSERT_EQ(test1.find(1)->second, "test1"); + ASSERT_EQ(test1.find(999)->second, "test999"); + + test1.erase(1); + ASSERT_EQ(test1.find(1), test1.cend()); + test1.emplace(87, "test87"); + ASSERT_EQ(test1.find(87)->second, "test87"); + + test1.emplace(78, "test78"); + ASSERT_EQ(test1.find(78)->second, "test78"); + + ASSERT_EQ(test1.find(5)->second, "test5"); + ASSERT_EQ(test1.find(999)->second, "test999"); + test1.erase(5); + test1.erase(999); + ASSERT_EQ(test1.find(5)->second, "test5"); + ASSERT_EQ(test1.find(999)->second, "test999"); + test1.erase(5); + test1.erase(999); + ASSERT_EQ(test1.find(5), test1.end()); + ASSERT_EQ(test1.find(999), test1.end()); + + auto r1 = test1.emplace(999, "test9991"); + ASSERT_TRUE(r1.second); + ASSERT_EQ(r1.first->second, "test9991"); + auto r2 = test1.emplace(999, "test9992"); + ASSERT_TRUE(r2.second); + ASSERT_EQ(r2.first->second, "test9992"); + auto r3 = test1.emplace(999, "test9993"); + ASSERT_TRUE(r3.second); + ASSERT_EQ(r3.first->second, "test9993"); + + test1.erase(r2.first); + ASSERT_TRUE(r1.second); + ASSERT_EQ(r1.first->second, "test9991"); + ASSERT_TRUE(r3.second); + ASSERT_EQ(r3.first->second, "test9993"); }