diff options
-rw-r--r-- | raul/Makefile.am | 2 | ||||
-rw-r--r-- | raul/Table.hpp | 97 | ||||
-rw-r--r-- | raul/TableImpl.hpp | 166 | ||||
-rw-r--r-- | tests/Makefile.am | 5 | ||||
-rw-r--r-- | tests/atomic_test.cpp | 4 | ||||
-rw-r--r-- | tests/list_test.cpp | 2 | ||||
-rw-r--r-- | tests/midi_ringbuffer_test.cpp | 2 | ||||
-rw-r--r-- | tests/path_test.cpp | 2 | ||||
-rw-r--r-- | tests/quantize_test.cpp | 2 | ||||
-rw-r--r-- | tests/queue_test.cpp | 8 | ||||
-rw-r--r-- | tests/rdf_test.cpp | 4 | ||||
-rw-r--r-- | tests/ringbuffer_test.cpp | 2 | ||||
-rw-r--r-- | tests/smf_test.cpp | 4 | ||||
-rw-r--r-- | tests/thread_test.cpp | 2 | ||||
-rw-r--r-- | tests/time_test.cpp | 2 |
15 files changed, 286 insertions, 18 deletions
diff --git a/raul/Makefile.am b/raul/Makefile.am index 77df449..7f47a50 100644 --- a/raul/Makefile.am +++ b/raul/Makefile.am @@ -29,6 +29,8 @@ raulinclude_HEADERS = \ Slave.hpp \ StampedChunkRingBuffer.hpp \ Stateful.hpp \ + Table.hpp \ + TableImpl.hpp \ Thread.hpp \ TimeSlice.hpp \ WeakPtr.hpp \ diff --git a/raul/Table.hpp b/raul/Table.hpp new file mode 100644 index 0000000..b51bfe2 --- /dev/null +++ b/raul/Table.hpp @@ -0,0 +1,97 @@ +/* This file is part of Raul. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Raul is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Raul is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef RAUL_TABLE_HPP +#define RAUL_TABLE_HPP + +#include <vector> +#include <algorithm> + +namespace Raul { + + +/** Slow insertion, fast lookup, cache optimized, super fast sorted iteration. + * + * This has the advantage over std::map that iterating over the collection + * is super fast, and iteration is sorted. Probably also faster due to all + * data being in a single chunk of memory, cache optimized, etc. + */ +template <typename K, typename T> +class Table { +public: + Table<K, T>() : _size(0), _array(NULL) {} + ~Table<K, T>() { free(_array); } + + void clear() { free(_array); _array = NULL; _size = 0; } + + typedef std::pair<K,T> Entry; + + struct const_iterator { + const_iterator(const Table<K,T>& t, size_t i) : _table(t), _index(i) {} + inline const Entry& operator*() const { return _table._array[_index]; } + inline const Entry* operator->() const { return &_table._array[_index]; } + inline const_iterator& operator++() { ++_index; return *this; } + inline bool operator==(const const_iterator& i) const { return _index == i._index; } + inline bool operator!=(const const_iterator& i) const { return _index != i._index; } + private: + const Table<K,T>& _table; + size_t _index; + }; + + struct iterator { + iterator(Table<K,T>& t, size_t i) : _table(t), _index(i) {} + inline Entry& operator*() const { return _table._array[_index]; } + inline Entry* operator->() const { return &_table._array[_index]; } + inline iterator& operator++() { ++_index; return *this; } + inline bool operator==(const iterator& i) const { return _index == i._index; } + inline bool operator!=(const iterator& i) const { return _index != i._index; } + operator const_iterator() { return *(const_iterator*)this; } + private: + Table<K,T>& _table; + size_t _index; + }; + + inline size_t size() const { return _size; } + + void insert(K key, T value); + void remove(K& key); + void remove(iterator i); + + const_iterator find(const K& key) const; + iterator find(const K& key); + + const_iterator begin() const { return const_iterator(*this, 0); } + const_iterator end() const { return const_iterator(*this, size()); } + iterator begin() { return iterator(*this, 0); } + iterator end() { return iterator(*this, size()); } + +private: +#ifndef NDEBUG + bool is_sorted() const; +#endif + + friend class iterator; + + + size_t _size; + Entry* _array; +}; + + +} // namespace Raul + +#endif // RAUL_TABLE_HPP diff --git a/raul/TableImpl.hpp b/raul/TableImpl.hpp new file mode 100644 index 0000000..bac948e --- /dev/null +++ b/raul/TableImpl.hpp @@ -0,0 +1,166 @@ +/* This file is part of Raul. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Raul is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Raul is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <cassert> +#include <stdexcept> +#include <algorithm> +#include <iostream> +#include <raul/Table.hpp> + +namespace Raul { + +#ifndef NDEBUG +template <typename K, typename T> +bool +Table<K,T>::is_sorted() const +{ + if (_size <= 1) + return true; + + K prev_key = _array[0].first; + + for (size_t i=1; i < _size; ++i) + if (_array[i].first < prev_key) + return false; + else + prev_key = _array[i].first; + + return true; +} +#endif + + +/** Binary search (O(log(n))) */ +template <typename K, typename T> +typename Table<K,T>::iterator +Table<K,T>::find(const K& key) +{ + if (_size == 0) + return end(); + + size_t lower = 0; + size_t upper = _size - 1; + size_t i; + + while (upper >= lower) { + i = lower + ((upper - lower) / 2); + Entry& elem = _array[i]; + + if (elem.first == key) + return iterator(*this, i); + else if (i < _size-1 && elem.first < key) + lower = i + 1; + else if (i > 0) + upper = i - 1; + else + break; + } + + return end(); +} + + +template <typename K, typename T> +void +Table<K,T>::insert(K key, T value) +{ + Entry new_entry = make_pair(key, value); + + if (_size == 0) { + _array = (std::pair<K, T>*)malloc(sizeof(Entry) * 2); + _array[0] = new_entry; + ++_size; + return; + } else if (_size == 1) { + if (key > _array[0].first) { + _array[1] = new_entry; + } else { + _array[1] = _array[0]; + _array[0] = new_entry; + } + ++_size; + return; + } + + size_t lower = 0; + size_t upper = _size - 1; + size_t i; + + // Find the earliest element > key + while (upper >= lower) { + i = lower + ((upper - lower) / 2); + assert(i >= lower); + assert(i <= upper); + assert(_array[lower].first <= _array[i].first); + assert(_array[i].first <= _array[upper].first); + + assert(i < _size); + Entry& elem = _array[i]; + + if (elem.first == key) { + break; + } else if (elem.first > key) { + if (i == 0 || _array[i-1].first < key) + break; + upper = i - 1; + } else { + lower = i + 1; + } + } + + // Lil' off by one touchup :) + if (i < _size && _array[i].first <= key) + ++i; + + _array = (Entry*)realloc(_array, (_size + 1) * sizeof(Entry)); + + // FIXME: This could be faster using memmove etc... + for (size_t j=_size; j > i; --j) + _array[j] = _array[j-1]; + + _array[i] = new_entry; + + ++_size; + + assert(is_sorted()); +} + + +template <typename K, typename T> +void +Table<K,T>::remove(K& key) +{ + iterator i = find(key); + if (i != _array.end()) + remove(i); +} + + +template <typename K, typename T> +void +Table<K,T>::remove(iterator i) +{ + _array.erase(i); + +#ifndef NDEBUG + assert(_array.is_sorted()); +#endif +} + + +} // namespace Raul + diff --git a/tests/Makefile.am b/tests/Makefile.am index 27a8f1a..ae3db13 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -14,7 +14,8 @@ noinst_PROGRAMS = \ time_test \ quantize_test \ smf_test \ - rdf_test + rdf_test \ + table_test thread_test_LDADD = $(ALL_LIBS) path_test_LDADD = $(ALL_LIBS) @@ -27,6 +28,7 @@ time_test_LDADD = $(ALL_LIBS) quantize_test_LDADD = $(ALL_LIBS) smf_test_LDADD = $(ALL_LIBS) rdf_test_LDADD = $(ALL_LIBS) +table_test_LDADD = $(ALL_LIBS) path_test_SOURCES = path_test.cpp thread_test_SOURCES = thread_test.cpp @@ -39,5 +41,6 @@ time_test_SOURCES = time_test.cpp quantize_test_SOURCES = quantize_test.cpp smf_test_SOURCES = smf_test.cpp rdf_test_SOURCES = rdf_test.cpp +table_test_SOURCES = table_test.cpp endif diff --git a/tests/atomic_test.cpp b/tests/atomic_test.cpp index c10677a..caa6395 100644 --- a/tests/atomic_test.cpp +++ b/tests/atomic_test.cpp @@ -1,6 +1,6 @@ #include <iostream> -#include <raul/AtomicInt.h> -#include <raul/AtomicPtr.h> +#include <raul/AtomicInt.hpp> +#include <raul/AtomicPtr.hpp> using namespace std; using namespace Raul; diff --git a/tests/list_test.cpp b/tests/list_test.cpp index 133067b..d374b40 100644 --- a/tests/list_test.cpp +++ b/tests/list_test.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <cstddef> -#include <raul/List.h> +#include <raul/List.hpp> using namespace std; using namespace Raul; diff --git a/tests/midi_ringbuffer_test.cpp b/tests/midi_ringbuffer_test.cpp index 5f903d1..706aef1 100644 --- a/tests/midi_ringbuffer_test.cpp +++ b/tests/midi_ringbuffer_test.cpp @@ -1,5 +1,5 @@ #include <iostream> -#include "raul/StampedChunkRingBuffer.h" +#include "raul/StampedChunkRingBuffer.hpp" #include "raul/midi_names.h" using namespace std; diff --git a/tests/path_test.cpp b/tests/path_test.cpp index 280f968..cf62340 100644 --- a/tests/path_test.cpp +++ b/tests/path_test.cpp @@ -1,5 +1,5 @@ #include <iostream> -#include <raul/Path.h> +#include <raul/Path.hpp> using namespace std; using namespace Raul; diff --git a/tests/quantize_test.cpp b/tests/quantize_test.cpp index 434df2e..f79d279 100644 --- a/tests/quantize_test.cpp +++ b/tests/quantize_test.cpp @@ -1,5 +1,5 @@ #include <iostream> -#include <raul/Quantizer.h> +#include <raul/Quantizer.hpp> using namespace std; using namespace Raul; diff --git a/tests/queue_test.cpp b/tests/queue_test.cpp index 0b8bbd3..7a1d44b 100644 --- a/tests/queue_test.cpp +++ b/tests/queue_test.cpp @@ -6,10 +6,10 @@ #include <stdlib.h> #include <fcntl.h> #include <termios.h> -#include "raul/SRSWQueue.h" -#include "raul/SRMWQueue.h" -#include "raul/Thread.h" -#include "raul/AtomicInt.h" +#include "raul/SRSWQueue.hpp" +#include "raul/SRMWQueue.hpp" +#include "raul/Thread.hpp" +#include "raul/AtomicInt.hpp" using namespace std; using namespace Raul; diff --git a/tests/rdf_test.cpp b/tests/rdf_test.cpp index 6e224cf..afb1887 100644 --- a/tests/rdf_test.cpp +++ b/tests/rdf_test.cpp @@ -1,5 +1,5 @@ -#include <raul/RDFModel.h> -#include <raul/RDFQuery.h> +#include <raul/RDFModel.hpp> +#include <raul/RDFQuery.hpp> int main() diff --git a/tests/ringbuffer_test.cpp b/tests/ringbuffer_test.cpp index 01f136f..851d7b9 100644 --- a/tests/ringbuffer_test.cpp +++ b/tests/ringbuffer_test.cpp @@ -1,5 +1,5 @@ #include <iostream> -#include "raul/RingBuffer.h" +#include "raul/RingBuffer.hpp" using namespace std; using namespace Raul; diff --git a/tests/smf_test.cpp b/tests/smf_test.cpp index 3396b90..3ca9715 100644 --- a/tests/smf_test.cpp +++ b/tests/smf_test.cpp @@ -1,7 +1,7 @@ #include <iostream> #include <string> -#include <raul/SMFReader.h> -#include <raul/SMFWriter.h> +#include <raul/SMFReader.hpp> +#include <raul/SMFWriter.hpp> using namespace std; using namespace Raul; diff --git a/tests/thread_test.cpp b/tests/thread_test.cpp index ef3ad53..eb06701 100644 --- a/tests/thread_test.cpp +++ b/tests/thread_test.cpp @@ -1,5 +1,5 @@ #include <iostream> -#include <raul/Thread.h> +#include <raul/Thread.hpp> using namespace std; using namespace Raul; diff --git a/tests/time_test.cpp b/tests/time_test.cpp index 0ffdc61..c5c89b7 100644 --- a/tests/time_test.cpp +++ b/tests/time_test.cpp @@ -1,5 +1,5 @@ #include <iostream> -#include <raul/TimeSlice.h> +#include <raul/TimeSlice.hpp> using namespace std; using namespace Raul; |