mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 00:14:32 -08:00
New templatized linked_list collection
Also updated tests to work with memory changes
This commit is contained in:
253
src/libraries/kutil/linked_list.h
Normal file
253
src/libraries/kutil/linked_list.h
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
#pragma once
|
||||||
|
/// \file linked_list.h
|
||||||
|
/// A generic templatized linked list.
|
||||||
|
|
||||||
|
namespace kutil {
|
||||||
|
|
||||||
|
template <typename T> class linked_list;
|
||||||
|
|
||||||
|
|
||||||
|
/// A list node in a `linked_list<T>` or `sortable_linked_list<T>`.
|
||||||
|
template <typename T>
|
||||||
|
class list_node :
|
||||||
|
public T
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using item_type = T;
|
||||||
|
using node_type = list_node<T>;
|
||||||
|
|
||||||
|
/// Dereference operator. Helper to cast this node to the contained type.
|
||||||
|
/// \returns A pointer to the node, cast to T*.
|
||||||
|
inline item_type & operator*() { return *this; }
|
||||||
|
|
||||||
|
/// Dereference operator. Helper to cast this node to the contained type.
|
||||||
|
/// \returns A pointer to the node, cast to T*.
|
||||||
|
inline const item_type & operator*() const { return *this; }
|
||||||
|
|
||||||
|
/// Cast operator. Helper to cast this node to the contained type.
|
||||||
|
/// \returns A reference to the node, cast to T&.
|
||||||
|
inline operator item_type& () { return *this; }
|
||||||
|
|
||||||
|
/// Cast operator. Helper to cast this node to the contained type.
|
||||||
|
/// \returns A reference to the node, cast to const T&.
|
||||||
|
inline operator const item_type& () { return *this; }
|
||||||
|
|
||||||
|
/// Accessor for the next pointer.
|
||||||
|
/// \returns The next node in the list
|
||||||
|
inline node_type * next() { return m_next; }
|
||||||
|
|
||||||
|
/// Accessor for the next pointer.
|
||||||
|
/// \returns The next node in the list
|
||||||
|
inline const node_type * next() const { return m_next; }
|
||||||
|
|
||||||
|
/// Accessor for the prev pointer.
|
||||||
|
/// \returns The prev node in the list
|
||||||
|
inline node_type * prev() { return m_prev; }
|
||||||
|
|
||||||
|
/// Accessor for the prev pointer.
|
||||||
|
/// \returns The prev node in the list
|
||||||
|
inline const node_type * prev() const { return m_prev; }
|
||||||
|
|
||||||
|
/// Insert an item after this one in the list.
|
||||||
|
/// \arg item The item to insert
|
||||||
|
void insert_after(node_type *item)
|
||||||
|
{
|
||||||
|
if (m_next) m_next->m_prev = item;
|
||||||
|
item->m_next = m_next;
|
||||||
|
item->m_prev = this;
|
||||||
|
m_next = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert an item before this one in the list.
|
||||||
|
/// \arg item The item to insert
|
||||||
|
void insert_before(node_type *item)
|
||||||
|
{
|
||||||
|
if (m_prev) m_prev->m_next = item;
|
||||||
|
item->m_prev = m_prev;
|
||||||
|
item->m_next = this;
|
||||||
|
m_prev = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove this item from its list.
|
||||||
|
void remove()
|
||||||
|
{
|
||||||
|
if (m_next) m_next->m_prev = m_prev;
|
||||||
|
if (m_prev) m_prev->m_next = m_next;
|
||||||
|
m_next = m_prev = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class linked_list<T>;
|
||||||
|
|
||||||
|
node_type *m_next;
|
||||||
|
node_type *m_prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// An iterator for linked lists
|
||||||
|
template <typename T>
|
||||||
|
class list_iterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using item_type = list_node<T>;
|
||||||
|
|
||||||
|
list_iterator(item_type *item) : m_item(item) {}
|
||||||
|
|
||||||
|
inline T & operator*() { return *m_item; }
|
||||||
|
inline const T & operator*() const { return *m_item; }
|
||||||
|
inline list_iterator & operator++() { m_item = m_item ? m_item->next() : nullptr; return *this; }
|
||||||
|
inline list_iterator operator++(int) { return list_iterator<T>(m_item ? m_item->next() : nullptr); }
|
||||||
|
inline bool operator!=(const list_iterator<T> &other) { return m_item != other.m_item; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
item_type *m_item;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// A templatized doubly-linked list container of `list_node<T>` items.
|
||||||
|
template <typename T>
|
||||||
|
class linked_list
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using item_type = list_node<T>;
|
||||||
|
using iterator = list_iterator<T>;
|
||||||
|
|
||||||
|
/// Constructor. Creates an empty list.
|
||||||
|
linked_list() :
|
||||||
|
m_head(nullptr),
|
||||||
|
m_tail(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// Count the items in the list.
|
||||||
|
/// \returns The number of entries in the list.
|
||||||
|
size_t length()
|
||||||
|
{
|
||||||
|
size_t len = 0;
|
||||||
|
for (item_type *cur = m_head; cur; cur = cur->m_next) ++len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the item at the front of the list, without removing it
|
||||||
|
/// \returns The first item in the list
|
||||||
|
inline item_type * front() { return m_head; }
|
||||||
|
|
||||||
|
/// Get the item at the back of the list, without removing it
|
||||||
|
/// \returns The last item in the list
|
||||||
|
inline item_type * back() { return m_tail; }
|
||||||
|
|
||||||
|
/// Prepend an item to the front of this list.
|
||||||
|
/// \arg item The node to insert.
|
||||||
|
void push_front(item_type *item)
|
||||||
|
{
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!m_head) {
|
||||||
|
m_head = m_tail = item;
|
||||||
|
item->m_next = item->m_prev = nullptr;
|
||||||
|
} else {
|
||||||
|
m_head->m_prev = item;
|
||||||
|
item->m_next = m_head;
|
||||||
|
item->m_prev = nullptr;
|
||||||
|
m_head = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append an item to the end of this list.
|
||||||
|
/// \arg item The node to append.
|
||||||
|
void push_back(item_type *item)
|
||||||
|
{
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!m_tail) {
|
||||||
|
m_head = m_tail = item;
|
||||||
|
item->m_next = item->m_prev = nullptr;
|
||||||
|
} else {
|
||||||
|
m_tail->m_next = item;
|
||||||
|
item->m_prev = m_tail;
|
||||||
|
item->m_next = nullptr;
|
||||||
|
m_tail = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove an item from the front of this list.
|
||||||
|
/// \returns The node that was removed
|
||||||
|
item_type * pop_front()
|
||||||
|
{
|
||||||
|
item_type *item = m_head;
|
||||||
|
if (m_head) {
|
||||||
|
m_head = item->m_next;
|
||||||
|
item->m_next = nullptr;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove an item from the end of this list.
|
||||||
|
/// \returns The node that was removed
|
||||||
|
item_type * pop_back()
|
||||||
|
{
|
||||||
|
item_type *item = m_tail;
|
||||||
|
if (m_tail) {
|
||||||
|
m_tail = item->m_prev;
|
||||||
|
item->m_prev = nullptr;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append the contents of another list to the end of this list. The other
|
||||||
|
/// list is emptied, and this list takes ownership of its items.
|
||||||
|
/// \arg list The other list.
|
||||||
|
void append(linked_list<T> &list)
|
||||||
|
{
|
||||||
|
if (!list.m_head) return;
|
||||||
|
|
||||||
|
if (!m_tail) {
|
||||||
|
m_head = list.m_head;
|
||||||
|
m_tail = list.m_tail;
|
||||||
|
} else {
|
||||||
|
m_tail->m_next = list.m_head;
|
||||||
|
m_tail = list.m_tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.m_head = list.m_tail = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert an item into the list in a sorted position. Depends on T
|
||||||
|
/// having a method `int compare(const T *other)`.
|
||||||
|
/// \arg item The item to insert
|
||||||
|
void sorted_insert(item_type *item)
|
||||||
|
{
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
item_type *cur = m_head;
|
||||||
|
while (cur && item->compare(cur) > 0)
|
||||||
|
cur = cur->m_next;
|
||||||
|
|
||||||
|
if (!cur)
|
||||||
|
push_back(item);
|
||||||
|
else if (cur == m_head)
|
||||||
|
push_front(item);
|
||||||
|
else
|
||||||
|
cur->insert_before(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Range-based for iterator generator.
|
||||||
|
/// \returns An iterator to the beginning of the list
|
||||||
|
inline iterator begin() { return iterator(m_head); }
|
||||||
|
|
||||||
|
/// Range-based for iterator generator.
|
||||||
|
/// \returns A const iterator to the beginning of the list
|
||||||
|
inline const iterator begin() const { return iterator(m_head); }
|
||||||
|
|
||||||
|
/// Range-based for end-iterator generator.
|
||||||
|
/// \returns An iterator to the end of the list
|
||||||
|
inline const iterator end() const { return iterator(nullptr); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
item_type *m_head;
|
||||||
|
item_type *m_tail;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace kutil
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "memory_manager.h"
|
#include "memory_manager.h"
|
||||||
|
#include "type_macros.h"
|
||||||
|
|
||||||
void * operator new (size_t, void *p) noexcept { return p; }
|
__weak void * operator new (size_t, void *p) noexcept { return p; }
|
||||||
void * operator new (size_t n) { return kutil::malloc(n); }
|
__weak void * operator new (size_t n) { return kutil::malloc(n); }
|
||||||
void * operator new[] (size_t n) { return kutil::malloc(n); }
|
__weak void * operator new[] (size_t n) { return kutil::malloc(n); }
|
||||||
void operator delete (void *p) noexcept { return kutil::free(p); }
|
__weak void operator delete (void *p) noexcept { return kutil::free(p); }
|
||||||
void operator delete[] (void *p) noexcept { return kutil::free(p); }
|
__weak void operator delete[] (void *p) noexcept { return kutil::free(p); }
|
||||||
|
|
||||||
namespace kutil {
|
namespace kutil {
|
||||||
|
|
||||||
|
|||||||
3
src/libraries/kutil/type_macros.h
Normal file
3
src/libraries/kutil/type_macros.h
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define __weak __attribute__ ((weak))
|
||||||
123
src/tests/linked_list.cpp
Normal file
123
src/tests/linked_list.cpp
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
|
#include <random>
|
||||||
|
#include <vector>
|
||||||
|
#include "kutil/linked_list.h"
|
||||||
|
#include "catch.hpp"
|
||||||
|
|
||||||
|
using namespace kutil;
|
||||||
|
|
||||||
|
const int test_list_size = 100;
|
||||||
|
|
||||||
|
struct unsortableT {
|
||||||
|
int value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sortableT {
|
||||||
|
int value;
|
||||||
|
int compare(const sortableT *other) {
|
||||||
|
return value - other->value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class ListVectorCompare :
|
||||||
|
public Catch::MatcherBase<std::vector<list_node<T>>>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using item = list_node<T>;
|
||||||
|
using vector = std::vector<item>;
|
||||||
|
|
||||||
|
ListVectorCompare(const linked_list<T> &list, bool reversed) :
|
||||||
|
m_list(list), m_reverse(reversed) {}
|
||||||
|
|
||||||
|
virtual bool match (vector const& vec) const override
|
||||||
|
{
|
||||||
|
size_t index = m_reverse ? vec.size() - 1 : 0;
|
||||||
|
for (const T &i : m_list) {
|
||||||
|
if (&i != &vec[index]) return false;
|
||||||
|
index += m_reverse ? -1 : 1;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual std::string describe() const override
|
||||||
|
{
|
||||||
|
return "is the same as the given linked list";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const linked_list<T> &m_list;
|
||||||
|
bool m_reverse;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class IsSorted :
|
||||||
|
public Catch::MatcherBase<linked_list<T>>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using item = list_node<T>;
|
||||||
|
using list = linked_list<T>;
|
||||||
|
|
||||||
|
IsSorted() {}
|
||||||
|
|
||||||
|
virtual bool match (list const& l) const override
|
||||||
|
{
|
||||||
|
int big = std::numeric_limits<int>::min();
|
||||||
|
for (const T &i : l) {
|
||||||
|
if (i.value < big) return false;
|
||||||
|
big = i.value;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual std::string describe() const override
|
||||||
|
{
|
||||||
|
return "is sorted";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
ListVectorCompare<T> IsSameAsList(const linked_list<T> &list, bool reversed = false)
|
||||||
|
{
|
||||||
|
return ListVectorCompare<T>(list, reversed);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE( "Linked list tests", "[containers list]" )
|
||||||
|
{
|
||||||
|
using clock = std::chrono::system_clock;
|
||||||
|
unsigned seed = clock::now().time_since_epoch().count();
|
||||||
|
std::default_random_engine rng(seed);
|
||||||
|
std::uniform_int_distribution<int> gen(1, 1000);
|
||||||
|
|
||||||
|
linked_list<unsortableT> ulist;
|
||||||
|
|
||||||
|
std::vector<list_node<unsortableT>> unsortables(test_list_size);
|
||||||
|
for (auto &i : unsortables) {
|
||||||
|
i.value = gen(rng);
|
||||||
|
ulist.push_back(&i);
|
||||||
|
}
|
||||||
|
CHECK( ulist.length() == test_list_size );
|
||||||
|
CHECK_THAT( unsortables, IsSameAsList(ulist) );
|
||||||
|
|
||||||
|
linked_list<unsortableT> ulist_reversed;
|
||||||
|
|
||||||
|
for (auto &i : unsortables) {
|
||||||
|
i.remove();
|
||||||
|
ulist_reversed.push_front(&i);
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK( ulist_reversed.length() == test_list_size );
|
||||||
|
CHECK_THAT( unsortables, IsSameAsList(ulist_reversed, true) );
|
||||||
|
|
||||||
|
linked_list<sortableT> slist;
|
||||||
|
|
||||||
|
std::vector<list_node<sortableT>> sortables(test_list_size);
|
||||||
|
for (auto &i : sortables) {
|
||||||
|
i.value = gen(rng);
|
||||||
|
slist.sorted_insert(&i);
|
||||||
|
}
|
||||||
|
CHECK( slist.length() == test_list_size );
|
||||||
|
CHECK_THAT( slist, IsSorted<sortableT>() );
|
||||||
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
#define CATCH_CONFIG_MAIN
|
#define CATCH_CONFIG_MAIN
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
|
||||||
// kutil malloc/free stubs
|
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
namespace kutil {
|
void * operator new (size_t n) { return ::malloc(n); }
|
||||||
void * malloc(size_t n) { return ::malloc(n); }
|
void * operator new[] (size_t n) { return ::malloc(n); }
|
||||||
void free(void *p) { ::free(p); }
|
void operator delete (void *p) noexcept { return ::free(p); }
|
||||||
}
|
void operator delete[] (void *p) noexcept { return ::free(p); }
|
||||||
|
|
||||||
|
|||||||
10
wscript
10
wscript
@@ -203,7 +203,7 @@ def configure(ctx):
|
|||||||
for mod_path in ctx.env.LIBRARIES:
|
for mod_path in ctx.env.LIBRARIES:
|
||||||
ctx.recurse(mod_path)
|
ctx.recurse(mod_path)
|
||||||
|
|
||||||
for mod_path in ctx.env.LIBRARIES:
|
for mod_path in ctx.env.TOOLS:
|
||||||
ctx.recurse(mod_path)
|
ctx.recurse(mod_path)
|
||||||
|
|
||||||
## Image configuration
|
## Image configuration
|
||||||
@@ -225,6 +225,7 @@ def configure(ctx):
|
|||||||
|
|
||||||
for mod_path in ctx.env.LIBRARIES:
|
for mod_path in ctx.env.LIBRARIES:
|
||||||
ctx.recurse(mod_path)
|
ctx.recurse(mod_path)
|
||||||
|
|
||||||
ctx.recurse(join("src", "tests"))
|
ctx.recurse(join("src", "tests"))
|
||||||
|
|
||||||
|
|
||||||
@@ -358,6 +359,13 @@ def build(bld):
|
|||||||
copy_part.set_outputs([disk])
|
copy_part.set_outputs([disk])
|
||||||
bld.add_to_group(copy_part)
|
bld.add_to_group(copy_part)
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
#
|
||||||
|
elif bld.variant == 'tests':
|
||||||
|
for mod_path in bld.env.LIBRARIES:
|
||||||
|
bld.recurse(mod_path)
|
||||||
|
bld.recurse(join("src", "tests"))
|
||||||
|
|
||||||
|
|
||||||
def test(bld):
|
def test(bld):
|
||||||
from os.path import join
|
from os.path import join
|
||||||
|
|||||||
Reference in New Issue
Block a user