diff options
Diffstat (limited to 'lout')
-rw-r--r-- | lout/Makefile.am | 14 | ||||
-rw-r--r-- | lout/container.cc | 558 | ||||
-rw-r--r-- | lout/container.hh | 441 | ||||
-rw-r--r-- | lout/debug.hh | 152 | ||||
-rw-r--r-- | lout/identity.cc | 114 | ||||
-rw-r--r-- | lout/identity.hh | 148 | ||||
-rw-r--r-- | lout/misc.cc | 237 | ||||
-rw-r--r-- | lout/misc.hh | 320 | ||||
-rw-r--r-- | lout/object.cc | 311 | ||||
-rw-r--r-- | lout/object.hh | 161 | ||||
-rw-r--r-- | lout/signal.cc | 171 | ||||
-rw-r--r-- | lout/signal.hh | 310 |
12 files changed, 2937 insertions, 0 deletions
diff --git a/lout/Makefile.am b/lout/Makefile.am new file mode 100644 index 00000000..18e00cf2 --- /dev/null +++ b/lout/Makefile.am @@ -0,0 +1,14 @@ +noinst_LIBRARIES = liblout.a + +liblout_a_SOURCES = \ + container.cc \ + container.hh \ + debug.hh \ + identity.cc \ + identity.hh \ + misc.cc \ + misc.hh \ + object.cc \ + object.hh \ + signal.cc \ + signal.hh diff --git a/lout/container.cc b/lout/container.cc new file mode 100644 index 00000000..0b00c195 --- /dev/null +++ b/lout/container.cc @@ -0,0 +1,558 @@ +/* + * Dillo Widget + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + + +#include "container.hh" +#include "misc.hh" + +namespace lout { + +using namespace object; + +namespace container { + +namespace untyped { + +// ------------- +// Iterator +// ------------- + +Iterator::Iterator() +{ + impl = NULL; +} + +Iterator::Iterator(const Iterator &it2) +{ + impl = it2.impl; + if(impl) + impl->ref(); +} + +Iterator::Iterator(Iterator &it2) +{ + impl = it2.impl; + if(impl) + impl->ref(); +} + +Iterator &Iterator::operator=(const Iterator &it2) +{ + if(impl) + impl->unref(); + impl = it2.impl; + if(impl) + impl->ref(); + return *this; +} + +Iterator &Iterator::operator=(Iterator &it2) +{ + if(impl) + impl->unref(); + impl = it2.impl; + if(impl) + impl->ref(); + return *this; +} + +Iterator::~Iterator() +{ + if(impl) + impl->unref(); +} + +// ---------------- +// Collection +// ---------------- + +void Collection::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append("{ "); + bool first = true; + for(Iterator it = iterator(); it.hasNext(); ) { + if(!first) + sb->append(", "); + it.getNext()->intoStringBuffer(sb); + first = false; + } + sb->append(" }"); +} + +// ------------ +// Vector +// ------------ + +Vector::Vector(int initSize, bool ownerOfObjects) +{ + numAlloc = initSize == 0 ? 1 : initSize; + this->ownerOfObjects = ownerOfObjects; + numElements = 0; + array = (Object**)malloc(numAlloc * sizeof(Object*)); +} + +Vector::~Vector() +{ + clear(); + free(array); +} + +void Vector::put(Object *newElement, int newPos) +{ + if(newPos == -1) + newPos = numElements; + + // Old entry is overwritten. + if(newPos < numElements) { + if(ownerOfObjects && array[newPos]) + delete array[newPos]; + } + + // Allocated memory has to be increased. + if(newPos >= numAlloc) { + while (newPos >= numAlloc) + numAlloc *= 2; + array = (Object**)realloc(array, numAlloc * sizeof(Object*)); + } + + // Insert NULL's into possible gap before new position. + for(int i = numElements; i < newPos; i++) + array[i] = NULL; + + if(newPos >= numElements) + numElements = newPos + 1; + + array[newPos] = newElement; +} + +void Vector::clear() +{ + if (ownerOfObjects) { + for(int i = 0; i < numElements; i++) + if(array[i]) + delete array[i]; + } + + numElements = 0; +} + +void Vector::insert(Object *newElement, int pos) +{ + if(pos >= numElements) + put(newElement, pos); + else { + numElements++; + + // Allocated memory has to be increased. + if(numElements >= numAlloc) { + numAlloc *= 2; + array = (Object**)realloc(array, numAlloc * sizeof(Object*)); + } + + for(int i = numElements - 1; i > pos; i--) + array[i] = array[i - 1]; + + array[pos] = newElement; + } +} + +void Vector::remove(int pos) +{ + if(ownerOfObjects && array[pos]) + delete array[pos]; + + for(int i = pos + 1; i < numElements; i++) + array[i - 1] = array[i]; + + numElements--; +} + +/** + * Sort the elements in the vector. Assumes that all elements are Comparable's. + */ +void Vector::sort() +{ + qsort(array, numElements, sizeof(Object*), misc::Comparable::compareFun); +} + + +/** + * \bug Not implemented. + */ +Collection0::AbstractIterator* Vector::createIterator() +{ + return NULL; +} + +// ------------ +// List +// ------------ + +List::List(bool ownerOfObjects) +{ + this->ownerOfObjects = ownerOfObjects; + first = last = NULL; + numElements = 0; +} + +List::~List() +{ + clear(); +} + +void List::clear() +{ + while(first) { + if(ownerOfObjects && first->object) + delete first->object; + Node *next = first->next; + delete first; + first = next; + } + + last = NULL; + numElements = 0; +} + +void List::append(Object *element) +{ + Node *newLast = new Node; + newLast->next = NULL; + newLast->object = element; + + if(last) { + last->next = newLast; + last = newLast; + } else + first = last = newLast; + + numElements++; +} + + +bool List::remove0(Object *element, bool compare, bool doNotDeleteAtAll) +{ + Node *beforeCur, *cur; + + for(beforeCur = NULL, cur = first; cur; beforeCur = cur, cur = cur->next) { + if (compare ? + (cur->object && element->equals(cur->object)) : + element == cur->object) { + if(beforeCur) { + beforeCur->next = cur->next; + if(cur->next == NULL) + last = beforeCur; + } else { + first = cur->next; + if(first == NULL) + last = NULL; + } + + if(ownerOfObjects && cur->object && !doNotDeleteAtAll) + delete cur->object; + delete cur; + + numElements--; + return true; + } + } + + return false; +} + +Object *List::ListIterator::getNext() +{ + Object *object; + + if(current) { + object = current->object; + current = current->next; + } else + object = NULL; + + return object; +} + +bool List::ListIterator::hasNext() +{ + return current != NULL; +} + +Collection0::AbstractIterator* List::createIterator() +{ + return new ListIterator(first); +} + + +// --------------- +// HashTable +// --------------- + +HashTable::HashTable(bool ownerOfKeys, bool ownerOfValues, int tableSize) +{ + this->ownerOfKeys = ownerOfKeys; + this->ownerOfValues = ownerOfValues; + this->tableSize = tableSize; + + table = new Node*[tableSize]; + for(int i = 0; i < tableSize; i++) + table[i] = NULL; +} + +HashTable::~HashTable() +{ + for(int i = 0; i < tableSize; i++) { + Node *n1 = table[i]; + while(n1) { + Node *n2 = n1->next; + + if(ownerOfValues && n1->value) + delete n1->value; + if(ownerOfKeys) + delete n1->key; + delete n1; + + n1 = n2; + } + } + + delete[] table; +} + +void HashTable::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append("{ "); + + bool first = true; + for(int i = 0; i < tableSize; i++) { + for(Node *node = table[i]; node; node = node->next) { + if(!first) + sb->append(", "); + node->key->intoStringBuffer(sb); + sb->append(" => "); + node->value->intoStringBuffer(sb); + first = false; + } + } + + sb->append(" }"); +} + +void HashTable::put(Object *key, Object *value) +{ + int h = calcHashValue(key); + Node *n = new Node; + n->key = key; + n->value = value; + n->next = table[h]; + table[h] = n; +} + +bool HashTable::contains(Object *key) +{ + int h = calcHashValue(key); + for(Node *n = table[h]; n; n = n->next) { + if (key->equals(n->key)) + return true; + } + + return false; +} + +Object *HashTable::get(Object *key) +{ + int h = calcHashValue(key); + for(Node *n = table[h]; n; n = n->next) { + if (key->equals(n->key)) + return n->value; + } + + return NULL; +} + +bool HashTable::remove(Object *key) +{ + int h = calcHashValue(key); + Node *last, *cur; + + for(last = NULL, cur = table[h]; cur; last = cur, cur = cur->next) { + if (key->equals(cur->key)) { + if(last) + last->next = cur->next; + else + table[h] = cur->next; + + if(ownerOfValues && cur->value) + delete cur->value; + if(ownerOfKeys) + delete cur->key; + delete cur; + + return true; + } + } + + return false; +} + +Object *HashTable::getKey (Object *key) +{ + int h = calcHashValue(key); + for(Node *n = table[h]; n; n = n->next) { + if (key->equals(n->key)) + return n->key; + } + + return NULL; +} + +HashTable::HashTableIterator::HashTableIterator(HashTable *table) +{ + this->table = table; + node = NULL; + pos = -1; + gotoNext(); +} + +void HashTable::HashTableIterator::gotoNext() +{ + if(node) + node = node->next; + + while(node == NULL) { + if(pos >= table->tableSize - 1) + return; + pos++; + node = table->table[pos]; + } +} + + +Object *HashTable::HashTableIterator::getNext() +{ + Object *result; + if(node) + result = node->key; + else + result = NULL; + + gotoNext(); + return result; +} + +bool HashTable::HashTableIterator::hasNext() +{ + return node != NULL; +} + +Collection0::AbstractIterator* HashTable::createIterator() +{ + return new HashTableIterator(this); +} + +// ----------- +// Stack +// ----------- + +Stack::Stack (bool ownerOfObjects) +{ + this->ownerOfObjects = ownerOfObjects; + bottom = top = NULL; + numElements = 0; +} + +Stack::~Stack() +{ + while (top) + pop (); +} + +void Stack::push (object::Object *object) +{ + Node *newTop = new Node (); + newTop->object = object; + newTop->prev = top; + + top = newTop; + if(bottom == NULL) + bottom = top; + + numElements++; +} + +void Stack::pushUnder (object::Object *object) +{ + Node *newBottom = new Node (); + newBottom->object = object; + newBottom->prev = NULL; + if(bottom != NULL) + bottom->prev = newBottom; + + bottom = newBottom; + if(top == NULL) + top = bottom; + + numElements++; +} + +void Stack::pop () +{ + Node *newTop = top->prev; + + if (ownerOfObjects) + delete top->object; + delete top; + + top = newTop; + if(top == NULL) + bottom = NULL; + + numElements--; +} + +Object *Stack::StackIterator::getNext() +{ + Object *object; + + if(current) { + object = current->object; + current = current->prev; + } else + object = NULL; + + return object; +} + +bool Stack::StackIterator::hasNext() +{ + return current != NULL; +} + +Collection0::AbstractIterator* Stack::createIterator() +{ + return new StackIterator(top); +} + +} // namespace untyped + +} // namespace container + +} // namespace lout diff --git a/lout/container.hh b/lout/container.hh new file mode 100644 index 00000000..26803e23 --- /dev/null +++ b/lout/container.hh @@ -0,0 +1,441 @@ +#ifndef __LOUT_CONTAINER_HH_ +#define __LOUT_CONTAINER_HH_ + +#include "object.hh" + +/** + * \brief This namespace contains a framework for container classes, which + * members are instances of object::Object. + * + * A common problem in languanges without garbage collection is, where the + * children belong to, and so, who is responsible to delete them (instanciation + * is always done by the caller). This information is here told to the + * collections, each container has a constructor with the parameter + * "ownerOfObjects" (HashTable has two such parameters, for keys and values). + * + * \sa container::untyped, container::typed + */ +namespace lout { + +namespace container { + +/** + * \brief The container classes defined here contain instances of + * object::Object. + * + * Different sub-classes may be mixed, and you have to care about casting, + * there is no type-safety. + */ +namespace untyped { + +/** + * \brief ... + */ +class Collection0: public object::Object +{ + friend class Iterator; + +protected: + /** + * \brief The base class for all iterators, as created by + * container::untyped::Collection::createIterator. + */ + class AbstractIterator: public object::Object + { + private: + int refcount; + + public: + AbstractIterator() { refcount = 1; } + + void ref () { refcount++; } + void unref () { refcount--; if (refcount == 0) delete this; } + + virtual bool hasNext () = 0; + virtual Object *getNext () = 0; + }; + +protected: + virtual AbstractIterator* createIterator() = 0; +}; + +/** + * \brief This is a small wrapper for AbstractIterator, which may be used + * directly, not as a pointer, to makes memory management simpler. + */ +class Iterator +{ + friend class Collection; + +private: + Collection0::AbstractIterator *impl; + + // Should not instanciated directly. + inline Iterator(Collection0::AbstractIterator *impl) { this->impl = impl; } + +public: + Iterator(); + Iterator(const Iterator &it2); + Iterator(Iterator &it2); + ~Iterator(); + Iterator &operator=(const Iterator &it2); + Iterator &operator=(Iterator &it2); + + inline bool hasNext() { return impl->hasNext(); } + inline object::Object *getNext() { return impl->getNext(); } +}; + +/** + * \brief Abstract base class for all container objects in container::untyped. + */ +class Collection: public Collection0 +{ +public: + void intoStringBuffer(misc::StringBuffer *sb); + inline Iterator iterator() { Iterator it(createIterator()); return it; } +}; + + +/** + * \brief Container, which is implemented by an array, which is + * dynamically resized. + */ +class Vector: public Collection +{ +private: + object::Object **array; + int numAlloc, numElements; + bool ownerOfObjects; + +protected: + AbstractIterator* createIterator(); + +public: + Vector(int initSize, bool ownerOfObjects); + ~Vector(); + + void put(object::Object *newElement, int newPos = -1); + void insert(object::Object *newElement, int pos); + void remove(int pos); + inline object::Object *get(int pos) + { return (pos >= 0 && pos < numElements) ? array[pos] : NULL; } + inline int size() { return numElements; } + void clear(); + void sort(); +}; + + +/** + * \brief A single-linked list. + */ +class List: public Collection +{ + friend class ListIterator; + +private: + struct Node + { + public: + object::Object *object; + Node *next; + }; + + class ListIterator: public AbstractIterator + { + private: + List::Node *current; + public: + ListIterator(List::Node *node) { current = node; } + bool hasNext(); + Object *getNext(); + }; + + Node *first, *last; + bool ownerOfObjects; + int numElements; + + bool remove0(object::Object *element, bool compare, bool doNotDeleteAtAll); + +protected: + AbstractIterator* createIterator(); + +public: + List(bool ownerOfObjects); + ~List(); + + void clear(); + void append(object::Object *element); + inline bool removeRef(object::Object *element) + { return remove0(element, false, false); } + inline bool remove(object::Object *element) + { return remove0(element, true, false); } + inline bool detachRef(object::Object *element) + { return remove0(element, false, true); } + inline int size() { return numElements; } + inline bool isEmpty() { return numElements == 0; } + inline object::Object *getFirst() { return first->object; } + inline object::Object *getLast() { return last->object; } +}; + + +/** + * \brief A hash table. + */ +class HashTable: public Collection +{ + friend class HashTableIterator; + +private: + struct Node + { + object::Object *key, *value; + Node *next; + }; + + class HashTableIterator: public Collection0::AbstractIterator + { + private: + HashTable *table; + HashTable::Node *node; + int pos; + + void gotoNext(); + + public: + HashTableIterator(HashTable *table); + bool hasNext(); + Object *getNext(); + }; + + Node **table; + int tableSize; + bool ownerOfKeys, ownerOfValues; + +private: + inline int calcHashValue(object::Object *key) + { + return abs(key->hashValue()) % tableSize; + } + +protected: + AbstractIterator* createIterator(); + +public: + HashTable(bool ownerOfKeys, bool ownerOfValues, int tableSize = 251); + ~HashTable(); + + void intoStringBuffer(misc::StringBuffer *sb); + + void put (object::Object *key, object::Object *value); + bool contains (object::Object *key); + Object *get (object::Object *key); + bool remove (object::Object *key); + Object *getKey (Object *key); +}; + +/** + * \brief A stack (LIFO). + * + * Note that the iterator returns all elements in the reversed order they have + * been put on the stack. + */ +class Stack: public Collection +{ + friend class StackIterator; + +private: + class Node + { + public: + object::Object *object; + Node *prev; + }; + + class StackIterator: public AbstractIterator + { + private: + Stack::Node *current; + public: + StackIterator(Stack::Node *node) { current = node; } + bool hasNext(); + Object *getNext(); + }; + + Node *bottom, *top; + bool ownerOfObjects; + int numElements; + +protected: + AbstractIterator* createIterator(); + +public: + Stack (bool ownerOfObjects); + ~Stack(); + + void push (object::Object *object); + void pushUnder (object::Object *object); + inline object::Object *getTop () { return top ? top->object : NULL; } + void pop (); + inline int size() { return numElements; } +}; + +} // namespace untyped + +/** + * \brief This namespace provides thin wrappers, implemented as C++ templates, + * to gain type-safety. + * + * Notice, that nevertheless, all objects still have to be instances of + * object::Object. + */ +namespace typed { + +template <class T> class Collection; + +/** + * \brief Typed version of container::untyped::Iterator. + */ +template <class T> class Iterator +{ + friend class Collection<T>; + +private: + untyped::Iterator base; + +public: + inline Iterator() { } + inline Iterator(const Iterator<T> &it2) { this->base = it2.base; } + inline Iterator(Iterator<T> &it2) { this->base = it2.base; } + ~Iterator() { } + inline Iterator &operator=(const Iterator<T> &it2) + { this->base = it2.base; return *this; } + inline Iterator &operator=(Iterator<T> &it2) + { this->base = it2.base; return *this; } + + inline bool hasNext() { return this->base.hasNext(); } + inline T *getNext() { return (T*)this->base.getNext(); } +}; + +/** + * \brief Abstract base class template for all container objects in + * container::typed. + * + * Actually, a wrapper for container::untyped::Collection. + */ +template <class T> class Collection: public object::Object +{ +protected: + untyped::Collection *base; + +public: + void intoStringBuffer(misc::StringBuffer *sb) + { this->base->intoStringBuffer(sb); } + + inline Iterator<T> iterator() { + Iterator<T> it; it.base = this->base->iterator(); return it; } +}; + + +/** + * \brief Typed version of container::untyped::Vector. + */ +template <class T> class Vector: public Collection <T> +{ +public: + inline Vector(int initSize, bool ownerOfObjects) { + this->base = new untyped::Vector(initSize, ownerOfObjects); } + ~Vector() { delete this->base; } + + inline void put(T *newElement, int newPos = -1) + { ((untyped::Vector*)this->base)->put(newElement, newPos); } + inline void insert(T *newElement, int pos) + { ((untyped::Vector*)this->base)->insert(newElement, pos); } + inline void remove(int pos) { ((untyped::Vector*)this->base)->remove(pos); } + inline T *get(int pos) + { return (T*)((untyped::Vector*)this->base)->get(pos); } + inline int size() { return ((untyped::Vector*)this->base)->size(); } + inline void clear() { ((untyped::Vector*)this->base)->clear(); } + inline void sort() { ((untyped::Vector*)this->base)->sort(); } +}; + + +/** + * \brief Typed version of container::untyped::List. + */ +template <class T> class List: public Collection <T> +{ +public: + inline List(bool ownerOfObjects) + { this->base = new untyped::List(ownerOfObjects); } + ~List() { delete this->base; } + + inline void clear() { ((untyped::List*)this->base)->clear(); } + inline void append(T *element) + { ((untyped::List*)this->base)->append(element); } + inline bool removeRef(T *element) { + return ((untyped::List*)this->base)->removeRef(element); } + inline bool remove(T *element) { + return ((untyped::List*)this->base)->remove(element); } + inline bool detachRef(T *element) { + return ((untyped::List*)this->base)->detachRef(element); } + + inline int size() { return ((untyped::List*)this->base)->size(); } + inline bool isEmpty() + { return ((untyped::List*)this->base)->isEmpty(); } + inline T *getFirst() + { return (T*)((untyped::List*)this->base)->getFirst(); } + inline T *getLast() + { return (T*)((untyped::List*)this->base)->getLast(); } +}; + + +/** + * \brief Typed version of container::untyped::HashTable. + */ +template <class K, class V> class HashTable: public Collection <K> +{ +public: + inline HashTable(bool ownerOfKeys, bool ownerOfValues, int tableSize = 251) + { this->base = new untyped::HashTable(ownerOfKeys, ownerOfValues, + tableSize); } + ~HashTable() { delete this->base; } + + inline void put(K *key, V *value) + { return ((untyped::HashTable*)this->base)->put(key, value); } + inline bool contains(K *key) + { return ((untyped::HashTable*)this->base)->contains(key); } + inline V *get(K *key) + { return (V*)((untyped::HashTable*)this->base)->get(key); } + inline bool remove(K *key) + { return ((untyped::HashTable*)this->base)->remove(key); } + inline K *getKey(K *key) + { return (K*)((untyped::HashTable*)this->base)->getKey(key); } +}; + +/** + * \brief Typed version of container::untyped::Stack. + */ +template <class T> class Stack: public Collection <T> +{ +public: + inline Stack (bool ownerOfObjects) + { this->base = new untyped::Stack (ownerOfObjects); } + ~Stack() { delete this->base; } + + inline void push (T *object) { + ((untyped::Stack*)this->base)->push (object); } + inline void pushUnder (T *object) + { ((untyped::Stack*)this->base)->pushUnder (object); } + inline T *getTop () + { return (T*)((untyped::Stack*)this->base)->getTop (); } + inline void pop () { ((untyped::Stack*)this->base)->pop (); } + inline int size() { return ((untyped::Stack*)this->base)->size(); } +}; + +} // namespace untyped + +} // namespace container + +} // namespace lout + +#endif // __LOUT_CONTAINER_HH_ diff --git a/lout/debug.hh b/lout/debug.hh new file mode 100644 index 00000000..a2c08393 --- /dev/null +++ b/lout/debug.hh @@ -0,0 +1,152 @@ +#ifndef __LOUT_DEBUG_H__ +#define __LOUT_DEBUG_H__ + +/* + * Simple debug messages. Add: + * + * #define DEBUG_LEVEL <n> + * #include "debug.h" + * + * to the file you are working on, or let DEBUG_LEVEL undefined to + * disable all messages. A higher level denotes a greater importance + * of the message. + */ + +#include <stdio.h> + +#define D_STMT_START do +#define D_STMT_END while (0) + +# ifdef DEBUG_LEVEL +# define DEBUG_MSG(level, ...) \ + D_STMT_START { \ + if (DEBUG_LEVEL && (level) >= DEBUG_LEVEL) \ + printf(__VA_ARGS__); \ + } D_STMT_END +# else +# define DEBUG_MSG(level, ...) +# endif /* DEBUG_LEVEL */ + + + +/* + * Following is experimental, and will be explained soon. + */ + +#ifdef DBG_RTFL + +#include <unistd.h> +#include <stdio.h> + +#define DBG_MSG(obj, aspect, prio, msg) \ + D_STMT_START { \ + printf ("[rtfl]%s:%d:%d:msg:%p:%s:%d:%s\n", \ + __FILE__, __LINE__, getpid(), obj, aspect, prio, msg); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_MSGF(obj, aspect, prio, fmt, ...) \ + D_STMT_START { \ + printf ("[rtfl]%s:%d:%d:msg:%p:%s:%d:" fmt "\n", \ + __FILE__, __LINE__, getpid(), obj, aspect, prio, __VA_ARGS__); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_MSG_START(obj) \ + D_STMT_START { \ + printf ("[rtfl]%s:%d:%d:msg-start:%p\n", \ + __FILE__, __LINE__, getpid(), obj); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_MSG_END(obj) \ + D_STMT_START { \ + printf ("[rtfl]%s:%d:%d:msg-end:%p\n", \ + __FILE__, __LINE__, getpid(), obj); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_CREATE(obj, klass) \ + D_STMT_START { \ + printf ("[rtfl]%s:%d:%d:obj-create:%p:%s\n", \ + __FILE__, __LINE__, getpid(), obj, klass); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ASSOC(child, parent) \ + D_STMT_START { \ + printf ("[rtfl]%s:%d:%d:obj-assoc:%p:%p\n", \ + __FILE__, __LINE__, getpid(), child, parent); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_SET_NUM(obj, var, val) \ + D_STMT_START { \ + printf ("[rtfl]%s:%d:%d:obj-set:%p:%s:%d\n", \ + __FILE__, __LINE__, getpid(), obj, var, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_SET_STR(obj, var, val) \ + D_STMT_START { \ + printf ("[rtfl]%s:%d:%d:obj-set:%p:%s:%s\n", \ + __FILE__, __LINE__, getpid(), obj, var, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_SET_PTR(obj, var, val) \ + D_STMT_START { \ + printf ("[rtfl]%s:%d:%d:obj-set:%p:%s:%p\n", \ + __FILE__, __LINE__, getpid(), obj, var, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ARRSET_NUM(obj, var, ind, val) \ + D_STMT_START { \ + printf ("[rtfl]%s:%d:%d:obj-set:%p:" var ":%d\n", \ + __FILE__, __LINE__, getpid(), obj, ind, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ARRSET_STR(obj, var, ind, val) \ + D_STMT_START { \ + printf ("[rtfl]%s:%d:%d:obj-set:%p:" var ":%s\n", \ + __FILE__, __LINE__, getpid(), obj, ind, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ARRSET_PTR(obj, var, ind, val) \ + D_STMT_START { \ + printf ("[rtfl]%s:%d:%d:obj-set:%p:" var ":%p\n", \ + __FILE__, __LINE__, getpid(), obj, ind, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_COLOR(klass, color) \ + D_STMT_START { \ + printf ("[rtfl]%s:%d:%d:obj-color:%s:%s\n", \ + __FILE__, __LINE__, getpid(), klass, color); \ + fflush (stdout); \ + } D_STMT_END + +#else /* DBG_RTFL */ + +#define DBG_MSG(obj, aspect, prio, msg) +#define DBG_MSGF(obj, aspect, prio, fmt, ...) +#define DBG_MSG_START(obj) +#define DBG_MSG_END(obj) +#define DBG_OBJ_CREATE(obj, klass) +#define DBG_OBJ_ASSOC(child, parent) +#define DBG_OBJ_SET_NUM(obj, var, val) +#define DBG_OBJ_SET_STR(obj, var, val) +#define DBG_OBJ_SET_PTR(obj, var, val) +#define DBG_OBJ_ARRSET_NUM(obj, var, ind, val) +#define DBG_OBJ_ARRSET_STR(obj, var, ind, val) +#define DBG_OBJ_ARRSET_PTR(obj, var, ind, val) +#define DBG_OBJ_COLOR(klass, color) + +#endif /* DBG_RTFL */ + +#endif /* __LOUT_DEBUG_H__ */ + + diff --git a/lout/identity.cc b/lout/identity.cc new file mode 100644 index 00000000..5e4965f1 --- /dev/null +++ b/lout/identity.cc @@ -0,0 +1,114 @@ +/* + * Dillo Widget + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + + +#include "identity.hh" + +#include <stdio.h> + +namespace lout { +namespace identity { + +// ------------------------ +// IdentifiableObject +// ------------------------ + +using namespace object; +using namespace container::typed; + +IdentifiableObject::Class::Class (IdentifiableObject::Class *parent, int id, + const char *className) +{ + this->parent = parent; + this->id = id; + this->className = className; +} + +HashTable <ConstString, IdentifiableObject::Class> + *IdentifiableObject::classesByName = + new HashTable<ConstString, IdentifiableObject::Class> (true, true); +Vector <IdentifiableObject::Class> *IdentifiableObject::classesById = + new Vector <IdentifiableObject::Class> (16, false); +IdentifiableObject::Class *IdentifiableObject::currentlyConstructedClass; + +IdentifiableObject::IdentifiableObject () +{ + currentlyConstructedClass = NULL; +} + +void IdentifiableObject::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append("<instance of "); + sb->append(getClassName()); + sb->append(">"); +} + +/** + * \brief This method must be called in the constructor for the sub class. + * See class comment for details. + */ +void IdentifiableObject::registerName (const char *className, int *classId) +{ + ConstString str (className); + Class *klass = classesByName->get (&str); + if (klass == NULL) { + klass = new Class (currentlyConstructedClass, classesById->size (), + className); + ConstString *key = new ConstString (className); + classesByName->put (key, klass); + classesById->put (klass); + } + + this->classId = klass->id; + currentlyConstructedClass = klass; +} + +/** + * \brief Returns, whether this class is an instance of the class, given by + * \em otherClassId, or of a sub class of this class. + */ +bool IdentifiableObject::instanceOf (int otherClassId) +{ + if (otherClassId == -1) + // Other class has not been registered yet, while it should have been, + // if this class is an instance of it or of a sub-class. + return false; + + Class *otherClass = classesById->get (otherClassId); + + if (otherClass == NULL) { + fprintf (stderr, + "WARNING: Something got wrong here, it seems that a " + "CLASS_ID was not initialized properly.\n"); + return false; + } + + for (Class *klass = classesById->get (classId); klass != NULL; + klass = klass->parent) { + if (klass == otherClass) + return true; + } + + return false; +} + +} // namespace identity +} // namespace lout diff --git a/lout/identity.hh b/lout/identity.hh new file mode 100644 index 00000000..7dcdbac4 --- /dev/null +++ b/lout/identity.hh @@ -0,0 +1,148 @@ +#ifndef __LOUT_OBJECTX_HH__ +#define __LOUT_OBJECTX_HH__ + +#include "object.hh" +#include "container.hh" +#include "signal.hh" + +/** + * \brief Some stuff to identify classes of objects at run-time. + */ + +namespace lout { + +namespace identity { + +/** + * \brief Instances of classes, which are sub classes of this class, may + * be identified at run-time. + * + * <h3>Testing the class</h3> + * + * Since e.g. dw::Textblock is a sub class of IdentifiableObject, and + * implemented in the correct way (as described below), for any given + * IdentifiableObject the following test can be done: + * + * \code + * identity::IdentifiableObject *o; + * // ... + * bool isATextblock = o->instanceOf(dw::Textblock::CLASS_ID); + * \endcode + * + * \em isATextblock is true, when \em o is an instance of dw::Textblock, + * or of a sub class of dw::Textblock. Otherwise, \em isATextblock is false. + * + * It is also possible to get the class identifier of an + * identity::IdentifiableObject, e.g. + * + * \code + * bool isOnlyATextblock = o->getClassId() == dw::Textblock::CLASS_ID; + * \endcode + * + * would result in true, if o is an instance of dw::Textblock, but not an + * instance of a sub class of dw::Textblock. + * + * <h3>Defining Sub Classes</h3> + * + * Each direct or indirect sub class of IdentifiableObject must + * + * <ul> + * <li> add a static int CLASS_ID with -1 as initial value, and + * <li> call registerName (\em name, &CLASS_ID) in the constructor, where + * \em name should be unique, e.g. the fully qualified class name. + * </ul> + * + * After this, <i>class</i>::CLASS_ID refers to a number, which denotes the + * class. (If this is still -1, since the class has not yet been instanciated, + * any test will fail, which is correct.) + * + * <h3>Notes on implementation</h3> + * + * If there are some classes like this: + * + * \dot + * digraph G { + * node [shape=record, fontname=Helvetica, fontsize=10]; + * edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica, + * labelfontsize=10, color="#404040", labelfontcolor="#000080"]; + * fontname=Helvetica; fontsize=10; + * IdentifiableObject [color="#a0a0a0"]; + * A; + * B [color="#a0a0a0"]; + * C; + * IdentifiableObject -> A; + * IdentifiableObject -> B; + * B -> C; + * } + * \enddot + * + * <center>[\ref uml-legend "legend"]</center> + * + * and first, an instance of A, and then an instance of C is created, there + * will be the following calls of functions and constructors: + * + * <ol> + * <li> %IdentifiableObject (); + * <li> %registerName ("A", &A::CLASS_ID); + * <li> %IdentifiableObject (); + * <li> %registerName ("B", &B::CLASS_ID); + * <li> %registerName ("C", &C::CLASS_ID); + * </ol> + * + * From this, the class hierarchy above can easily constructed, and stored + * in identity::IdentifiableObject::classesByName and + * in identity::IdentifiableObject::classesById. See the code for details. + * + * N.b. Multiple inheritance is not supported, the construction of the + * tree would become confused. + */ +class IdentifiableObject: public object::Object +{ +private: + class Class: public object::Object + { + public: + Class *parent; + int id; + const char *className; + + Class (Class *parent, int id, const char *className); + }; + + static container::typed::HashTable <object::ConstString, + Class> *classesByName; + static container::typed::Vector <Class> *classesById; + static Class *currentlyConstructedClass; + + int classId; + +protected: + void registerName (const char *className, int *classId); + +public: + IdentifiableObject (); + + virtual void intoStringBuffer(misc::StringBuffer *sb); + + /** + * \brief Returns the class identifier. + * + * This is rarely used, rather, tests with + * identity::IdentifiableObject::instanceOf are done. + */ + int getClassId () { return classId; } + + /** + * \brief Return the name, under which the class of this object was + * registered. + */ + const char *getClassName() { return classesById->get(classId)->className; } + + bool instanceOf (int otherClassId); +}; + +} // namespace identity + +} // namespace lout + +#endif // __LOUT_OBJECTX_HH__ diff --git a/lout/misc.cc b/lout/misc.cc new file mode 100644 index 00000000..2008737b --- /dev/null +++ b/lout/misc.cc @@ -0,0 +1,237 @@ +/* + * Dillo Widget + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + + +#include "misc.hh" + +#include <ctype.h> +#include <config.h> + +#define PRGNAME PACKAGE "/" VERSION + +namespace lout { + +namespace misc { + +const char *prgName = PRGNAME; + +void init (int argc, char *argv[]) +{ + prgName = strdup (argv[0]); +} + +void chop (char *s) +{ + char *p = s + strlen (s) - 1; + while (*p == '\n') { + *p = 0; + p--; + } +} + +char *strip (char *s) +{ + while (isspace (*s)) + s++; + + char *p = s + strlen (s) - 1; + while (isspace (*p)) { + *p = 0; + p--; + } + + return s; +} + +// ---------------- +// Comparable +// ---------------- + +Comparable::~Comparable() +{ +} + +/** + * \brief This static method may be used as compare function for qsort(3), for + * an array of Object* (Object*[] or Object**). + */ +int Comparable::compareFun(const void *p1, const void *p2) +{ + Comparable **c1 = (Comparable**)p1; + Comparable **c2 = (Comparable**)p2; + if(c1 && c2) + return ((*c1)->compareTo(*c2)); + else if(c1) + return 1; + else if(c2) + return -1; + else + return 0; +} + + +// ------------------ +// StringBuffer +// ------------------ + + +StringBuffer::StringBuffer() +{ + firstNode = lastNode = NULL; + numChars = 0; + str = NULL; + strValid = false; +} + +StringBuffer::~StringBuffer() +{ + clear (); + if(str) + delete[] str; +} + +/** + * \brief Append a NUL-terminated string to the buffer, without copying. + * + * No copy is made, so this method should only be used in cases, where + * the string would otherwise be freed again. (This method may then + * save some CPU cycles.) + */ +void StringBuffer::appendNoCopy(char *str) +{ + Node *node = new Node(); + node->data = str; + node->next = NULL; + + if(firstNode == NULL) { + firstNode = node; + lastNode = node; + } else { + lastNode->next = node; + lastNode = node; + } + + numChars += strlen(str); + strValid = false; +} + +/** + * \brief Return a NUL-terminated strings containing all appended strings. + * + * The caller does not have to free the string, this is done in + * misc::StringBuffer::~StringBuffer. + */ +const char *StringBuffer::getChars() +{ + if(strValid) + return str; + + if(str) + delete[] str; + str = new char[numChars + 1]; + char *p = str; + + for(Node *node = firstNode; node; node = node->next) { + int l = strlen(node->data); + memcpy(p, node->data, l * sizeof(char)); + p += l; + } + + *p = 0; + strValid = true; + return str; +} + +/** + * \brief Remove all strings appended to the string buffer. + */ +void StringBuffer::clear () +{ + Node *node, *nextNode; + for(node = firstNode; node; node = nextNode) { + nextNode = node->next; + delete node->data; + delete node; + } + firstNode = lastNode = NULL; + numChars = 0; + strValid = false; +} + + +// ------------ +// BitSet +// ------------ + +BitSet::BitSet(int initBits) +{ + numBytes = bytesForBits(initBits); + bits = (unsigned char*)malloc(numBytes * sizeof(unsigned char)); + clear(); +} + +BitSet::~BitSet() +{ + free(bits); +} + +void BitSet::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append("["); + for(int i = 0; i < numBytes; i++) + sb->append(get(i) ? "1" : "0"); + sb->append("]"); +} + +bool BitSet::get(int i) +{ + if(8 * i >= numBytes) + return false; + else + return bits[i / 8] & (1 << (i % 8)); +} + +void BitSet::set(int i, bool val) +{ + if(8 * i >= numBytes) { + int newNumBytes = numBytes; + while(8 * i >= newNumBytes) + newNumBytes *= 2; + bits = + (unsigned char*)realloc(bits, newNumBytes * sizeof(unsigned char)); + memset(bits + numBytes, 0, newNumBytes - numBytes); + numBytes = newNumBytes; + } + + if(val) + bits[i / 8] |= (1 << (i % 8)); + else + bits[i / 8] &= ~(1 << (i % 8)); +} + +void BitSet::clear() +{ + memset(bits, 0, numBytes); +} + +} // namespace misc + +} // namespace lout diff --git a/lout/misc.hh b/lout/misc.hh new file mode 100644 index 00000000..f9184bf3 --- /dev/null +++ b/lout/misc.hh @@ -0,0 +1,320 @@ +#ifndef __LOUT_MISC_HH__ +#define __LOUT_MISC_HH__ + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +/** + * \brief Miscellaneous stuff, which does not fit anywhere else. + * + * Actually, the other parts, beginning with ::object, depend on this. + */ +namespace lout { + +namespace misc { + +template <class T> inline T min (T a, T b) { return a < b ? a : b; } +template <class T> inline T max (T a, T b) { return a > b ? a : b; } + +template <class T> inline T min (T a, T b, T c) +{ + return (min (a, min (b, c))); +} +template <class T> inline T max (T a, T b, T c) +{ + return (max (a, max (b, c))); +} + +extern const char *prgName; + +void init (int argc, char *argv[]); +void chop (char *s); +char *strip (char *s); + + +inline void assertNotReached () +{ + fprintf (stderr, "*** [%s] This should not happen! ***\n", prgName); + abort (); +} + +/** + * \brief Instances of a sub class of this interface may be compared (less, + * greater). + * + * Used for sorting etc. + */ +class Comparable +{ +public: + virtual ~Comparable(); + + /** + * \brief Compare two objects c1 and c2. + * + * return a value < 0, when c1 is less than c2, a value > 0, when c1 + * is greater than c2, or 0, when c1 and c2 are equal. + * + * If also object::Object is implemented, and if c1.equals(c2), + * c1.compareTo(c2) must be 0, but, unlike you may expect, + * the reversed is not necessarily true. This method returns 0, if, + * according to the rules for sorting, there is no difference, but there + * may still be differences (not relevant for sorting), which "equals" will + * care about. + */ + virtual int compareTo(Comparable *other) = 0; + + static int compareFun(const void *p1, const void *p2); +}; + +/** + * \brief Simple (simpler than container::untyped::Vector and + * container::typed::Vector) template based vector. + */ +template <class T> class SimpleVector +{ +private: + enum { + /** + * \brief Edit this for debugging. Should be optimized by the compiler. + */ + BOUND_CHECKING = 1 + }; + + T *array; + int num, numAlloc; + + inline void resize () + { + /* This algorithm was tunned for memory&speed with this huge page: + * http://downloads.mysql.com/docs/refman-6.0-en.html.tar.gz + */ + if (array == NULL) { + this->numAlloc = 1; + this->array = (T*) malloc (sizeof (T)); + } + if (this->numAlloc < this->num) { + this->numAlloc = (this->num < 100) ? + this->num : this->num + this->num/10; + this->array = + (T*) realloc(this->array, (this->numAlloc * sizeof (T))); + } + } + +public: + inline SimpleVector (int initAlloc) + { + this->num = 0; + this->numAlloc = initAlloc; + this->array = NULL; + } + + inline ~SimpleVector () + { + if (this->array) + free (this->array); + } + + /** + * \brief Return the number of elements put into this vector. + */ + inline int size() { return this->num; } + + inline T* getArray() { return array; } + + /** + * \brief Increase the vector size by one. + * + * May be necessary before calling misc::SimpleVector::set. + */ + inline void increase() { this->num++; this->resize (); } + + /** + * \brief Set the size explicitely. + * + * May be necessary before called before misc::SimpleVector::set. + */ + inline void setSize(int newSize) { this->num = newSize; this->resize (); } + + /** + * \brief Set the size explicitely and initialize new values. + * + * May be necessary before called before misc::SimpleVector::set. + */ + inline void setSize (int newSize, T t) { + int oldSize = this->num; + setSize (newSize); + for (int i = oldSize; i < newSize; i++) + set (i, t); + } + + /** + * \brief Return the reference of one element. + * + * \sa misc::SimpleVector::get + */ + inline T* getRef (int i) { + if (BOUND_CHECKING) + assert (i >= 0 && i < this->num); + return array + i; + } + + /** + * \brief Return the one element, explicitety. + * + * The element is copied, so for complex elements, you should rather used + * misc::SimpleVector::getRef. + */ + inline T get (int i) { + if (BOUND_CHECKING) + assert (i >= 0 && i < this->num); + return this->array[i]; + } + + /** + * \brief Store an object in the vector. + * + * Unlike in container::untyped::Vector and container::typed::Vector, + * you have to care about the size, so a call to + * misc::SimpleVector::increase or misc::SimpleVector::setSize may + * be necessary before. + */ + inline void set (int i, T t) { + if (BOUND_CHECKING) + assert (i >= 0 && i < this->num); + this->array[i] = t; + } +}; + + +/** + * \brief A class for fast concatenation of a large number of strings. + */ +class StringBuffer +{ +private: + struct Node + { + char *data; + Node *next; + }; + + Node *firstNode, *lastNode; + int numChars; + char *str; + bool strValid; + +public: + StringBuffer(); + ~StringBuffer(); + + /** + * \brief Append a NUL-terminated string to the buffer, with copying. + * + * A copy is kept in the buffer, so the caller does not have to care + * about memory management. + */ + inline void append(const char *str) { appendNoCopy(strdup(str)); } + void appendNoCopy(char *str); + const char *getChars(); + void clear (); +}; + + +/** + * \brief A bit set, which automatically reallocates when needed. + */ +class BitSet +{ +private: + unsigned char *bits; + int numBytes; + + inline int bytesForBits(int bits) { return bits == 0 ? 1 : (bits + 7) / 8; } + +public: + BitSet(int initBits); + ~BitSet(); + void intoStringBuffer(misc::StringBuffer *sb); + bool get(int i); + void set(int i, bool val); + void clear(); +}; + +/** + * \brief A simple allocator optimized to handle many small chunks of memory. + * The chunks can not be free'd individually. Instead the whole zone must be + * free'd with zoneFree(). + */ +class ZoneAllocator +{ +private: + size_t poolSize, poolLimit, freeIdx; + SimpleVector <char*> *pools; + SimpleVector <char*> *bulk; + +public: + ZoneAllocator (size_t poolSize) { + this->poolSize = poolSize; + this->poolLimit = poolSize / 4; + this->freeIdx = poolSize; + this->pools = new SimpleVector <char*> (1); + this->bulk = new SimpleVector <char*> (1); + }; + + ~ZoneAllocator () { + zoneFree (); + delete pools; + delete bulk; + } + + inline void * zoneAlloc (size_t t) { + void *ret; + + if (t > poolLimit) { + bulk->increase (); + bulk->set (bulk->size () - 1, (char*) malloc (t)); + return bulk->get (bulk->size () - 1); + } + + if (t > poolSize - freeIdx) { + pools->increase (); + pools->set (pools->size () - 1, (char*) malloc (poolSize)); + freeIdx = 0; + } + + ret = pools->get (pools->size () - 1) + freeIdx; + freeIdx += t; + return ret; + } + + inline void zoneFree () { + for (int i = 0; i < pools->size (); i++) + free (pools->get (i)); + pools->setSize (0); + for (int i = 0; i < bulk->size (); i++) + free (bulk->get (i)); + bulk->setSize (0); + freeIdx = poolSize; + } + + inline const char *strndup (const char *str, size_t t) { + char *new_str = (char *) zoneAlloc (t + 1); + memcpy (new_str, str, t); + new_str[t] = '\0'; + return new_str; + } + + inline const char *strdup (const char *str) { + return strndup (str, strlen (str)); + } +}; + +} // namespace misc + +} // namespace lout + +#endif // __LOUT_MISC_HH__ diff --git a/lout/object.cc b/lout/object.cc new file mode 100644 index 00000000..7da124fa --- /dev/null +++ b/lout/object.cc @@ -0,0 +1,311 @@ +/* + * Dillo Widget + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + + +#include "object.hh" +#include <stdio.h> + +namespace lout { + +namespace object { + +// ------------ +// Object +// ------------ + +/** + * \brief The destructor is defined as virtual (but not abstract), so that + * destruction of Object's works properly. + */ +Object::~Object() +{ +} + +/** + * \brief Returns, whether two objects are equal. + * + * The caller should ensure, that this and the object have the same class; + * this makes casting of "other" safe. Typically, an implementation should + * check this == other first, the caller can assume a fast implementation. + */ +bool Object::equals(Object *other) +{ + misc::assertNotReached (); + return false; +} + +/** + * \brief Return a hash value for the object. + */ +int Object::hashValue() +{ + fprintf (stderr, "Object::hashValue() should be implemented.\n"); + return 0; +} + +/** + * \brief Return an exact copy of the object. + */ +Object *Object::clone() +{ + misc::assertNotReached (); + return NULL; +} + +/** + * \brief Use object::Object::intoStringBuffer to return a textual + * representation of the object. + * + * The caller does not have to free the memory, object::Object is responsible + * for this. + */ +const char *Object::toString() +{ + /** \todo garbage! */ + misc::StringBuffer sb; + intoStringBuffer(&sb); + char *s = strdup(sb.getChars()); + return s; +} + +/** + * \brief Store a textual representation of the object in a misc::StringBuffer. + * + * This is used by object::Object::toString. + */ +void Object::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append("<not further specified object>"); +} + +/** + * \brief Return the number of bytes, this object totally uses. + */ +size_t Object::sizeOf() +{ + fprintf (stderr, "Object::sizeOf() should be implemented.\n"); + return sizeof(Object*); +} + +// ------------- +// Pointer +// ------------- + +bool Pointer::equals(Object *other) +{ + return value == ((Pointer*)other)->value; +} + +int Pointer::hashValue() +{ +/* For some unknown reason, this doesn't compile on some 64bit platforms: + * + * if (sizeof (int) == sizeof (void*)) + * return (int)value; + * else + * return ((int*)value)[0] ^ ((int*)value)[1]; + */ +#if SIZEOF_VOID_P == 4 + return (int)value; +#else + return ((int*)value)[0] ^ ((int*)value)[1]; +#endif +} + +void Pointer::intoStringBuffer(misc::StringBuffer *sb) +{ + char buf[64]; + snprintf(buf, sizeof(buf), "0x%p", value); + sb->append(buf); +} + +// ------------- +// Integer +// ------------- + +bool Integer::equals(Object *other) +{ + return value == ((Integer*)other)->value; +} + +int Integer::hashValue() +{ + return (int)value; +} + +void Integer::intoStringBuffer(misc::StringBuffer *sb) +{ + char buf[64]; + sprintf(buf, "%d", value); + sb->append(buf); +} + +int Integer::compareTo(Comparable *other) +{ + return value - ((Integer*)other)->value; +} + +// ----------------- +// ConstString +// ----------------- + +bool ConstString::equals(Object *other) +{ + ConstString *otherString = (ConstString*)other; + return + this == other || + (str == NULL && otherString->str == NULL) || + (str != NULL && otherString->str != NULL && + strcmp(str, otherString->str) == 0); +} + +int ConstString::hashValue() +{ + return hashValue(str); +} + + +int ConstString::compareTo(Comparable *other) +{ + String *otherString = (String*)other; + if(str && otherString->str) + return strcmp(str, otherString->str); + else if(str) + return 1; + else if(otherString->str) + return -1; + else + return 0; +} + + +int ConstString::hashValue(const char *str) +{ + if(str) { + int h = 0; + for (int i = 0; str[i]; i++) + h = (h * 256 + str[i]); + return h; + } else + return 0; +} + +void ConstString::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append(str); +} + +// ------------ +// String +// ------------ + +String::String (const char *str): ConstString (str ? strdup(str) : NULL) +{ +} + +String::~String () +{ + if(str) + delete str; +} + +// ------------ +// Pair +// ------------ + +PairBase::PairBase(Object *first, Object *second) +{ + this->first = first; + this->second = second; +} + +PairBase::~PairBase() +{ + if(first) + delete first; + if(second) + delete second; +} + +bool PairBase::equals(Object *other) +{ + PairBase *otherPair = (PairBase*)other; + + return + // Identical? + this == other || + (// Both first parts are NULL, ... + (first == NULL && otherPair->first == NULL) || + // ... or both first parts are not NULL and equal + (first != NULL && otherPair->first != NULL + && first->equals (otherPair->first))) && + // Same with second part. + ((second == NULL && otherPair->second == NULL) || + (second != NULL && otherPair->second != NULL + && second->equals (otherPair->second))); +} + +int PairBase::hashValue() +{ + int value = 0; + + if(first) + value ^= first->hashValue(); + if(second) + value ^= second->hashValue(); + + return value; +} + +void PairBase::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append("<pair: "); + + if(first) + first->intoStringBuffer(sb); + else + sb->append("(nil)"); + + sb->append(","); + + if(second) + second->intoStringBuffer(sb); + else + sb->append("(nil)"); + + sb->append(">"); +} + +size_t PairBase::sizeOf() +{ + size_t size = 0; + + if(first) + size += first->sizeOf(); + if(second) + size += second->sizeOf(); + + return size; +} + +} // namespace object + +} // namespace lout diff --git a/lout/object.hh b/lout/object.hh new file mode 100644 index 00000000..7d505c99 --- /dev/null +++ b/lout/object.hh @@ -0,0 +1,161 @@ +#ifndef __LOUT_OBJECT_HH__ +#define __LOUT_OBJECT_HH__ + +#include <stdlib.h> +#include <string.h> + +#include "misc.hh" + +/** + * \brief Here, some common classes (or interfaces) are defined, to standardize + * the access to other classes. + */ +namespace lout { + +namespace object { + +/** + * \brief This is the base class for many other classes, which defines very + * common virtual methods. + * + * For convenience, none of them are abstract, but they + * must be defined, when they are needed, especially for containers. + */ +class Object +{ +public: + virtual ~Object(); + virtual bool equals(Object *other); + virtual int hashValue(); + virtual Object *clone(); + virtual void intoStringBuffer(misc::StringBuffer *sb); + const char *toString(); + virtual size_t sizeOf(); +}; + +/** + * \brief An object::Object wrapper for void pointers. + */ +class Pointer: public Object +{ +private: + void *value; + +public: + Pointer(void *value) { this->value = value; } + bool equals(Object *other); + int hashValue(); + void intoStringBuffer(misc::StringBuffer *sb); + inline void *getValue() { return value; } +}; + +/** + * \brief A typed version of object::Pointer. + */ +template <class T> class TypedPointer: public Pointer +{ +public: + inline TypedPointer(T *value) : Pointer ((void*)value) { } + inline T *getTypedValue() { return (T*)getValue(); } +}; + + +/** + * \brief An object::Object wrapper for int's. + */ +class Integer: public Object, misc::Comparable +{ + int value; + +public: + Integer(int value) { this->value = value; } + bool equals(Object *other); + int hashValue(); + void intoStringBuffer(misc::StringBuffer *sb); + int compareTo(Comparable *other); + inline int getValue() { return value; } +}; + + +/** + * \brief An object::Object wrapper for constant strings (char*). + * + * As opposed to object::String, the char array is not copied. + */ +class ConstString: public Object, misc::Comparable +{ +protected: + const char *str; + +public: + ConstString(const char *str) { this->str = str; } + bool equals(Object *other); + int hashValue(); + int compareTo(Comparable *other); + void intoStringBuffer(misc::StringBuffer *sb); + + inline const char *chars() { return str; } + + static int hashValue(const char *str); +}; + + +/** + * \brief An object::Object wrapper for strings (char*). + * + * As opposed to object::ConstantString, the char array is copied. + */ +class String: public ConstString +{ +public: + String(const char *str); + ~String(); +}; + +/** + * \todo Comment + */ +class PairBase: public Object +{ +protected: + Object *first, *second; + +public: + PairBase(Object *first, Object *second); + ~PairBase(); + + bool equals(Object *other); + int hashValue(); + void intoStringBuffer(misc::StringBuffer *sb); + size_t sizeOf(); +}; + +/** + * \todo Comment + */ +class Pair: public PairBase +{ +public: + Pair(Object *first, Object *second): PairBase (first, second) { } + + inline Object *getFirst () { return first; } + inline Object *getSecond () { return second; } +}; + +/** + * \todo Comment + */ +template <class F, class S> class TypedPair: public PairBase +{ +public: + TypedPair(F *first, S *second): PairBase (first, second) { } + + inline F *getFirst () { return first; } + inline S *getSecond () { return second; } +}; + +} // namespace object + +} // namespace lout + +#endif // __LOUT_OBJECT_HH__ diff --git a/lout/signal.cc b/lout/signal.cc new file mode 100644 index 00000000..46aae626 --- /dev/null +++ b/lout/signal.cc @@ -0,0 +1,171 @@ +/* + * Dillo Widget + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + + +#include "signal.hh" + +namespace lout { +namespace signal { + +using namespace container::typed; + +// ------------ +// Emitter +// ------------ + +Emitter::Emitter () +{ + receivers = new List <Receiver> (false); +} + +Emitter::~Emitter () +{ + for(Iterator<Receiver> it = receivers->iterator (); it.hasNext (); ) { + Receiver *receiver = it.getNext (); + receiver->unconnectFrom (this); + } + delete receivers; +} + +void Emitter::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append ("<emitter: "); + receivers->intoStringBuffer (sb); + sb->append (">"); +} + +void Emitter::unconnect (Receiver *receiver) +{ + receivers->removeRef (receiver); +} + +/** + * \brief Connect a receiver to the emitter. + * + * This is protected, a sub class should define a wrapper, with the respective + * receiver as an argument, to gain type safety. + */ +void Emitter::connect (Receiver *receiver) +{ + receivers->append (receiver); + receiver->connectTo (this); +} + +/** + * \brief Emit a void signal. + * + * This method should be called by a wrapper (return value void), which + * \em folds the signal, and delegates the emission to here. + */ +void Emitter::emitVoid (int signalNo, int argc, Object **argv) +{ + for(Iterator <Receiver> it = receivers->iterator (); it.hasNext (); ) { + Receiver *receiver = it.getNext(); + emitToReceiver (receiver, signalNo, argc, argv); + } +} + +/** + * \brief Emit a boolean signal. + * + * This method should be called by a wrapper, which \em folds the signal, + * delegates the emission to here, and returns the same boolean value. + */ +bool Emitter::emitBool (int signalNo, int argc, Object **argv) +{ + bool b = false, bt; + + for(Iterator <Receiver> it = receivers->iterator (); it.hasNext (); ) { + Receiver *receiver = it.getNext(); + // Note: All receivers are called, even if one returns true. + // Therefore, something like + // b = b || emitToReceiver (receiver, signalNo, argc, argv); + // does not work. + bt = emitToReceiver (receiver, signalNo, argc, argv); + b = b || bt; + } + + return b; +} + + +// -------------- +// Receiver +// -------------- + +Receiver::Receiver() +{ + emitters = new List <Emitter> (false); +} + +Receiver::~Receiver() +{ + for(Iterator<Emitter> it = emitters->iterator(); it.hasNext(); ) { + Emitter *emitter = it.getNext(); + emitter->unconnect (this); + } + delete emitters; +} + +void Receiver::intoStringBuffer(misc::StringBuffer *sb) +{ + // emitters are not listed, to prevent recursion + sb->append ("<receiver>"); +} + +void Receiver::connectTo(Emitter *emitter) +{ + emitters->append (emitter); +} + +void Receiver::unconnectFrom(Emitter *emitter) +{ + emitters->removeRef (emitter); +} + +// ------------------------ +// ObservedObject +// ------------------------ + +bool ObservedObject::DeletionEmitter::emitToReceiver (Receiver *receiver, + int signalNo, + int argc, Object **argv) +{ + object::TypedPointer <ObservedObject> *p = + (object::TypedPointer<ObservedObject>*)argv[0]; + ((DeletionReceiver*)receiver)->deleted (p->getTypedValue ()); + return false; +} + +void ObservedObject::DeletionEmitter::emitDeletion (ObservedObject *obj) +{ + object::TypedPointer <ObservedObject> p(obj); + object::Object *argv[1] = { &p }; + emitVoid (0, 1, argv); +} + +ObservedObject::~ObservedObject() +{ + deletionEmitter.emitDeletion (this); +} + +} // namespace signal +} // namespace lout diff --git a/lout/signal.hh b/lout/signal.hh new file mode 100644 index 00000000..c96247be --- /dev/null +++ b/lout/signal.hh @@ -0,0 +1,310 @@ +#ifndef __LOUT_SIGNALS_HH__ +#define __LOUT_SIGNALS_HH__ + +#include "object.hh" +#include "container.hh" + +/** + * \brief This namespace provides base classes to define signals. + * + * By using signals, objects may be connected at run-time, e.g. a general + * button widget may be connected to another application-specific object, + * which reacts on the operations on the button by the user. In this case, + * the button e.g. defines a signal "clicked", which is "emitted" each + * time the user clicks on the button. After the application-specific + * object has been connected to this signal, a specific method of it will + * be called each time, this button emits the "clicked" signal. + * + * Below, we will call the level, on which signals are defined, the + * "general level", and the level, on which the signals are connected, + * the "caller level". + * + * <h3>Defining Signals</h3> + * + * Typically, signals are grouped. To define a signal group \em bar for your + * class \em Foo, you have to define two classes, the emitter and the + * receiver (BarEmitter and BarReceiver), and instanciate the emitter: + * + * \dot + * digraph G { + * node [shape=record, fontname=Helvetica, fontsize=10]; + * edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica, + * labelfontsize=10, color="#404040", labelfontcolor="#000080"]; + * fontname=Helvetica; fontsize=10; + * + * subgraph cluster_signal { + * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + * label="signal"; + * + * Emitter [color="#a0a0a0", URL="\ref signal::Emitter"]; + * Receiver [color="#a0a0a0", URL="\ref signal::Receiver"]; + * } + * + * subgraph cluster_foo { + * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + * label="General (foo)"; + * + * Foo; + * BarEmitter; + * BarReceiver [color="#a0a0a0"]; + * } + * + * Emitter -> BarEmitter; + * Receiver -> BarReceiver; + * Foo -> BarEmitter [arrowhead="open", arrowtail="none", + * headlabel="1", taillabel="1"]; + * } + * \enddot + * + * <center>[\ref uml-legend "legend"]</center> + * + * BarEmitter (class and instance) may be kept private, but BarReceiver must + * be public, since the caller of Foo must create a sub class of it. For + * BarEmitter, several methods must be implemented, see signal::Emitter for + * details. In BarReceiver, only some virtual abstract methods are defined, + * which the caller must implement. In this case, it is recommended to define + * a connectBar(BarReceiver*) method in Foo, which is delegated to the + * BarEmitter. + * + * <h3>Connecting to Signals</h3> + * + * A caller, which wants to connect to a signal, must define a sub class of + * the receiver, and implement the virtual methods. A typical design looks + * like this: + * + * \dot + * digraph G { + * node [shape=record, fontname=Helvetica, fontsize=10]; + * edge [arrowhead="open", arrowtail="none", labelfontname=Helvetica, + * labelfontsize=10, color="#404040", labelfontcolor="#000080"]; + * fontname=Helvetica; fontsize=10; + * + * subgraph cluster_foo { + * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + * label="Generall (foo)"; + * + * BarReceiver [color="#a0a0a0"]; + * } + * + * subgraph cluster_qix { + * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; + * label="Caller (qix)"; + * + * Qix; + * QixBarReceiver; + * } + * + * BarReceiver -> QixBarReceiver [arrowhead="none", arrowtail="empty"]; + * QixBarReceiver -> Qix [headlabel="1", taillabel="*"]; + * } + * \enddot + * + * <center>[\ref uml-legend "legend"]</center> + * + * (We skip "baz" in the canon, for better readability.) + * + * Here, the QixBarReceiver is connected to the Qix, so that the signals can + * be delegated to the Qix. Notice that the receiver gets automatically + * disconnected, when deleted (see signal::Receiver::~Receiver). + * + * <h3>Void and Boolean Signals</h3> + * + * In the simplest case, signal emitting involves calling a list of + * signal receivers (void signals). For boolean signals, the receivers return + * a boolean value, and the result of the signal emission (the return value of + * signal::Emitter::emitBool) returns the disjunction of the values returned + * by the receivers. Typically, a receiver states with its return value, + * whether the signal was used in any way, the resulting return value so + * indicates, whether at least one receiver has used the signal. + * + * In Dw, events are processed this way. In the simplest case, they are + * delegated to the parent widget, if the widget does not process them (by + * returning false). As an addition, signals are emitted, and if a receiver + * processes the event, this is handled the same way, as if the widget itself + * would have processed it. + * + * Notice, that also for boolean signals, all receivers are called, even + * after one receiver has already returned true. + * + * <h3>Memory Management</h3> + * + * <h4>Emitters</h4> + * + * Emitters are typically instanciated one, for one object emitting the + * signals. In the example above, the class Foo will contain a field + * "BarEmitter barEmitter" (not as a pointer, "BarEmitter *barEmitter"). + * + * <h4>Receivers</h4> + * + * It is important, that a emitter never deletes a receiver, it just removes + * them from the receivers list. Likewise, when a receiver is deleted, it + * unconnects itself from all emitters. (The same receiver instance can indeed + * be connected to multiple emitters.) So, the caller has to care about + * deleting receivers. + * + * In the example above, something like that will work: + * + * \code + * class Qix + * { + * private: + * class QixBarReceiver + * { + * public: + * Qix *qix; + * // ... + * }; + * + * QixBarReceiver barReceiver; + * + * // ... + * }; + * \endcode + * + * The constructor of Qix should then set \em qix: + * + * \code + * Qix::Qix () + * { + * barReceiver.qix = this. + * // ... + * } + * \endcode + * + * After this, &\em barReceiver can be connected to all instances of + * BarEmitter, also multiple times. + */ +namespace lout { + +namespace signal { + +class Receiver; + +/** + * \brief The base class for signal emitters. + * + * If defining a signal group, a sub class of this class must be defined, + * with + * + * <ul> + * <li> a definition of the different signals (as enumeration), + * <li> an implementation of signal::Emitter::emitToReceiver, + * <li> wrappers for signal::Emitter::emitVoid and signal::Emitter::emitBool, + * respectively (one for each signal), and + * <li> a wrapper for signal::Emitter::connect. + * </ul> + * + * There are two representations of signals: + * + * <ul> + * <li> In the \em unfolded representation, the signal itself is represented + * by the method itself (in the emitter or the receiver), and the + * arguments are represented as normal C++ types. + * + * <li> \em Folding signals means to represent the signal itself by an integer + * number (enumeration), and translate the arguments in an object::Object* + * array. (If a given argument is not an instance of object::Object*, + * the wrappers in ::object can be used.) + * </ul> + * + * \sa ::signal + */ +class Emitter: public object::Object +{ + friend class Receiver; + +private: + container::typed::List <Receiver> *receivers; + + void unconnect (Receiver *receiver); + +protected: + void emitVoid (int signalNo, int argc, Object **argv); + bool emitBool (int signalNo, int argc, Object **argv); + void connect(Receiver *receiver); + + /** + * \brief A sub class must implement this for a call to a single + * receiver. + * + * This methods gets the signal in a \em folded representation, it has + * to unfold it, and pass it to a single receiver. For boolean signals, + * the return value of the receiver must be returned, for void signals, + * the return value is discarded. + */ + virtual bool emitToReceiver (Receiver *receiver, int signalNo, + int argc, Object **argv) = 0; + +public: + Emitter(); + ~Emitter(); + + void intoStringBuffer(misc::StringBuffer *sb); +}; + +/** + * \brief The base class for signal receiver base classes. + * + * If defining a signal group, a sub class of this class must be defined, + * in which only the abstract signal methods must be defined. + * + * \sa ::signal + */ +class Receiver: public object::Object +{ + friend class Emitter; + +private: + container::typed::List<Emitter> *emitters; + + void connectTo(Emitter *emitter); + void unconnectFrom(Emitter *emitter); + +public: + Receiver(); + ~Receiver(); + + void intoStringBuffer(misc::StringBuffer *sb); +}; + +/** + * \brief An observed object has a signal emitter, which tells the + * receivers, when the object is deleted. + */ +class ObservedObject +{ +public: + class DeletionReceiver: public signal::Receiver + { + public: + virtual void deleted (ObservedObject *object) = 0; + }; + +private: + class DeletionEmitter: public signal::Emitter + { + protected: + bool emitToReceiver (signal::Receiver *receiver, int signalNo, + int argc, Object **argv); + + public: + inline void connectDeletion (DeletionReceiver *receiver) + { connect (receiver); } + + void emitDeletion (ObservedObject *obj); + }; + + DeletionEmitter deletionEmitter; + +public: + virtual ~ObservedObject(); + + inline void connectDeletion (DeletionReceiver *receiver) + { deletionEmitter.connectDeletion (receiver); } +}; + +} // namespace signal + +} // namespace lout + +#endif // __LOUT_SIGNALS_HH__ |