-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)
+
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)
#include "trace.hpp"
#include "util.hpp"
-CStringFactory* CStringFactory::m_instance = NULL;
-
/**
* Constructor.
*
printf("%2d: %s\n", ii, (ptr = get(ii)) == NULL ? "<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<const char*>(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<br>
- * < 0: item1 < item2<br>
- * > 0: item1 > item2
- */
-int CStringFactory::compareItems(const void* item1, const void* item2) const{
- int rc = strcmp(reinterpret_cast<const char*>(item1),
- reinterpret_cast<const char*>(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 <i>true</i>assignment successful<br>
- * <i>false</i>transfer failed, e.g. space not enough
- */
-bool CStringFactory::putItem(const void* source, void* target){
- return false;
-}
#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<br>
- * < 0: item1 < item2<br>
- * > 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 <i>true</i>assignment successful<br>
- * <i>false</i>transfer failed, e.g. space not enough
- */
- virtual bool putItem(const void* source, void* target) = 0;
-};
class BaseArrayList
{
public:
* Copy constructor.
* @param source source to copy
*/
- ArrayList ( const BaseArrayList& source ):
+ ArrayList ( const ArrayList& source ):
BaseArrayList(source){
}
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
--- /dev/null
+#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();
+}
--- /dev/null
+/*
+ * (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 <i>true</i>: 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<br>
+ * 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 <i>buffer</i>: 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<br>
+ * 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 ? "<null>" : ptr);
+}
+
--- /dev/null
+/*
+ * (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 Key, class Val> 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 <i>true</i>: 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 <i>NULL</i>: not found<br>
+ * otherwise: the wanted item
+ */
+ inline Val* get(Key key) const{
+ Val* rc = reinterpret_cast<Val*>(BaseDictionary::get(
+ reinterpret_cast<const Key*>(key)));
+ return rc;
+ }
+ /**
+ * Puts a key value pair to the dictionary.
+ *
+ * @param key item to add
+ * @param index insert index. If <i>>= m_count</i>: item is appended
+ * @return the instance (for chaining)
+ */
+ inline Dictionary& put(const Key* key, const Val* value){
+ BaseDictionary::put(reinterpret_cast<const void*>(key),
+ reinterpret_cast<const void*>(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<const void*>(item));
+ return *this;
+ }
+};
+
+/**
+ * (Key, value) pairs with type (C string, C string).
+ *
+ * C strings are strings delimited by '\0'.
+ */
+class CStringCString : public Dictionary<char, char> {
+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<ItemFactory*>(CStringFactory::instance()),
+ reinterpret_cast<ItemFactory*>(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<char, int> {
+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<ItemFactory*>(CStringFactory::instance()),
+ reinterpret_cast<ItemFactory*>(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<const void*>(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<const void*>(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
--- /dev/null
+/*
+ * (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;
--- /dev/null
+/*
+ * (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<br>
+ * < 0: item1 < item2<br>
+ * > 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 <i>true</i>assignment successful<br>
+ * <i>false</i>transfer 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<const char*>(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<br>
+ * < 0: item1 < item2<br>
+ * > 0: item1 > item2
+ */
+ virtual int compareItems(const void* item1, const void* item2) const{
+ int rc = strcmp(reinterpret_cast<const char*>(item1),
+ reinterpret_cast<const char*>(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 <i>true</i>assignment successful<br>
+ * <i>false</i>transfer 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<br>
+ * < 0: item1 < item2<br>
+ * > 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 <i>true</i>assignment successful<br>
+ */
+ virtual bool putItem(const void* source, void*& target){
+ target = (void*) source;
+ return true;
+ }
+};
+
+#endif /* ITEMFACTORY_HPP_ */
#include <errno.h>
#include <pthread.h>
#include <stdint.h>
-
+#include <limits.h>
+/**
+ * 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 T> class Singleton{
static T* m_instance;
}
};
+#include "trace.hpp"
#include "dynbuffer.hpp"
+#include "itemfactory.hpp"
#include "arraylist.hpp"
+#include "dictionary.hpp"
#include "timeutils.hpp"
#include "logger.hpp"
#include "thread.hpp"