delete lists
This commit is contained in:
@ -1,102 +0,0 @@
|
||||
#include "user/demo/ArrayListDemo.h"
|
||||
|
||||
void ArrayListDemo::run() {
|
||||
kout.lock();
|
||||
kout.clear();
|
||||
kout << "Initial list size: " << dec << this->list.size() << endl;
|
||||
|
||||
kout << "Adding elements in order" << endl;
|
||||
for (unsigned int i = 0; i < 5; ++i) {
|
||||
this->list.insert_last(i);
|
||||
}
|
||||
this->list.print(kout);
|
||||
|
||||
kout << "Removing all elements from the front" << endl;
|
||||
for (unsigned int i = 0; i < 5; ++i) {
|
||||
this->list.remove_first();
|
||||
}
|
||||
this->list.print(kout);
|
||||
|
||||
// ============================================================
|
||||
|
||||
kout << "Adding elements in order with realloc" << endl;
|
||||
for (unsigned int i = 0; i < 10; ++i) {
|
||||
kout << "Add " << dec << i << endl;
|
||||
this->list.insert_last(i);
|
||||
}
|
||||
this->list.print(kout);
|
||||
|
||||
kout << "Removing all elements from the back" << endl;
|
||||
for (unsigned int i = 0; i < 10; ++i) {
|
||||
this->list.remove_last();
|
||||
}
|
||||
this->list.print(kout);
|
||||
|
||||
// ============================================================
|
||||
|
||||
for (unsigned int i = 0; i < 5; ++i) {
|
||||
this->list.insert_last(i);
|
||||
}
|
||||
this->list.print(kout);
|
||||
|
||||
kout << "Adding inside the list (at idx 0, 2, 5)" << endl;
|
||||
this->list.insert_at(10, 0);
|
||||
this->list.insert_at(10, 2);
|
||||
this->list.insert_at(10, 5);
|
||||
this->list.print(kout);
|
||||
|
||||
kout << "Removing inside the list (at idx 0, 2, 5)" << endl;
|
||||
this->list.remove_at(0);
|
||||
this->list.remove_at(2);
|
||||
this->list.remove_at(5);
|
||||
this->list.print(kout);
|
||||
|
||||
for (unsigned int i = 0; i < 5; ++i) {
|
||||
this->list.remove_first();
|
||||
}
|
||||
this->list.print(kout);
|
||||
|
||||
// ============================================================
|
||||
|
||||
kout << "Mirror scheduling behavior" << endl;
|
||||
|
||||
// These are the threads
|
||||
int active = 0; // Idle thread
|
||||
this->list.insert_last(1);
|
||||
this->list.insert_last(2);
|
||||
this->list.insert_last(3);
|
||||
this->list.print(kout);
|
||||
|
||||
kout << "Starting..." << endl;
|
||||
for (unsigned int n = 0; n < 10000; ++n) {
|
||||
this->list.insert_last(active);
|
||||
active = list.remove_first().value_or(-1);
|
||||
|
||||
if (this->list.size() != 3 || active == -1) {
|
||||
kout << "ERROR: Thread went missing" << endl;
|
||||
break;
|
||||
}
|
||||
|
||||
if (n < 5) {
|
||||
this->list.print(kout);
|
||||
}
|
||||
}
|
||||
kout << "Finished." << endl;
|
||||
|
||||
this->list.print(kout);
|
||||
|
||||
// ============================================================
|
||||
|
||||
kout << "Range based for support" << endl;
|
||||
for (int i : this->list) {
|
||||
kout << "List contains element: " << dec << i << endl;
|
||||
}
|
||||
|
||||
kout << "Const iterator" << endl;
|
||||
for (const int i : this->list) {
|
||||
kout << "List contains element: " << dec << i << endl;
|
||||
}
|
||||
|
||||
kout.unlock();
|
||||
scheduler.exit();
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
#ifndef __ArrayListDemo_include__
|
||||
#define __ArrayListDemo_include__
|
||||
|
||||
#include "kernel/Globals.h"
|
||||
#include "kernel/threads/Thread.h"
|
||||
#include "user/lib/ArrayList.h"
|
||||
|
||||
class ArrayListDemo : public Thread {
|
||||
private:
|
||||
ArrayListDemo(const ArrayListDemo& copy) = delete;
|
||||
|
||||
ArrayList<int> list;
|
||||
|
||||
public:
|
||||
ArrayListDemo() {
|
||||
kout << "Initialized ArrayListDemo" << endl;
|
||||
}
|
||||
|
||||
void run() override;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,97 +0,0 @@
|
||||
#include "user/demo/LinkedListDemo.h"
|
||||
|
||||
void LinkedListDemo::run() {
|
||||
kout.lock();
|
||||
kout.clear();
|
||||
kout << "Initial list size: " << dec << this->list.size() << endl;
|
||||
|
||||
kout << "Adding elements in order" << endl;
|
||||
for (unsigned int i = 0; i < 5; ++i) {
|
||||
this->list.insert_last(i);
|
||||
}
|
||||
this->list.print(kout); // BUG: Crash
|
||||
|
||||
kout << "Removing all elements from the front" << endl;
|
||||
for (unsigned int i = 0; i < 5; ++i) {
|
||||
this->list.remove_first();
|
||||
}
|
||||
this->list.print(kout);
|
||||
|
||||
// ============================================================
|
||||
|
||||
// kout << "Adding elements in order with realloc" << endl;
|
||||
// for (unsigned int i = 0; i < 10; ++i) {
|
||||
// kout << "Add " << dec << i << endl;
|
||||
// this->list.insert_last(i);
|
||||
// }
|
||||
// this->list.print(kout);
|
||||
|
||||
// kout << "Removing all elements from the back" << endl;
|
||||
// for (unsigned int i = 0; i < 10; ++i) {
|
||||
// this->list.remove_last();
|
||||
// }
|
||||
// this->list.print(kout);
|
||||
|
||||
// ============================================================
|
||||
|
||||
for (unsigned int i = 0; i < 5; ++i) {
|
||||
this->list.insert_last(i);
|
||||
}
|
||||
this->list.print(kout);
|
||||
|
||||
kout << "Adding inside the list (at idx 0, 2, 5)" << endl;
|
||||
this->list.insert_at(10, 0);
|
||||
this->list.insert_at(10, 2);
|
||||
this->list.insert_at(10, 5);
|
||||
this->list.print(kout);
|
||||
|
||||
kout << "Removing inside the list (at idx 0, 2, 5)" << endl;
|
||||
this->list.remove_at(0);
|
||||
this->list.remove_at(2);
|
||||
this->list.remove_at(5);
|
||||
this->list.print(kout);
|
||||
|
||||
for (unsigned int i = 0; i < 5; ++i) {
|
||||
this->list.remove_first();
|
||||
}
|
||||
this->list.print(kout);
|
||||
|
||||
// ============================================================
|
||||
|
||||
kout << "Mirror scheduling behavior" << endl;
|
||||
|
||||
// These are the threads
|
||||
int active = 0; // Idle thread
|
||||
this->list.insert_last(1);
|
||||
this->list.insert_last(2);
|
||||
this->list.insert_last(3);
|
||||
this->list.print(kout);
|
||||
|
||||
kout << "Starting..." << endl;
|
||||
for (unsigned int n = 0; n < 10000; ++n) {
|
||||
this->list.insert_last(active);
|
||||
active = list.remove_first().value_or(-1);
|
||||
|
||||
if (this->list.size() != 3 || active == -1) {
|
||||
kout << "ERROR: Thread went missing" << endl;
|
||||
break;
|
||||
}
|
||||
|
||||
if (n < 5) {
|
||||
this->list.print(kout);
|
||||
}
|
||||
}
|
||||
kout << "Finished." << endl;
|
||||
|
||||
this->list.print(kout);
|
||||
|
||||
// ============================================================
|
||||
|
||||
kout << "Range based for support" << endl;
|
||||
for (int i : this->list) {
|
||||
kout << "List contains element: " << dec << i << endl;
|
||||
}
|
||||
|
||||
kout.unlock();
|
||||
scheduler.exit();
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
#ifndef __LinkedListDemo_include__
|
||||
#define __LinkedListDemo_include__
|
||||
|
||||
#include "kernel/Globals.h"
|
||||
#include "kernel/threads/Thread.h"
|
||||
#include "user/lib/LinkedList.h"
|
||||
|
||||
class LinkedListDemo : public Thread {
|
||||
private:
|
||||
LinkedListDemo(const LinkedListDemo& copy) = delete;
|
||||
|
||||
LinkedList<int> list;
|
||||
|
||||
public:
|
||||
LinkedListDemo() {
|
||||
kout << "Initialized LinkedListDemo" << endl;
|
||||
}
|
||||
|
||||
void run() override;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,273 +0,0 @@
|
||||
#ifndef __ARRAYLIST_INCLUDE_H_
|
||||
#define __ARRAYLIST_INCLUDE_H_
|
||||
|
||||
// NOTE: I decided to implement this because I wanted some sort of dynamic array (for example for the keyeventmanager).
|
||||
// Also I wanted to template the Queue (for the scheduler) but with this I can just replace the Queue and use the
|
||||
// ArrayList instead, without additional effort.
|
||||
|
||||
#include "user/lib/List.h"
|
||||
#include "user/lib/Logger.h"
|
||||
#include "user/lib/mem/UniquePointer.h"
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
// I put most of the implementation in the header because the templating makes it cumbersome to split
|
||||
|
||||
template<typename T>
|
||||
class ArrayList : public List<T> {
|
||||
public:
|
||||
using Type = typename List<T>::Type; // Use this just in case T changes from the List type
|
||||
using Iterator = typename List<T>::Iterator;
|
||||
|
||||
private:
|
||||
static constexpr const std::size_t default_cap = 10; // Arbitrary but very small because this isn't a real OS :(
|
||||
static constexpr const std::size_t min_cap = 5; // Slots to allocate extra when array full
|
||||
|
||||
Type* buf = nullptr; // Heap allocated as size needs to change during runtime
|
||||
// Can't use Array for the same reason so we use a C Style array
|
||||
std::size_t buf_pos = 0;
|
||||
std::size_t buf_cap = 0;
|
||||
|
||||
void init() {
|
||||
buf = new Type[ArrayList::default_cap];
|
||||
buf_cap = ArrayList::default_cap;
|
||||
}
|
||||
|
||||
std::size_t get_rem_cap() const {
|
||||
return buf_cap - size();
|
||||
}
|
||||
|
||||
// Enlarges the buffer if we run out of space
|
||||
std::size_t expand() {
|
||||
// Init if necessary
|
||||
if (buf == nullptr) {
|
||||
init();
|
||||
return buf_cap; // Dont have to realloc after init
|
||||
}
|
||||
|
||||
// Since we only ever add single elements this should never get below zero
|
||||
if (get_rem_cap() < min_cap) {
|
||||
std::size_t new_cap = buf_cap + min_cap;
|
||||
|
||||
// Alloc new array
|
||||
Type* new_buf = new Type[new_cap];
|
||||
|
||||
// Swap current elements to new array
|
||||
for (std::size_t i = 0; i < size(); ++i) {
|
||||
new_buf[i] = std::move(buf[i]);
|
||||
buf[i].~Type();
|
||||
}
|
||||
|
||||
// Move new array to buf, deleting the old array
|
||||
delete[] buf;
|
||||
buf = new_buf;
|
||||
buf_cap = new_cap;
|
||||
}
|
||||
|
||||
return buf_cap;
|
||||
}
|
||||
|
||||
// unsigned int shrink {}
|
||||
|
||||
// Returns new pos, both do element copying if necessary, -1 if failed
|
||||
// Index is location where space should be made/removed
|
||||
std::size_t copy_right(std::size_t i) {
|
||||
if (i > size()) {
|
||||
// Error: No elements here
|
||||
return -1;
|
||||
}
|
||||
|
||||
expand();
|
||||
|
||||
// Otherwise i == pos and we don't need to copy anything
|
||||
if (i < size()) {
|
||||
// Enough space to copy elements after pos i
|
||||
// Copy to the right to make space
|
||||
//
|
||||
// [0 1 2 3 _], expand(0) => [_ 0 1 2 3 _]
|
||||
// ^ | |
|
||||
// [0 1 2 3 _], expand(1) => [0 _ 1 2 3 _]
|
||||
// ^ | |
|
||||
// pos = 4 pos = 5
|
||||
for (std::size_t idx = size(); idx > i; --idx) { // idx > i so idx - 1 is never < 0
|
||||
buf[idx] = std::move(buf[idx - 1]);
|
||||
buf[idx - 1].~Type();
|
||||
}
|
||||
|
||||
// Only change pos if elements were copied
|
||||
++buf_pos;
|
||||
}
|
||||
|
||||
return size();
|
||||
}
|
||||
|
||||
// Don't realloc here, we don't need to shring the buffer every time
|
||||
// One could introduce a limit of free space but I don't care for now
|
||||
// Would be bad if the scheduler triggers realloc everytime a thread is removed (if used as readyqueue)...
|
||||
std::size_t copy_left(std::size_t i) {
|
||||
if (i >= size()) {
|
||||
// Error: No elements here
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Decrement before loop because we overwrite 1 element (1 copy less than expand)
|
||||
--buf_pos;
|
||||
|
||||
// [0 1 2 3 _], shrink(1) => [0 2 3 _]
|
||||
// ^ | |
|
||||
// pos = 3 pos = 2
|
||||
for (std::size_t idx = i; idx < size(); ++idx) { // idx < pos so idx + 1 is never outside of size limit
|
||||
buf[idx] = std::move(buf[idx + 1]);
|
||||
buf[idx + 1].~Type();
|
||||
}
|
||||
|
||||
return size();
|
||||
}
|
||||
|
||||
public:
|
||||
~ArrayList() {
|
||||
for (std::size_t i; i < size(); ++i) {
|
||||
buf[i].~Type();
|
||||
}
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
Iterator begin() override {
|
||||
return Iterator(&buf[0]);
|
||||
}
|
||||
|
||||
Iterator end() override {
|
||||
return Iterator(&buf[size()]);
|
||||
}
|
||||
|
||||
// Returns new pos
|
||||
// NOTE: Insert copies
|
||||
std::size_t insert_at(Type e, std::size_t i) override {
|
||||
if (i > size()) {
|
||||
// Error: Space between elements
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (i == size()) {
|
||||
// Insert at end
|
||||
return insert_last(std::forward<Type>(e));
|
||||
}
|
||||
|
||||
if constexpr (std::is_copy_assignable_v<Type>) {
|
||||
copy_right(i); // Changes pos
|
||||
buf[i] = e;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return size();
|
||||
}
|
||||
|
||||
std::size_t insert_first(Type e) override {
|
||||
return insert_at(std::forward<Type>(e), 0);
|
||||
}
|
||||
|
||||
std::size_t insert_last(Type e) override {
|
||||
if (!buf) {
|
||||
init();
|
||||
}
|
||||
|
||||
if constexpr (std::is_copy_assignable_v<Type>) {
|
||||
buf[size()] = e;
|
||||
++buf_pos;
|
||||
expand();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return size();
|
||||
}
|
||||
|
||||
// Returns removed element
|
||||
// NOTE: Remove moves
|
||||
std::optional<Type> remove_at(std::size_t i) override {
|
||||
if (i >= size()) {
|
||||
// ERROR: No element here
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Move moves the data if possible, copies otherwise
|
||||
// It does not destroy the original object, an intact
|
||||
// invariant is left so we have to destruct the old object
|
||||
Type e = std::move(buf[i]);
|
||||
buf[i].~Type(); // Cleanup the leftovers from the move
|
||||
|
||||
copy_left(i);
|
||||
return e;
|
||||
}
|
||||
|
||||
std::optional<Type> remove_first() override {
|
||||
return remove_at(0);
|
||||
}
|
||||
|
||||
std::optional<Type> remove_last() override {
|
||||
// If index -1 unsigned int will overflow and remove_at will catch that
|
||||
return remove_at(size() - 1);
|
||||
}
|
||||
|
||||
// Returns true on success
|
||||
bool remove(Type e) override {
|
||||
for (std::size_t i = 0; i < size(); ++i) {
|
||||
if (buf[i] == e) {
|
||||
copy_left(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: All gets should be optional references (c++20)
|
||||
std::optional<Type> get(std::size_t i) const override {
|
||||
if (i >= size()) {
|
||||
// ERROR: No element there
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// TODO: assignable or constructable?
|
||||
if constexpr (std::is_copy_assignable_v<Type>) {
|
||||
return buf[i];
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<Type> first() const override {
|
||||
return get(0);
|
||||
}
|
||||
|
||||
std::optional<Type> last() const override {
|
||||
return get(size() - 1); // Underflow gets catched by get(unsigned int i)
|
||||
}
|
||||
|
||||
bool empty() const override {
|
||||
return !size();
|
||||
}
|
||||
|
||||
std::size_t size() const override {
|
||||
return buf_pos;
|
||||
}
|
||||
|
||||
void print(OutStream& out) const override {
|
||||
// Our stream cannot print all types so enable this only for debugging purposes (only int)
|
||||
if constexpr (std::is_same_v<Type, int>) {
|
||||
if (empty()) {
|
||||
out << "Print List (0 elements)" << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
out << "Print List (" << dec << size() << " elements): ";
|
||||
for (std::size_t i = 0; i < size(); ++i) {
|
||||
out << dec << buf[i] << " ";
|
||||
}
|
||||
out << endl;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@ -1,306 +0,0 @@
|
||||
#ifndef __LinkedLIST_INCLUDE_H_
|
||||
#define __LinkedLIST_INCLUDE_H_
|
||||
|
||||
#include "user/lib/List.h"
|
||||
#include <cstddef>
|
||||
|
||||
template<typename T>
|
||||
class Wrapper {
|
||||
private:
|
||||
T value = NULL;
|
||||
|
||||
public:
|
||||
Wrapper(T value) : value(value) {}
|
||||
|
||||
Wrapper<T>* next = nullptr;
|
||||
Wrapper<T>* prev = nullptr;
|
||||
|
||||
// Allow conversion to make the ListIterator dereferencing work
|
||||
operator T() {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
// Implement linked traversal by extending the ListIterator
|
||||
template<typename T>
|
||||
class LinkedListIterator : public Iterator<T> {
|
||||
public:
|
||||
using Type = typename Iterator<T>::Type;
|
||||
|
||||
LinkedListIterator(Type* ptr) : Iterator<T>(ptr) {}
|
||||
|
||||
// Allow the iterator to traverse the links
|
||||
LinkedListIterator& operator++() override {
|
||||
// ptr is of type Wrapper<T>*
|
||||
this->ptr = this->ptr->next;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class LinkedList : public List<T, LinkedListIterator<Wrapper<T>>> {
|
||||
public:
|
||||
// Type is T
|
||||
using Type = typename List<T, LinkedListIterator<Wrapper<T>>>::Type; // T is different from the List type (Wrapper<T>)
|
||||
using Iterator = typename List<T, LinkedListIterator<Wrapper<T>>>::Iterator;
|
||||
using WrapperType = typename Iterator::Type;
|
||||
|
||||
private:
|
||||
std::size_t num_elements = 0;
|
||||
WrapperType* head = nullptr;
|
||||
WrapperType* tail = nullptr;
|
||||
|
||||
std::optional<WrapperType*> get_wrapper(std::size_t i) {
|
||||
WrapperType* current = head;
|
||||
std::size_t pos = 0;
|
||||
while (current != nullptr) {
|
||||
if (pos == i) {
|
||||
return current;
|
||||
}
|
||||
|
||||
current = current->next;
|
||||
pos = pos + 1;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
public:
|
||||
~LinkedList() {
|
||||
WrapperType* current = head;
|
||||
WrapperType* next;
|
||||
|
||||
while (current != nullptr) {
|
||||
next = current->next;
|
||||
((Type)*current).~Type(); // Object
|
||||
delete current; // Wrapper
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
Iterator begin() override {
|
||||
return Iterator(head);
|
||||
}
|
||||
|
||||
Iterator end() override {
|
||||
return Iterator(tail->next);
|
||||
}
|
||||
|
||||
// NOTE: Insert copies
|
||||
std::size_t insert_at(Type e, std::size_t i) override {
|
||||
if (i > size()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
return insert_first(std::forward<Type>(e));
|
||||
}
|
||||
|
||||
if (i == size()) {
|
||||
return insert_last(std::forward<Type>(e));
|
||||
}
|
||||
|
||||
WrapperType* old_e = get_wrapper(i).value_or(nullptr);
|
||||
if (old_e == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
WrapperType* new_e = new WrapperType(e);
|
||||
|
||||
new_e->prev = old_e->prev;
|
||||
new_e->next = old_e;
|
||||
new_e->prev->next = new_e;
|
||||
new_e->next->prev = new_e;
|
||||
|
||||
num_elements = num_elements + 1;
|
||||
return size();
|
||||
}
|
||||
|
||||
std::size_t insert_first(Type e) override {
|
||||
WrapperType* old_head = head;
|
||||
head = new WrapperType(e);
|
||||
|
||||
head->prev = nullptr;
|
||||
head->next = old_head;
|
||||
if (old_head != nullptr) {
|
||||
old_head->prev = head;
|
||||
}
|
||||
|
||||
if (tail == nullptr) {
|
||||
tail = head;
|
||||
}
|
||||
|
||||
num_elements = num_elements + 1;
|
||||
return size();
|
||||
}
|
||||
|
||||
std::size_t insert_last(Type e) override {
|
||||
WrapperType* old_tail = tail;
|
||||
tail = new WrapperType(e);
|
||||
|
||||
tail->next = nullptr;
|
||||
tail->prev = old_tail;
|
||||
if (old_tail != nullptr) {
|
||||
old_tail->next = tail;
|
||||
}
|
||||
|
||||
if (head == nullptr) {
|
||||
head = tail;
|
||||
}
|
||||
|
||||
num_elements = num_elements + 1;
|
||||
return size();
|
||||
}
|
||||
|
||||
// NOTE: Remove moves
|
||||
std::optional<Type> remove_at(std::size_t i) override {
|
||||
if (empty() || i >= size()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
return remove_first();
|
||||
}
|
||||
|
||||
if (i == size() - 1) {
|
||||
return remove_last();
|
||||
}
|
||||
|
||||
WrapperType* e = get_wrapper(i).value_or(nullptr);
|
||||
if (e == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Type ret = std::move(*e);
|
||||
((Type)*e).~Type(); // Cleanup rest of the move
|
||||
|
||||
// Remove the wrapper
|
||||
e->next->prev = e->prev;
|
||||
e->prev->next = e->next;
|
||||
delete e;
|
||||
|
||||
num_elements = num_elements - 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::optional<Type> remove_first() override {
|
||||
if (empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Type e = std::move(*head);
|
||||
((Type)*head).~Type(); // Cleanup rest of the move
|
||||
|
||||
// Remove wrapper
|
||||
WrapperType* old_head = head;
|
||||
head = head->next;
|
||||
if (head != nullptr) {
|
||||
head->prev = nullptr;
|
||||
} else {
|
||||
tail = nullptr;
|
||||
}
|
||||
delete old_head;
|
||||
|
||||
num_elements = num_elements - 1;
|
||||
return e;
|
||||
}
|
||||
|
||||
std::optional<Type> remove_last() override {
|
||||
if (empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Type e = std::move(*tail);
|
||||
((Type)*tail).~Type(); // Cleanup rest of the move
|
||||
|
||||
// Remove wrapper
|
||||
WrapperType* old_tail = tail;
|
||||
tail = tail->prev;
|
||||
if (tail != nullptr) {
|
||||
tail->next = nullptr;
|
||||
} else {
|
||||
head = nullptr;
|
||||
}
|
||||
delete old_tail;
|
||||
|
||||
num_elements = num_elements - 1;
|
||||
return e;
|
||||
}
|
||||
|
||||
bool remove(Type e) override {
|
||||
std::size_t pos = 0;
|
||||
WrapperType* wrapper = head;
|
||||
while (wrapper != nullptr) {
|
||||
if (*wrapper == e) {
|
||||
return remove_at(pos).has_value();
|
||||
}
|
||||
|
||||
wrapper = wrapper->next;
|
||||
pos++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<Type> get(std::size_t i) const override {
|
||||
if (i >= size()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
return first();
|
||||
}
|
||||
if (i == size() - 1) {
|
||||
return last();
|
||||
}
|
||||
|
||||
WrapperType* wrapper = head;
|
||||
for (std::size_t pos = 0; pos < i; ++pos) {
|
||||
wrapper = wrapper->next;
|
||||
}
|
||||
return *wrapper;
|
||||
}
|
||||
|
||||
std::optional<Type> first() const override {
|
||||
if (empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return *head;
|
||||
}
|
||||
|
||||
std::optional<Type> last() const override {
|
||||
if (empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return *tail;
|
||||
}
|
||||
|
||||
bool empty() const override {
|
||||
return !size();
|
||||
}
|
||||
std::size_t size() const override {
|
||||
return num_elements;
|
||||
}
|
||||
|
||||
void print(OutStream& out) const override {
|
||||
// Our stream cannot print all types so enable this only for debugging purposes (only int)
|
||||
if constexpr (std::is_same_v<Type, int>) {
|
||||
if (empty()) {
|
||||
out << "Print List (0 elements)" << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
out << "Print List (" << dec << size() << " elements): ";
|
||||
typename Iterator::Type* current = head;
|
||||
while (current != nullptr) {
|
||||
out << dec << *current << " ";
|
||||
current = current->next;
|
||||
}
|
||||
out << endl;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@ -1,50 +0,0 @@
|
||||
#ifndef __LIST_INCLUDE_H_
|
||||
#define __LIST_INCLUDE_H_
|
||||
|
||||
#include "lib/OutStream.h"
|
||||
#include "user/lib/Iterator.h"
|
||||
#include <optional>
|
||||
|
||||
// Define the list interface for ArrayList/LinkedList implementations with support for Iterators/ranged based for loops
|
||||
|
||||
// NOTE: Can't be used with types that are not movable
|
||||
// Also if used with unique_ptr the get methods can't be used as it deletes the pointer inside the list
|
||||
|
||||
template<typename T, typename I = Iterator<T>>
|
||||
class List {
|
||||
public:
|
||||
using Type = T; // We make the template argument accessible from the subclasses
|
||||
using Iterator = I; // Needed for range based for loop
|
||||
|
||||
// Iterator
|
||||
virtual Iterator begin() = 0;
|
||||
virtual Iterator end() = 0;
|
||||
constexpr Iterator begin() const { return this->begin(); }
|
||||
constexpr Iterator end() const { return this->end(); }
|
||||
|
||||
// Insert, should return 0 on fail
|
||||
// NOTE: Copies an element into the structure
|
||||
virtual std::size_t insert_at(Type e, unsigned int i) = 0;
|
||||
virtual std::size_t insert_first(Type e) = 0;
|
||||
virtual std::size_t insert_last(Type e) = 0;
|
||||
|
||||
// Remove
|
||||
// NOTE: Moves an element out of the structure
|
||||
virtual std::optional<Type> remove_at(std::size_t i) = 0;
|
||||
virtual std::optional<Type> remove_first() = 0;
|
||||
virtual std::optional<Type> remove_last() = 0;
|
||||
virtual bool remove(Type e) = 0;
|
||||
|
||||
// Get
|
||||
// TODO: Return c++20 optional references
|
||||
virtual std::optional<Type> get(std::size_t i) const = 0;
|
||||
virtual std::optional<Type> first() const = 0;
|
||||
virtual std::optional<Type> last() const = 0;
|
||||
|
||||
// Misc
|
||||
virtual bool empty() const = 0;
|
||||
virtual std::size_t size() const = 0;
|
||||
virtual void print(OutStream& out) const = 0;
|
||||
};
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user