]> gitweb.hamatoma.de Git - cpidjinn/commitdiff
dictionary added
authorhama <hama@siduction.net>
Mon, 15 Aug 2016 22:14:37 +0000 (00:14 +0200)
committerhama <hama@siduction.net>
Mon, 15 Aug 2016 22:14:37 +0000 (00:14 +0200)
* Dictionary, CStringCString, CStringInt added
* ItemFactory extracted to its own file

Makefile
util/Makefile
util/arraylist.cpp
util/arraylist.hpp
util/cudictionary.cpp [new file with mode: 0644]
util/dictionary.cpp [new file with mode: 0644]
util/dictionary.hpp [new file with mode: 0644]
util/itemfactory.cpp [new file with mode: 0644]
util/itemfactory.hpp [new file with mode: 0644]
util/util.hpp

index a2acf37aa582eaf4af47dff8165f45f7b7b1dc63..b20370ac50763b4a6fd5bbf1adb89d553b9f5a00 100644 (file)
--- 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)
+
index 0f09be755fdaf68911c29137f653e72e7df6b258..9f2d177176087e27673bcd09a5d526309af8a556 100644 (file)
@@ -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)
index c4ecba70ca035e241e76dcdda7e587a61539d4e0..71e68ba7a0229b01ce0d0ecee40d20361374782b 100644 (file)
@@ -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 ? "<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>
- *                                     &lt; 0: item1 &lt; item2<br>
- *                                     &gt; 0: item1 &gt; 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;
-}
index 6eeb2ff16920f887285c73c1b01ce61802aae11a..5d746d3b9bb89380a89bf30439b80cfe9dda5fe8 100644 (file)
 #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>
-       *                                       &lt; 0: item1 &lt; item2<br>
-       *                                       &gt; 0: item1 &gt; 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:
@@ -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 (file)
index 0000000..7d669d3
--- /dev/null
@@ -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 (file)
index 0000000..085855b
--- /dev/null
@@ -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                       <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);
+}
+
diff --git a/util/dictionary.hpp b/util/dictionary.hpp
new file mode 100644 (file)
index 0000000..98453b7
--- /dev/null
@@ -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 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>&gt;= 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
diff --git a/util/itemfactory.cpp b/util/itemfactory.cpp
new file mode 100644 (file)
index 0000000..dc678f3
--- /dev/null
@@ -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 (file)
index 0000000..6eb97ca
--- /dev/null
@@ -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<br>
+       *                                       &lt; 0: item1 &lt; item2<br>
+       *                                       &gt; 0: item1 &gt; 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>
+        *                                      &lt; 0: item1 &lt; item2<br>
+        *                                      &gt; 0: item1 &gt; 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>
+       *                                       &lt; 0: item1 &lt; item2<br>
+       *                                       &gt; 0: item1 &gt; 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_ */
index 50411ffc74ea932bae724ccfb45b671aa7715c54..41044393bc19c2e111c633506db0fb86c95f9842 100644 (file)
@@ -9,7 +9,17 @@
 #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;
@@ -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"