From: hama Date: Mon, 15 Aug 2016 22:14:37 +0000 (+0200) Subject: dictionary added X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=191662bef47fbc76bdb7bebaef176717a2a94e57;p=cpidjinn dictionary added * Dictionary, CStringCString, CStringInt added * ItemFactory extracted to its own file --- diff --git a/Makefile b/Makefile index a2acf37..b20370a 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,31 @@ -CC=gcc -CFLAGS="-Wall" +CC = g++ +#CC = clang +CFLAGS = "-Wall" +LDFLAGS = -lm -lpthread +CL = g++ +#CL = clang -debug:clean - $(CC) $(CFLAGS) -g -o cpidjinn main.c -stable:clean - $(CC) $(CFLAGS) -o cpidjinn main.c +OBJ = cuarraylist.o cutimeutils.o cutimer.o cudynbuffer.o \ + itemfactory.o dictionary.o arraylist.o dynbuffer.o logger.o timeutils.o \ + unittest.o test.o thread.o \ + timer.o +PROG = util/test + +all : $(PROG) +unittest : $(PROG) + +$(PROG): $(OBJ) + cd util ; $(CC) $(LDFLAGS) -g -o $(PROG) $(OBJ) + +%.o: %.cpp + $(CC) $(CFLAGS) -g -c $< + +debug: all + cd util ; $(CC) $(LDFLAGS) -g -o $(PROG) $(OBJ) +stable: all + cd util ; $(CL) $(LDFLAGS) -o $(PROG) $(OBJ) + +.PHONY: clean clean: - rm -vfr *~ cpidjinn + cd util ; rm -vfr *~ $(PROG) $(OBJ) + diff --git a/util/Makefile b/util/Makefile index 0f09be7..9f2d177 100644 --- a/util/Makefile +++ b/util/Makefile @@ -5,9 +5,9 @@ LDFLAGS = -lm -lpthread CL = g++ #CL = clang -OBJ = cuarraylist.o cutimeutils.o cutimer.o cudynbuffer.o \ +OBJ = cudictionary.o cuarraylist.o cutimeutils.o cutimer.o cudynbuffer.o \ arraylist.o dynbuffer.o logger.o timeutils.o unittest.o test.o thread.o \ - timer.o + itemfactory.o dictionary.o timer.o PROG = test all : $(PROG) diff --git a/util/arraylist.cpp b/util/arraylist.cpp index c4ecba7..71e68ba 100644 --- a/util/arraylist.cpp +++ b/util/arraylist.cpp @@ -14,8 +14,6 @@ #include "trace.hpp" #include "util.hpp" -CStringFactory* CStringFactory::m_instance = NULL; - /** * Constructor. * @@ -358,52 +356,3 @@ void CStringList::dump(const char* title) const{ printf("%2d: %s\n", ii, (ptr = get(ii)) == NULL ? "" : ptr); } -/** - * Creates a new item with the same content as the source. - * - * @param source the source to copy - * @return an new item with the same content as the source - */ -void* CStringFactory::cloneItem(const void* source) { - char* rc = strdup(reinterpret_cast(source)); - TRACEF(("strdup [%llx] -> [%llx]: %s\n", (long long int) source, (long long int) rc, (char*) rc)); - return rc; -} -/** - * Compare two items. - * - * @param item1 the first item to compare - * @param item2 the 2nd item to compare - * @return 0: items are equal
- * < 0: item1 < item2
- * > 0: item1 > item2 - */ -int CStringFactory::compareItems(const void* item1, const void* item2) const{ - int rc = strcmp(reinterpret_cast(item1), - reinterpret_cast(item2)); - return rc; -} - -/** - * Destroy an item (frees the resources). - * - * Revers process of cloning. - * - * @param item the item to destroy - */ -void CStringFactory::destroyItem(const void* item) { - TRACE2("free [%llx]: %s\n", (long long int) item, (char*) item); - // reserved with strdup() - ::free((void*) item); -} -/** - * Puts the content from one item to another. - * - * @param source the source of the transfer - * @param target OUT: the content will be set to the content of the source - * @return trueassignment successful
- * falsetransfer failed, e.g. space not enough - */ -bool CStringFactory::putItem(const void* source, void* target){ - return false; -} diff --git a/util/arraylist.hpp b/util/arraylist.hpp index 6eeb2ff..5d746d3 100644 --- a/util/arraylist.hpp +++ b/util/arraylist.hpp @@ -12,43 +12,6 @@ #define ARRAYLIST_H #include "trace.hpp" -class ItemFactory { -public: - /** - * Creates a new item with the same content as the source. - * - * @param source the source to copy - * @return an new item with the same content as the source - */ - virtual void* cloneItem(const void* source) = 0; - /** - * Compare two items. - * - * @param item1 the first item to compare - * @param item2 the 2nd item to compare - * @return 0: items are equal
- * < 0: item1 < item2
- * > 0: item1 > item2 - */ - virtual int compareItems(const void* item1, const void* item2) const = 0; - /** - * Destroy an item (frees the resources). - * - * Revers process of cloning. - * - * @param item the item to destroy - */ - virtual void destroyItem(const void* item) = 0; - /** - * Puts the content from one item to another. - * - * @param source the source of the transfer - * @param target OUT: the content will be set to the content of the source - * @return trueassignment successful
- * falsetransfer failed, e.g. space not enough - */ - virtual bool putItem(const void* source, void* target) = 0; -}; class BaseArrayList { public: @@ -136,7 +99,7 @@ public: * Copy constructor. * @param source source to copy */ - ArrayList ( const BaseArrayList& source ): + ArrayList ( const ArrayList& source ): BaseArrayList(source){ } public: @@ -284,21 +247,4 @@ public: void dump(const char* title) const; }; -class CStringFactory { - static CStringFactory* m_instance; -private: - CStringFactory(){ - } -public: - static CStringFactory* instance(){ - if (m_instance == NULL) - m_instance = new CStringFactory; - return m_instance; - } -public: - virtual void* cloneItem(const void* source); - virtual int compareItems(const void* item1, const void* item2) const; - virtual void destroyItem(const void* item); - virtual bool putItem(const void* source, void* target); -}; #endif // ARRAYLIST_H diff --git a/util/cudictionary.cpp b/util/cudictionary.cpp new file mode 100644 index 0000000..7d669d3 --- /dev/null +++ b/util/cudictionary.cpp @@ -0,0 +1,343 @@ +#include "test.hpp" +#include "math.h" + +static Logger s_logger; +static ThreadPool s_timerPool ( 2, &s_logger ); + +class TestDictionary : public UnitTest { + private: + static int m_permutation[]; + static int m_permutationSize; + + public: + TestDictionary() : + UnitTest ( "cuDictionary" ) { + } + virtual ~TestDictionary(){ + } + public: + virtual void run() { + testCopy(); + testDestroy(); + testBasic(); + testAddSorted(); + testAddUnsorted(); + testBlocksize(); + testCapacity(); + testClear(); + testCount(); + testCompareItems(); + testEnsuresSize(); + testGet(); + testRemove(); + testRemoveAt(); + testSorted(); + testSetSorted(); + testDestroyItem(); + } + void testCopy() { + { + CStringInt table1; + table1.put ( "Hi", 4711 ); + + CStringInt table2 ( table1 ); + checkE ( 1, table2.count() ); + checkE ( 4711, table2.get ( "Hi" ) ); + + table1.put ( "Hello", 0xdeadbeef ); + table2 = table1; + checkE ( 2, table2.count() ); + checkE ( 4711, table2.get ( "Hi" ) ); + checkE ( 0xdeadbeef, table2.get ( "Hello" ) ); + } + + } + void testBasic() { + int capacity = 2; + int blocksize = 4; + int maxBlocksize = 1024*1024; + bool sorted = true; + CStringInt table ( capacity, blocksize, maxBlocksize, ! sorted ); + table.add ( "hi" ); + table.add ( " " ); + table.add ( "world" ); + DynBuffer buffer; + checkE ( "hi world", table.join ( buffer ) ); + table.clear(); + buffer.clear(); + checkE ( "", table.join ( buffer ) ); + } + void testAddUnsorted() { + int capacity = 2; + int blocksize = 4; + int maxBlocksize = 1024*1024; + bool sorted = true; + CStringInt table ( capacity, blocksize, maxBlocksize, ! sorted ); + DynBuffer buffer; + for ( int ix = 0; ix < 1024; ix++ ) { + buffer.clear().appendInt ( ix ); + table.add ( buffer.str() ); + } + for ( int ix = 0; ix < 1024; ix++ ) { + buffer.clear().appendInt ( ix ); + checkE ( buffer.str(), table.get ( ix ) ); + } + } + void testAddSorted() { + int capacity = 2; + int blocksize = 4; + int maxBlocksize = 1024*1024; + bool sorted = true; + CStringInt table ( capacity, blocksize, maxBlocksize, sorted ); + DynBuffer buffer; + for ( int ix = 0; ix < m_permutationSize; ix++ ) { + buffer.clear().appendInt ( m_permutation[ix] ); + table.add ( buffer.str() ); + } + //table.dump(NULL); + for ( int ix = 0; ix < m_permutationSize; ix++ ) { + buffer.clear().appendInt ( ix ); + checkE ( buffer.str(), table.get ( ix ) ); + } + } + void testBlocksize() { + int capacity = 1; + int blocksize = 1; + int maxBlocksize = 1024*1024; + bool sorted = true; + CStringInt table ( capacity, blocksize, maxBlocksize, ! sorted ); + DynBuffer buffer; + for ( int ix = 1; ix < 1024; ix++ ) { + buffer.clear().appendInt ( ix ); + table.add ( buffer.str() ); + } + checkE ( 512, table.blocksize() ); + } + void testCapacity() { + int capacity = 16; + int blocksize = 16; + int maxBlocksize = 1024*1024; + bool sorted = true; + CStringInt table ( capacity, blocksize, maxBlocksize, ! sorted ); + DynBuffer buffer; + for ( int ix = 1; ix < 1024; ix++ ) { + buffer.clear().appendInt ( ix ); + table.add ( buffer.str() ); + } + checkE ( 2032, table.capacity() ); + } + void testClear() { + int capacity = 16; + int blocksize = 16; + int maxBlocksize = 1024*1024; + bool sorted = true; + CStringInt table ( capacity, blocksize, maxBlocksize, ! sorted ); + DynBuffer buffer; + for ( int ix = 1; ix < 1024; ix++ ) { + buffer.clear().appendInt ( ix ); + table.add ( buffer.str() ); + } + checkE ( 1023, table.count() ); + checkE ( 0, table.clear().count() ); + + } + void testCompareItems() { + CStringFactory& factory = *CStringFactory::instance(); + // same length: + checkT ( factory.compareItems ( "abc", "abd" ) < 0 ); + checkT ( factory.compareItems ( "abc", "abc" ) == 0 ); + checkT ( factory.compareItems ( "abc", "abb" ) > 0 ); + // different length: + checkT ( factory.compareItems ( "abc", "abc " ) < 0 ); + checkT ( factory.compareItems ( "abc ", "abc" ) > 0 ); + + } + void testCount() { + int capacity = 16; + int blocksize = 16; + int maxBlocksize = 1024*1024; + bool sorted = true; + CStringInt table ( capacity, blocksize, maxBlocksize, ! sorted ); + DynBuffer buffer; + for ( int ix = 1; ix <= 1024; ix++ ) { + buffer.clear().appendInt ( ix ); + table.add ( buffer.str() ); + checkE ( ix, table.count() ); + } + } + void testDestroyItem() { + DynBuffer big; + int size = 100*1024*1024; + big.ensureSize ( size ); + memset ( big.buffer(), 'x', size -1 ); + big.setLength ( size - 1 ); + printf("waiting for 128...\n"); + for ( int ix = 0; ix < 128; ix++ ) { + if (ix % 10 == 0){ + if (ix > 0) + fputc('\n', stdout); + printf("%4d ", ix); + } else { + putc('.', stdout); + fflush(stdout); + } + CStringInt table1; + table1.add ( big.str() ); + table1.add ( big.str() ); + checkE ( size - 1, strlen ( table1.get ( 0 ) ) ); + checkE ( size - 1, strlen ( table1.get ( 1 ) ) ); + + CStringInt table2 ( table1 ); + checkE ( 2, table2.count() ); + checkE ( size - 1, strlen ( table2.get ( 0 ) ) ); + checkE ( size - 1, strlen ( table2.get ( 1 ) ) ); + } + m_logger.say ( LOG_INFO, "end of testDestroyItem()" ); + } + void testEnsuresSize() { + int capacity = 1; + int blocksize = 1; + int maxBlocksize = 1024*1024; + bool sorted = true; + CStringInt table ( capacity, blocksize, maxBlocksize, ! sorted ); + DynBuffer buffer; + for ( int ix = 1; ix < 1024; ix++ ) { + table.ensuresSize ( 7*ix ); + checkT ( table.capacity() >= 7*ix ); + } + } + void testGet() { + int capacity = 2; + int blocksize = 4; + int maxBlocksize = 1024*1024; + bool sorted = true; + CStringInt table ( capacity, blocksize, maxBlocksize, ! sorted ); + table.add ( "joe" ); + table.add ( "adam" ); + table.add ( "charly" ); + DynBuffer buffer; + checkE ( 3, table.count() ); + checkE ( "joe", table.get ( 0 ) ); + checkE ( "adam", table.get ( 1 ) ); + checkE ( "charly", table.get ( 2 ) ); + table.clear(); + + table.setSorted ( true ); + table.add ( "joe" ); + table.add ( "adam" ); + table.add ( "charly" ); + checkE ( 3, table.count() ); + checkE ( "adam", table.get ( 0 ) ); + checkE ( "charly", table.get ( 1 ) ); + checkE ( "joe", table.get ( 2 ) ); + } + void testIndexOfUnsorted() { + { + int capacity = 16; + int blocksize = 16; + int maxBlocksize = 1024*1024; + bool sorted = true; + CStringInt table ( capacity, blocksize, maxBlocksize, ! sorted ); + DynBuffer buffer; + int count = 1; + for ( int ix = 1; ix <= count; ix++ ) { + buffer.clear().appendInt ( 0x42ab7*ix ); + table.add ( buffer.str() ); + } + for ( int ix = 1; ix <= count; ix++ ) { + buffer.clear().appendInt ( 0x42ab7*ix ); + checkE ( ix - 1, table.indexOf ( buffer.str() ) ); + } + count++; + } + } + void testIndexOfSorted() { + int capacity = 16; + int blocksize = 16; + int maxBlocksize = 1024*1024; + bool sorted = true; + CStringInt table ( capacity, blocksize, maxBlocksize, sorted ); + DynBuffer buffer; + for ( int ix = 1; ix <= 1024; ix++ ) { + buffer.clear().appendInt ( 0x1*ix, "%08x" ); + table.add ( buffer.str() ); + } + for ( int ix = 1; ix <= 1024; ix++ ) { + buffer.clear().appendInt ( 0x1*ix, "%08x" ); + checkE ( ix - 1, table.indexOf ( buffer.str() )); + } + } + void testRemove() { + int capacity = 2; + int blocksize = 4; + int maxBlocksize = 1024*1024; + bool sorted = true; + CStringInt table ( capacity, blocksize, maxBlocksize, ! sorted ); + DynBuffer buffer; + for ( int ix = 0; ix < m_permutationSize; ix++ ) { + buffer.clear().appendInt ( m_permutation[ix] ); + table.add ( buffer.str() ); + } + for ( int ix = 0; ix < m_permutationSize; ix++ ) { + buffer.clear().appendInt ( ix ); + table.remove ( buffer.str() ); + } + checkE ( 0, table.count() ); + } + void testRemoveAt() { + int capacity = 16; + int blocksize = 16; + int maxBlocksize = 1024*1024; + bool sorted = true; + CStringInt table ( capacity, blocksize, maxBlocksize, ! sorted ); + DynBuffer buffer; + for ( int ix = 0; ix < 1024; ix++ ) { + buffer.clear().appendInt ( ix ); + table.add ( buffer.str() ); + } + int count = 1024; + checkE(count, table.count()); + for ( int ix = 1020; ix >= 3; ix-- ) { + table.removeAt ( ix ); + if (--count != table.count()) + checkE(count, table.count()); + } + table.dump("reduced table:"); + checkE ( count, table.count() ); + for ( int ix = 0; ix < 3; ix++ ) { + buffer.clear().appendInt ( ix ); + checkE ( buffer.str(), table.get ( ix ) ); + } + for ( int ix = 3; ix < table.count(); ix++ ) { + buffer.clear().appendInt ( ix + 1020 - 3 + 1 ); + checkE ( buffer.str(), table.get ( ix ) ); + } + } + void testSorted() { + int capacity = 16; + int blocksize = 16; + int maxBlocksize = 1024*1024; + bool sorted = true; + CStringInt table ( capacity, blocksize, maxBlocksize, ! sorted ); + checkF ( table.sorted() ); + table.setSorted ( sorted ); + checkT ( table.sorted() ); + } + void testSetSorted() { + int capacity = 16; + int blocksize = 16; + int maxBlocksize = 1024*1024; + bool sorted = true; + CStringInt table ( capacity, blocksize, maxBlocksize, sorted ); + checkT ( table.sorted() ); + table.setSorted ( !sorted ); + checkF ( table.sorted() ); + } +}; +int TestDictionary::m_permutation[] = { 2, 7, 3, 1, 0, 9, 6, 8, 4, 5 }; +int TestDictionary::m_permutationSize = 10; + +void testDictionary() { + TestDictionary test; + test.run(); +} diff --git a/util/dictionary.cpp b/util/dictionary.cpp new file mode 100644 index 0000000..085855b --- /dev/null +++ b/util/dictionary.cpp @@ -0,0 +1,328 @@ +/* + * (Un)License: Public Domain + * You can use and modify this file without any restriction. + * Do what you want. + * No warranties and disclaimer of any damages. + * More info: http://unlicense.org + * The latest sources: https://github.com/republib + * + */ +//#define TRACE_ON 1 +#include "stdlib.h" +#include "assert.h" +#include "memory.h" +#include "trace.hpp" +#include "util.hpp" + +/** + * Constructor. + * + * @param keyFactory factory for cloning/destroying the key + * @param vlaueFactory factory for cloning/destroying the value + * @param capacity the size of the list m_table + * @param blocksize the minimum count of entries to reserve + * @param maxBlocksize the blocksize is doubled during enlarging the buffer + * while blocksize is smaller than this maximum + * @param sorted true: the buffer is sorted + */ +BaseDictionary::BaseDictionary(ItemFactory& keyFactory, ItemFactory& valueFactory, + int capacity, int blocksize, + int maxBlocksize) : + m_capacity(capacity <= 0 ? 16 : capacity), + m_blocksize(blocksize <= 0 ? 16 : blocksize), + m_maxBlocksize(maxBlocksize), + m_count(0), + m_table(new KeyValue[m_capacity]), + m_keyFactory(keyFactory), + m_valueFactory(valueFactory) +{ + TRACE2("buffer: %llx capacity: %d\n", (long long int) m_table, m_capacity); + memset(m_table, 0, m_capacity * sizeof m_table[0] ); +} + +/** + * Copy constructor. + * + * @param source the source to copy + */ +BaseDictionary::BaseDictionary ( const BaseDictionary& source ) : + m_capacity(source.m_capacity), + m_blocksize(source.m_blocksize), + m_maxBlocksize(source.m_maxBlocksize), + m_count(source.m_count), + m_table(new KeyValue[m_capacity]), + m_keyFactory(source.m_keyFactory), + m_valueFactory(source.m_valueFactory) +{ + TRACE2("buffer (copy): %llx capacity: %d\n", (long long int) m_table, m_capacity); + ensuresSize(m_count = source.m_count); + for (int ix = 0; ix < m_count; ix++){ + m_table[ix].m_key = m_keyFactory.cloneItem(source.m_table[ix].m_key); + m_table[ix].m_value = m_valueFactory.cloneItem(source.m_table[ix].m_value); + } +} + + +/** + * Destructor. + */ +BaseDictionary::~BaseDictionary() { + clear(); + TRACE1("buffer del: %llx\n", (long long int) m_table); + delete[] m_table; + m_table = NULL; +} + +/** + * Assign operator. + * + * @param source source to copy + * @return the instance (for chaining) + */ +BaseDictionary& BaseDictionary::operator= ( const BaseDictionary& source ) { + clear(); + ensuresSize(m_count = source.m_count); + for (int ix = 0; ix < m_count; ix++){ + m_table[ix].m_key = m_keyFactory.cloneItem(source.m_table[ix].m_key); + m_table[ix].m_value = m_valueFactory.cloneItem(source.m_table[ix].m_value); + } + return *this; +} + +/** + * @brief Searches the key with binary search. + * + * @param key the value which should be found + * @param index OUT: the index usable for insert: + * index is the smallest value with content[index].m_key >= key + * @return true: the key has been found. + */ +bool BaseDictionary::binarySearch(const void* key, int& index) const { + bool rc = false; + int lbound = 0; + int theCount = m_count; + int ubound = theCount - 1; + int compareRc = 0; + // binary search over the sorted vector: + while (lbound <= ubound) { + int half = (ubound + lbound) / 2; + compareRc = m_keyFactory.compareItems(key, m_table[half].m_key); + if (compareRc < 0) + ubound = half - 1; + else if (compareRc > 0) + lbound = half + 1; + else { + rc = true; + index = half; + break; + } + } + if (!rc) + index = ubound > lbound ? ubound : + lbound > theCount ? theCount : lbound; + return rc; +} + +/** + * Removes all items. + * + * @return the instance (for chaining) + */ +BaseDictionary& BaseDictionary::clear(){ + for (int ix = m_count - 1; ix >= 0; ix--) + removeAt(ix); + m_count = 0; + return *this; +} + +/** + * Ensures that the list have at least the given size. + * + * @param capacity the size to inspect + * @return the instance (for chaining) + */ +BaseDictionary& BaseDictionary::ensuresSize(int capacity){ + if (capacity > m_capacity){ + if ((m_blocksize *= 2) > m_maxBlocksize) + m_blocksize = m_maxBlocksize; + if (capacity - m_capacity < m_blocksize) + capacity = m_capacity + m_blocksize; + KeyValue* table = new KeyValue[m_capacity = capacity]; + TRACE1("table (ensure): %llx\n", (long long int) table); + assert(m_count <= m_capacity && m_count >= 0); + memcpy(table, m_table, sizeof(m_table[0]) * m_count); + TRACE1("buffer del: %llx\n", (long long int) m_table); + delete[] m_table; + m_table = table; + } + return *this; +} + +/** + * Gets a value associated to a key. + * + * @param key the key to search + * @return NULL: not found
+ * otherwise: the value associated to the key + */ +void* BaseDictionary::get(const void* key) const{ + void* rc = NULL; + int index; + if (binarySearch(key, index)) + rc = m_table[index].m_value; + return rc; +} +/** + * Adds a (key, value) pair item to the dictionary. + * + * @param key key to add + * @param value vaue to add + * @return the instance (for chaining) + */ +BaseDictionary& BaseDictionary::put(const void* key, const void* value){ + int index; + if (binarySearch(key, index)){ + m_keyFactory.putItem(value, m_table[index].m_value); + } else { + if (m_count + 1 > m_capacity) + ensuresSize(m_count+1); + assert(m_count <= m_capacity && m_count >= 0); + // build a gap: + // [?, ?, ix, ?]: items to copy: 4 - 2 = count - ix + memmove(m_table + index + 1, m_table + index, + sizeof(m_table[0]) * (m_count++ - index)); + m_table[index].m_key = m_keyFactory.cloneItem(key); + m_table[index].m_value = m_valueFactory.cloneItem(value); + } + return *this; +} + + +/** + * Removes the (key, value) pair from the table, given by the key. + * + * @param key key to delete + * @return the instance (for chaining) + */ +BaseDictionary& BaseDictionary::remove(const void* key){ + int index; + if (binarySearch(key, index)) + removeAt(index); + return *this; +} + +/** + * Sorts a part of the array. + * + * @param count the number of elements to sort + */ +void BaseDictionary::sort(){ + heapify(); + + // The following loop maintains the invariants that a[0:end] is a heap and every element + // beyond end is greater than everything before it (so a[end:count] is in sorted order)) + int end = m_count - 1; + while (end > 0) { + // (a[0] is the root and largest value. The swap moves it in front of the sorted elements.) + swapItems(end, 0); + // the heap size is reduced by one + --end; + // the swap ruined the heap property, so restore it: + shiftDown(0, end); + } +} +void BaseDictionary::heapify(){ + // start is assigned the index in 'a' of the last parent node) + // the last element in a 0-based array is at index count-1; find the parent of that element) + int start = parentOf(m_count-1); + + while (start >= 0){ + // shift down the node at index 'start' to the proper place such that all nodes below + // the start index are in heap order: + shiftDown(start, m_count - 1); + // go to the next parent node: + --start; + } + // after shifting down the root all nodes/elements are in heap order) +} +void BaseDictionary::shiftDown(int start, int end){ + int root = start; + + // while the root has at least one child: + while (leftChildOf(root) <= end){ + // Left child of root) + int child = leftChildOf(root); + // (Keeps track of child to swap with: + int swap = root; + + if (m_keyFactory.compareItems(m_table[swap].m_key, + m_table[child].m_key) < 0) + swap = child; + + // If there is a right child and that child is greater: + if (child+1 <= end + && m_keyFactory.compareItems(m_table[swap].m_key, + m_table[child + 1].m_key) < 0) + swap = child + 1; + if (swap == root){ + // The root holds the largest element. Since we assume the heaps rooted at the + // children are valid, this means that we are done: + break; + } else { + swapItems(root, swap); + // repeat to continue shifting down the child now: + root = swap; + } + } +} + +/** + * Removes a the item with a given index from the list. + * + * @param index item to delete + * @return the instance (for chaining) + */ +BaseDictionary& BaseDictionary::removeAt(int index){ + if (index >= 0 && index < m_count){ + TRACEF(("removeAt(%d): %s factory: %llx\n", index, + (char*) m_table[index], (long long int) &m_factory)); + m_keyFactory.destroyItem(m_table[index].m_key); + m_valueFactory.destroyItem(m_table[index].m_value); + TRACE1("removeAt(%d)\n", index); + if (index < --m_count) + // [?, ?, item, ?]: items to copy: 4 - 2 - 1 = count - ix - 1 + memmove(m_table + index, m_table + index + 1, + sizeof(m_table[0]) * (m_count - index)); + else + m_table[index].m_key = m_table[index].m_value = NULL; + + } + return *this; +} +/** + * Joins the list members to a concatenated string. + * + * @param buffer the buffer for the result + * @return buffer: for chaining + */ +DynBuffer& CStringCString::join(DynBuffer& buffer, const char* separator){ + for (int ix = 0; ix < m_count; ix++){ + if (ix > 0 && separator != NULL) + buffer.append(separator); + buffer.append(get(ix)); + } + return buffer; +} +/** + * Writes a the list for debug purposes. + * + * @trace title NULL: none
+ * otherwise: the prefix to describe the list + */ +void CStringCString::dump(const char* title) const{ + printf("=== %s: count: %d capacity: %d\n", title == NULL ? "" : title, count(), capacity()); + const char* ptr; + for (int ii = 0; ii < m_count; ii++) + printf("%2d: %s\n", ii, (ptr = get(ii)) == NULL ? "" : ptr); +} + diff --git a/util/dictionary.hpp b/util/dictionary.hpp new file mode 100644 index 0000000..98453b7 --- /dev/null +++ b/util/dictionary.hpp @@ -0,0 +1,266 @@ +/* + * (Un)License: Public Domain + * You can use and modify this file without any restriction. + * Do what you want. + * No warranties and disclaimer of any damages. + * More info: http://unlicense.org + * The latest sources: https://github.com/republib + * + */ + +#ifndef DICTIONARY_HPP +#define DICTIONARY_HPP +#include "trace.hpp" + +struct KeyValue { + void* m_key; + void* m_value; +}; +class BaseDictionary +{ +public: + BaseDictionary(ItemFactory& keyFactory, ItemFactory& valueFactory, + int capacity = 16, int blocksize = 16, int maxBlocksize = 1024*1024); + BaseDictionary ( const BaseDictionary& other ); + virtual ~BaseDictionary(); + BaseDictionary& operator= ( const BaseDictionary& other ); +public: + bool binarySearch(const void* item, int& index) const; + BaseDictionary& clear(); + BaseDictionary& ensuresSize(int capacity); + void* get(const void* key) const; + inline KeyValue* getTable() const{ + return m_table; + } + BaseDictionary& put(const void* key, const void* value); + BaseDictionary& remove(const void* item); + BaseDictionary& removeAt(int index); + void sort(); +protected: + void heapify(); + void shiftDown(int start, int end); + /** + * Swaps two items in the buffer. + * @param index1 indes of the first item to swap + * @param index2 indes of the 2nd item to swap + */ + inline void swapItems(int index1, int index2){ + KeyValue tmp = m_table[index1]; + m_table[index1] = m_table[index2]; + m_table[index2] = tmp; + } + /** + * Calculates the parent index of an item given by its index. + * @param index the index of the item + * @return the parent index + */ + inline int parentOf(int index){ + return (index-1) / 2; + } + /** + * Calculates the left child index of an item given by its index. + * @param index the index of the item + * @return the left child index + */ + inline int leftChildOf(int index){ + return 2*index + 1; + } + /** + * Calculates the right child index of an item given by its index. + * @param index the index of the item + * @return the right child index + */ + inline int rightChildOf(int index){ + return 2*index + 2; + } +protected: + int m_capacity; + int m_blocksize; + int m_maxBlocksize; + int m_count; + KeyValue* m_table; + ItemFactory& m_keyFactory; + ItemFactory& m_valueFactory; +}; + +/** + * Handles a dictionary. + * + * A dictionary is a container of (key, value) pairs. + * The type of key and value is given by parameters of the template and can be + * different. + */ +template class Dictionary : protected BaseDictionary { +public: + /** + * Constructor. + * @param keyFactory factory for cloning/destroying the key + * @param valueFactory factory for cloning/destroying the value + * @param capacity the size of the list m_table + * @param blocksize the minimum count of entries to reserve + * @param maxBlocksize the blocksize is doubled during enlarging the buffer + * while blocksize is smaller than this maximum + * @param sorted true: the buffer is sorted + */ + Dictionary(ItemFactory* keyFactory, ItemFactory* valueFactory, + int capacity = 16, int blocksize = 16, + int maxBlocksize = 1024*1024, bool sorted = false): + BaseDictionary(*keyFactory, *valueFactory, capacity, blocksize, + maxBlocksize) { + } + /** + * Copy constructor. + * @param source source to copy + */ + Dictionary ( const Dictionary& source ): + BaseDictionary(source){ + } +public: + /** + * Returns the blocksize. + * @return the minimum count of items while expanding the buffer + */ + inline int blocksize() const{ + return m_blocksize; + } + /** + * Returns the capacity. + * @return the count of items which can be used without expanding + */ + inline int capacity() const{ + return m_capacity; + } + /** + * Clear all elements + */ + inline Dictionary& clear(){ + BaseDictionary::clear(); + return *this; + } +public: + /** + * Returns the number of items in the buffer. + * @return the number of items + */ + inline int count() const { + return m_count; + } + /** + * Ensures that the capacity has at least a given size. + * @param capacity the ordered capacity. If the current capacity is + * too small it will be expanded + */ + inline Dictionary& ensuresSize(int capacity){ + BaseDictionary::ensuresSize(capacity); + return *this; + } + /** + * Returns the value associated to a given key. + * + * @param key key to search + * @return NULL: not found
+ * otherwise: the wanted item + */ + inline Val* get(Key key) const{ + Val* rc = reinterpret_cast(BaseDictionary::get( + reinterpret_cast(key))); + return rc; + } + /** + * Puts a key value pair to the dictionary. + * + * @param key item to add + * @param index insert index. If >= m_count: item is appended + * @return the instance (for chaining) + */ + inline Dictionary& put(const Key* key, const Val* value){ + BaseDictionary::put(reinterpret_cast(key), + reinterpret_cast(value)); + return *this; + } + /** + * Removes the first item which is equal to a given item. + * @param item the item to remove + * @return the instance (for chaining) + */ + inline Dictionary& remove(const Key* item){ + BaseDictionary::remove(reinterpret_cast(item)); + return *this; + } +}; + +/** + * (Key, value) pairs with type (C string, C string). + * + * C strings are strings delimited by '\0'. + */ +class CStringCString : public Dictionary { +public: + /** + * Constructor. + * + * @param capacity number of elements at start + * @param blocksize the minimum of elements to reserve + * @param maxBlocksize the blocksize is doubled until this count + */ + CStringCString(int capacity = 16, int blocksize = 16, + int maxBlocksize = 1024*1024) : + Dictionary(reinterpret_cast(CStringFactory::instance()), + reinterpret_cast(CStringFactory::instance()), + capacity, blocksize, maxBlocksize){ + } + CStringCString ( const CStringCString& source ) : + Dictionary(source){ + } +public: + DynBuffer& join(DynBuffer& buffer, const char* separator = NULL); + void dump(const char* title) const; +}; + +/** + * (Key, value) pairs with type (C string, integer). + * + * C strings are strings delimited by '\0'. + */ +class CStringInt : public Dictionary { +public: + /** + * Constructor. + * + * @param capacity number of elements at start + * @param blocksize the minimum of elements to reserve + * @param maxBlocksize the blocksize is doubled until this count + */ + CStringInt(int capacity = 16, int blocksize = 16, + int maxBlocksize = 1024*1024) : + Dictionary(reinterpret_cast(CStringFactory::instance()), + reinterpret_cast(IntFactory::instance()), + capacity, blocksize, maxBlocksize){ + } + CStringInt ( const CStringInt& source ) : + Dictionary(source){ + } +public: + inline PtrInt_t get(const char* key, PtrInt_t value, PtrInt_t defaultValue) const{ + PtrInt_t rc = defaultValue; + int index; + if (binarySearch(reinterpret_cast(key), index)){ + rc = (PtrInt_t) m_table[index].m_value; + } + return rc; + } + inline CStringInt& put(const char* key, PtrInt_t value){ + int index; + if (binarySearch(reinterpret_cast(key), index)){ + m_table[index].m_value = (void*) value; + } else { + Dictionary::put(key, &value); + } + return *this; + } + DynBuffer& join(DynBuffer& buffer, const char* separator = NULL); + void dump(const char* title) const; +}; + + +#endif // DICTIONARY_HPP diff --git a/util/itemfactory.cpp b/util/itemfactory.cpp new file mode 100644 index 0000000..dc678f3 --- /dev/null +++ b/util/itemfactory.cpp @@ -0,0 +1,14 @@ +/* + * (Un)License: Public Domain + * You can use and modify this file without any restriction. + * Do what you want. + * No warranties and disclaimer of any damages. + * More info: http://unlicense.org + * The latest sources: https://github.com/republib + * + */ + +#include "util.hpp" + +CStringFactory* CStringFactory::m_instance = NULL; +IntFactory* IntFactory::m_instance = NULL; diff --git a/util/itemfactory.hpp b/util/itemfactory.hpp new file mode 100644 index 0000000..6eb97ca --- /dev/null +++ b/util/itemfactory.hpp @@ -0,0 +1,208 @@ +/* + * (Un)License: Public Domain + * You can use and modify this file without any restriction. + * Do what you want. + * No warranties and disclaimer of any damages. + * More info: http://unlicense.org + * The latest sources: https://github.com/republib + * + */ + + +#ifndef ITEMFACTORY_HPP_ +#define ITEMFACTORY_HPP_ + +/** + * Administrates an auxiliary class for containers. + * + * A factory handle items of a container: cloning, assignment, comparison. + * + */ +class ItemFactory { +public: + /** + * Destructor. + */ + virtual ~ItemFactory(){ + } +public: + /** + * Creates a new item with the same content as the source. + * + * @param source the source to copy + * @return an new item with the same content as the source + */ + virtual void* cloneItem(const void* source) = 0; + /** + * Compare two items. + * + * @param item1 the first item to compare + * @param item2 the 2nd item to compare + * @return 0: items are equal
+ * < 0: item1 < item2
+ * > 0: item1 > item2 + */ + virtual int compareItems(const void* item1, const void* item2) const = 0; + /** + * Destroy an item (frees the resources). + * + * Revers process of cloning. + * + * @param item the item to destroy + */ + virtual void destroyItem(const void* item) = 0; + /** + * Puts the content from one item to another. + * + * @param source the source of the transfer + * @param target OUT: the content will be set to the content of the source + * @return trueassignment successful
+ * falsetransfer failed, e.g. space not enough + */ + virtual bool putItem(const void* source, void*& target) = 0; +}; + +/** + * A factory for C strings, a character sequence ending with '\0'. + */ +class CStringFactory { +private: + static CStringFactory* m_instance; +private: + /** + * Constructor. + */ + CStringFactory(){ + } + /** + * Destructor. + */ + virtual ~CStringFactory(){ + } +public: + static CStringFactory* instance(){ + if (m_instance == NULL) + m_instance = new CStringFactory; + return m_instance; + } +public: + /** + * Creates a new item with the same content as the source. + * + * @param source the source to copy + * @return an new item with the same content as the source + */ + virtual void* cloneItem(const void* source) { + char* rc = strdup(reinterpret_cast(source)); + TRACEF(("strdup [%llx] -> [%llx]: %s\n", (long long int) source, (long long int) rc, (char*) rc)); + return rc; + } + /** + * Compare two items. + * + * @param item1 the first item to compare + * @param item2 the 2nd item to compare + * @return 0: items are equal
+ * < 0: item1 < item2
+ * > 0: item1 > item2 + */ + virtual int compareItems(const void* item1, const void* item2) const{ + int rc = strcmp(reinterpret_cast(item1), + reinterpret_cast(item2)); + return rc; + } + + /** + * Destroy an item (frees the resources). + * + * Revers process of cloning. + * + * @param item the item to destroy + */ + virtual void destroyItem(const void* item) { + TRACE2("free [%llx]: %s\n", (long long int) item, (char*) item); + // reserved with strdup() + ::free((void*) item); + } + /** + * Puts the content from one item to another. + * + * @param source the source of the transfer + * @param target OUT: the content will be set to the content of the source + * @return trueassignment successful
+ * falsetransfer failed, e.g. space not enough + */ + virtual bool putItem(const void* source, void*& target){ + return false; + } +}; + +/** + * A factory for integers. + */ +class IntFactory { +private: + static IntFactory* m_instance; +private: + /** + * Constructor. + */ + IntFactory(){ + } + /** + * Destructor. + */ + virtual ~IntFactory(){ + } +public: + static IntFactory* instance(){ + if (m_instance == NULL) + m_instance = new IntFactory; + return m_instance; + } +public: + /** + * Creates a new item with the same content as the source. + * + * @param source the source to copy + * @return an new item with the same content as the source + */ + virtual void* cloneItem(const void* source){ + return (void*) source; + } + /** + * Compare two items. + * + * @param item1 the first item to compare + * @param item2 the 2nd item to compare + * @return 0: items are equal
+ * < 0: item1 < item2
+ * > 0: item1 > item2 + */ + virtual int compareItems(const void* item1, const void* item2) const{ + return (PtrInt_t) item1 - (PtrInt_t) item2; + } + /** + * Destroy an item (frees the resources). + * + * Revers process of cloning. + * + * @param item the item to destroy + */ + virtual void destroyItem(const void* item){ + // nothing to do + } + /** + * Puts the content from one item to another. + * + * @param source the source of the transfer + * @param target OUT: the content will be set to the content of the source + * @return trueassignment successful
+ */ + virtual bool putItem(const void* source, void*& target){ + target = (void*) source; + return true; + } +}; + +#endif /* ITEMFACTORY_HPP_ */ diff --git a/util/util.hpp b/util/util.hpp index 50411ff..4104439 100644 --- a/util/util.hpp +++ b/util/util.hpp @@ -9,7 +9,17 @@ #include #include #include - +#include +/** + * An integer which has the same size as a pointer. + */ +#if defined(__LP64__) || defined(_LP64) +typedef int64_t PtrInt_t; +typedef uint64_t PtrUInt_t; +#else +typedef int32_t PtrInt_t; +typedef uint32_t PtrUInt_t; +#endif typedef unsigned char ubyte_t; template class Singleton{ static T* m_instance; @@ -21,8 +31,11 @@ public: } }; +#include "trace.hpp" #include "dynbuffer.hpp" +#include "itemfactory.hpp" #include "arraylist.hpp" +#include "dictionary.hpp" #include "timeutils.hpp" #include "logger.hpp" #include "thread.hpp"