Initial import of Dillo

This commit is contained in:
2025-02-28 13:34:30 -05:00
parent bd4e3eebd8
commit 20fea64cb5
496 changed files with 156174 additions and 0 deletions

22
lout/Makefile.am Normal file
View File

@ -0,0 +1,22 @@
AM_CPPFLAGS = \
-I$(top_srcdir) \
-DCUR_WORKING_DIR='"@BASE_CUR_WORKING_DIR@/lout"'
noinst_LIBRARIES = liblout.a
liblout_a_SOURCES = \
container.cc \
container.hh \
debug.hh \
debug_rtfl.hh \
identity.cc \
identity.hh \
misc.cc \
misc.hh \
object.cc \
object.hh \
signal.cc \
signal.hh \
unicode.cc \
unicode.hh \
msg.h

809
lout/container.cc Normal file
View File

@ -0,0 +1,809 @@
/*
* 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, see <http://www.gnu.org/licenses/>.
*/
#define PRINTF(fmt, ...)
//#define PRINTF printf
#include "container.hh"
#include "misc.hh"
#include "debug.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)
{
DBG_OBJ_CREATE ("lout::container::untyped::Vector");
numAlloc = initSize == 0 ? 1 : initSize;
this->ownerOfObjects = ownerOfObjects;
numElements = 0;
array = (Object**)malloc(numAlloc * sizeof(Object*));
}
Vector::~Vector()
{
clear();
free(array);
DBG_OBJ_DELETE ();
}
int Vector::size ()
{
return numElements;
}
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;
void *vp;
assert((vp = realloc(array, numAlloc * sizeof(Object*))));
if (!vp) exit(-2); // when NDEBUG is defined
array = (Object**)vp;
}
// 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++;
if (numElements >= numAlloc) {
// Allocated memory has to be increased.
numAlloc *= 2;
void *vp;
assert((vp = realloc(array, numAlloc * sizeof(Object*))));
if (!vp) exit(-2); // when NDEBUG is defined
array = (Object**)vp;
}
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(Comparator *comparator)
{
Comparator::compareFunComparator = comparator;
qsort (array, numElements, sizeof(Object*), Comparator::compareFun);
}
/**
* \brief Use binary search to find an element in a sorted vector.
*
* If "mustExist" is true, only exact matches are found; otherwise, -1
* is returned. If it is false, the position of the next greater
* element is returned, or, if the key is the greatest element, the
* size of the array. (This is the value which can be used for
* insertion; see insertSortet()).
*/
int Vector::bsearch(Object *key, bool mustExist, int start, int end,
Comparator *comparator)
{
// The case !mustExist is not handled by bsearch(3), so here is a
// new implementation.
DBG_OBJ_MSGF ("container", 0,
"<b>bsearch</b> (<i>key</i>, %s, %d, %d, <i>comparator</i>) "
"[size is %d]",
mustExist ? "true" : "false", start, end, size ());
DBG_OBJ_MSG_START ();
int result = -123; // Compiler happiness: GCC 4.7 does not handle this?
if (start > end) {
DBG_OBJ_MSG ("container", 1, "empty");
result = mustExist ? -1 : start;
} else {
int low = start, high = end;
bool found = false;
while (!found) {
int index = (low + high) / 2;
int c = comparator->compare (key, array[index]);
DBG_OBJ_MSGF ("container", 1,
"searching within %d and %d; compare key with #%d => %d",
low, high, index, c);
if (c == 0) {
found = true;
result = index;
} else {
if (low >= high) {
if (mustExist) {
found = true;
result = -1;
} else {
found = true;
result = c > 0 ? index + 1 : index;
}
}
if (c < 0)
high = index - 1;
else
low = index + 1;
}
}
}
DBG_OBJ_MSGF ("container", 1, "result = %d", result);
DBG_OBJ_MSG_END ();
return result;
}
Object *Vector::VectorIterator::getNext()
{
if (index < vector->numElements - 1)
index++;
return index < vector->numElements ? vector->array[index] : NULL;
}
bool Vector::VectorIterator::hasNext()
{
return index < vector->numElements - 1;
}
Collection0::AbstractIterator* Vector::createIterator()
{
return new VectorIterator(this);
}
// ------------
// List
// ------------
List::List(bool ownerOfObjects)
{
this->ownerOfObjects = ownerOfObjects;
first = last = NULL;
numElements = 0;
}
List::~List()
{
clear();
}
int List::size ()
{
return numElements;
}
bool List::equals(Object *other)
{
List *otherList = (List*)other;
Node *node1 = first, *node2 = otherList->first;
while (node1 != NULL && node2 != NULL ) {
if (!node1->object->equals (node2->object))
return false;
node1 = node1->next;
node2 = node2->next;
}
return node1 == NULL && node2 == NULL;
}
int List::hashValue()
{
int h = 0;
for (Node *node = first; node; node = node->next)
h = h ^ node->object->hashValue ();
return h;
}
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::insertBefore(object::Object *beforeThis, object::Object *neew)
{
Node *beforeCur, *cur;
for (beforeCur = NULL, cur = first; cur; beforeCur = cur, cur = cur->next) {
if (cur->object == beforeThis) {
Node *newNode = new Node;
newNode->next = cur;
newNode->object = neew;
if (beforeCur)
beforeCur->next = newNode;
else
first = newNode;
numElements++;
return true;
}
}
return false;
}
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);
}
// ---------------
// HashSet
// ---------------
HashSet::HashSet(bool ownerOfObjects, int tableSize)
{
this->ownerOfObjects = ownerOfObjects;
this->tableSize = tableSize;
table = new Node*[tableSize];
for (int i = 0; i < tableSize; i++)
table[i] = NULL;
numElements = 0;
}
HashSet::~HashSet()
{
for (int i = 0; i < tableSize; i++) {
Node *n1 = table[i];
while (n1) {
Node *n2 = n1->next;
// It seems appropriate to call "clearNode(n1)" here instead
// of "delete n1->object", but since this is the destructor,
// the implementation of a sub class would not be called
// anymore. This is the reason why HashTable has an
// destructor.
if (ownerOfObjects) {
PRINTF ("- deleting object: %s\n", n1->object->toString());
delete n1->object;
}
delete n1;
n1 = n2;
}
}
delete[] table;
}
int HashSet::size ()
{
return numElements;
}
HashSet::Node *HashSet::createNode()
{
return new Node;
}
void HashSet::clearNode(HashSet::Node *node)
{
if (ownerOfObjects) {
PRINTF ("- deleting object: %s\n", node->object->toString());
delete node->object;
}
}
HashSet::Node *HashSet::findNode(Object *object) const
{
int h = calcHashValue(object);
for (Node *node = table[h]; node; node = node->next) {
if (object->equals(node->object))
return node;
}
return NULL;
}
HashSet::Node *HashSet::insertNode(Object *object)
{
// Look whether object is already contained.
Node *node = findNode(object);
if (node) {
clearNode(node);
numElements--;
} else {
int h = calcHashValue(object);
node = createNode ();
node->next = table[h];
table[h] = node;
numElements++;
}
node->object = object;
return node;
}
void HashSet::put(Object *object)
{
insertNode (object);
}
bool HashSet::contains(Object *object) const
{
int h = calcHashValue(object);
for (Node *n = table[h]; n; n = n->next) {
if (object->equals(n->object))
return true;
}
return false;
}
bool HashSet::remove(Object *object)
{
int h = calcHashValue(object);
Node *last, *cur;
for (last = NULL, cur = table[h]; cur; last = cur, cur = cur->next) {
if (object->equals(cur->object)) {
if (last)
last->next = cur->next;
else
table[h] = cur->next;
clearNode (cur);
delete cur;
numElements--;
return true;
}
}
return false;
// TODO for HashTable: also remove value.
}
// For historical reasons: this method once existed under the name
// "getKey" in HashTable. It could be used to get the real object from
// the table, but it was dangerous, because a change of this object
// would also change the hash value, and so confuse the table.
/*Object *HashSet::getReference (Object *object)
{
int h = calcHashValue(object);
for (Node *n = table[h]; n; n = n->next) {
if (object->equals(n->object))
return n->object;
}
return NULL;
}*/
HashSet::HashSetIterator::HashSetIterator(HashSet *set)
{
this->set = set;
node = NULL;
pos = -1;
gotoNext();
}
void HashSet::HashSetIterator::gotoNext()
{
if (node)
node = node->next;
while (node == NULL) {
if (pos >= set->tableSize - 1)
return;
pos++;
node = set->table[pos];
}
}
Object *HashSet::HashSetIterator::getNext()
{
Object *result;
if (node)
result = node->object;
else
result = NULL;
gotoNext();
return result;
}
bool HashSet::HashSetIterator::hasNext()
{
return node != NULL;
}
Collection0::AbstractIterator* HashSet::createIterator()
{
return new HashSetIterator(this);
}
// ---------------
// HashTable
// ---------------
HashTable::HashTable(bool ownerOfKeys, bool ownerOfValues, int tableSize) :
HashSet (ownerOfKeys, tableSize)
{
this->ownerOfValues = ownerOfValues;
}
HashTable::~HashTable()
{
// See comment in the destructor of HashSet.
for (int i = 0; i < tableSize; i++) {
for (Node *n = table[i]; n; n = n->next) {
if (ownerOfValues) {
Object *value = ((KeyValuePair*)n)->value;
if (value) {
PRINTF ("- deleting value: %s\n", value->toString());
delete value;
}
}
}
}
}
HashSet::Node *HashTable::createNode()
{
return new KeyValuePair;
}
void HashTable::clearNode(HashSet::Node *node)
{
HashSet::clearNode (node);
if (ownerOfValues) {
Object *value = ((KeyValuePair*)node)->value;
if (value) {
PRINTF ("- deleting value: %s\n", value->toString());
delete value;
}
}
}
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->object->intoStringBuffer(sb);
sb->append(" => ");
Object *value = ((KeyValuePair*)node)->value;
if (value)
value->intoStringBuffer(sb);
else
sb->append ("null");
first = false;
}
}
sb->append(" }");
}
void HashTable::put(Object *key, Object *value)
{
KeyValuePair *node = (KeyValuePair*)insertNode(key);
node->value = value;
}
Object *HashTable::get(Object *key) const
{
Node *node = findNode(key);
if (node)
return ((KeyValuePair*)node)->value;
else
return NULL;
}
// -----------
// Stack
// -----------
Stack::Stack (bool ownerOfObjects)
{
this->ownerOfObjects = ownerOfObjects;
bottom = top = NULL;
numElements = 0;
}
Stack::~Stack()
{
while (top)
pop ();
}
int Stack::size ()
{
return numElements;
}
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

572
lout/container.hh Normal file
View File

@ -0,0 +1,572 @@
/*
* Dillo Widget
*
* Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
* Copyright 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __LOUT_CONTAINER_HH_
#define __LOUT_CONTAINER_HH_
#include "object.hh"
namespace lout {
/**
* \brief This namespace contains a framework for container classes, which
* members are instances of object::Object.
*
* A common problem in languages without garbage collection is, where the
* children belong to, and so, who is responsible to delete them (instantiation
* 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 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 instantiated 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; }
virtual int size() = 0;
};
/**
* \brief Container, which is implemented by an array, which is
* dynamically resized.
*/
class Vector: public Collection
{
friend class VectorIterator;
private:
object::Object **array;
int numAlloc, numElements;
bool ownerOfObjects;
class VectorIterator: public AbstractIterator
{
private:
Vector *vector;
int index;
public:
VectorIterator(Vector *vector) { this->vector = vector; index = -1; }
bool hasNext();
Object *getNext();
};
protected:
AbstractIterator* createIterator();
public:
Vector(int initSize, bool ownerOfObjects);
~Vector();
int size();
void put(object::Object *newElement, int newPos = -1);
void insert(object::Object *newElement, int pos);
/**
* \brief Insert into an already sorted vector.
*
* Notice that insertion is not very efficient, unless the position
* is rather at the end.
*/
inline int insertSorted(object::Object *newElement,
object::Comparator *comparator =
&object::standardComparator)
{ int pos = bsearch (newElement, false, comparator);
insert (newElement, pos); return pos; }
void remove(int pos);
inline object::Object *get(int pos) const
{ return (pos >= 0 && pos < numElements) ? array[pos] : NULL; }
void clear();
void sort(object::Comparator *comparator = &object::standardComparator);
int bsearch(Object *key, bool mustExist, int start, int end,
object::Comparator *comparator = &object::standardComparator);
inline int bsearch(Object *key, bool mustExist,
object::Comparator *comparator =
&object::standardComparator)
{ return bsearch (key, mustExist, 0, size () - 1, comparator); }
};
/**
* \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();
bool equals(Object *other);
int hashValue();
int size ();
void clear();
void append(object::Object *element);
bool insertBefore(object::Object *beforeThis, object::Object *neew);
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() const { return numElements; }
inline bool isEmpty() const { return numElements == 0; }
inline object::Object *getFirst() const { return first->object; }
inline object::Object *getLast() const { return last->object; }
};
/**
* \brief A hash set.
*/
class HashSet: public Collection
{
friend class HashSetIterator;
protected:
struct Node
{
object::Object *object;
Node *next;
virtual ~Node() {};
};
Node **table;
int tableSize, numElements;
bool ownerOfObjects;
inline int calcHashValue(object::Object *object) const
{
return abs(object->hashValue()) % tableSize;
}
virtual Node *createNode();
virtual void clearNode(Node *node);
Node *findNode(object::Object *object) const;
Node *insertNode(object::Object *object);
AbstractIterator* createIterator();
private:
class HashSetIterator: public Collection0::AbstractIterator
{
private:
HashSet *set;
HashSet::Node *node;
int pos;
void gotoNext();
public:
HashSetIterator(HashSet *set);
bool hasNext();
Object *getNext();
};
public:
HashSet(bool ownerOfObjects, int tableSize = 251);
~HashSet();
int size ();
void put (object::Object *object);
bool contains (object::Object *key) const;
bool remove (object::Object *key);
//Object *getReference (object::Object *object);
};
/**
* \brief A hash table.
*/
class HashTable: public HashSet
{
private:
bool ownerOfValues;
struct KeyValuePair: public Node
{
object::Object *value;
};
protected:
Node *createNode();
void clearNode(Node *node);
public:
HashTable(bool ownerOfKeys, bool ownerOfValues, int tableSize = 251);
~HashTable();
void intoStringBuffer(misc::StringBuffer *sb);
void put (object::Object *key, object::Object *value);
object::Object *get (object::Object *key) const;
};
/**
* \brief A stack (LIFO). Can be used as Queue (FIFO) when pushUnder()
* is used instead of push().
*
* 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();
int size ();
void push (object::Object *object);
void pushUnder (object::Object *object);
inline object::Object *getTop () const { return top ? top->object : NULL; }
void pop ();
inline int size() const { 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:
Collection () { this->base = NULL; }
~Collection () { if (this->base) delete this->base; }
bool equals(Object *other)
{ return this->base->equals (((Collection<T>*)other)->base); }
int hashValue() { return this->base->hashValue (); }
void intoStringBuffer(misc::StringBuffer *sb)
{ this->base->intoStringBuffer(sb); }
inline Iterator<T> iterator() {
Iterator<T> it; it.base = this->base->iterator(); return it; }
inline int size() { return this->base->size (); }
};
/**
* \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); }
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 int insertSorted(T *newElement,
object::Comparator *comparator =
&object::standardComparator)
{ return ((untyped::Vector*)this->base)->insertSorted(newElement,
comparator); }
inline void remove(int pos) { ((untyped::Vector*)this->base)->remove(pos); }
inline T *get(int pos) const
{ return (T*)((untyped::Vector*)this->base)->get(pos); }
inline void clear() { ((untyped::Vector*)this->base)->clear(); }
inline void sort(object::Comparator *comparator =
&object::standardComparator)
{ ((untyped::Vector*)this->base)->sort(comparator); }
inline int bsearch(T *key, bool mustExist, int start, int end,
object::Comparator *comparator =
&object::standardComparator)
{ return ((untyped::Vector*)this->base)->bsearch(key, mustExist, start, end,
comparator); }
inline int bsearch(T *key, bool mustExist,
object::Comparator *comparator =
&object::standardComparator)
{ return ((untyped::Vector*)this->base)->bsearch(key, mustExist,
comparator); }
};
/**
* \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); }
inline void clear() { ((untyped::List*)this->base)->clear(); }
inline void append(T *element)
{ ((untyped::List*)this->base)->append(element); }
inline bool insertBefore(object::Object *beforeThis, object::Object *neew)
{ return ((untyped::List*)this->base)->insertBefore(beforeThis, neew); }
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 bool isEmpty() const
{ return ((untyped::List*)this->base)->isEmpty(); }
inline T *getFirst() const
{ return (T*)((untyped::List*)this->base)->getFirst(); }
inline T *getLast() const
{ return (T*)((untyped::List*)this->base)->getLast(); }
};
/**
* \brief Typed version of container::untyped::HashSet.
*/
template <class T> class HashSet: public Collection <T>
{
protected:
inline HashSet() { }
public:
inline HashSet(bool owner, int tableSize = 251)
{ this->base = new untyped::HashSet(owner, tableSize); }
inline void put(T *object)
{ return ((untyped::HashSet*)this->base)->put(object); }
inline bool contains(T *object) const
{ return ((untyped::HashSet*)this->base)->contains(object); }
inline bool remove(T *object)
{ return ((untyped::HashSet*)this->base)->remove(object); }
//inline T *getReference(T *object)
//{ return (T*)((untyped::HashSet*)this->base)->getReference(object); }
};
/**
* \brief Typed version of container::untyped::HashTable.
*/
template <class K, class V> class HashTable: public HashSet <K>
{
public:
inline HashTable(bool ownerOfKeys, bool ownerOfValues, int tableSize = 251)
{ this->base = new untyped::HashTable(ownerOfKeys, ownerOfValues,
tableSize); }
inline void put(K *key, V *value)
{ return ((untyped::HashTable*)this->base)->put(key, value); }
inline V *get(K *key) const
{ return (V*)((untyped::HashTable*)this->base)->get(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); }
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 () const
{ return (T*)((untyped::Stack*)this->base)->getTop (); }
inline void pop () { ((untyped::Stack*)this->base)->pop (); }
};
} // namespace untyped
} // namespace container
} // namespace lout
#endif // __LOUT_CONTAINER_HH_

86
lout/debug.hh Normal file
View File

@ -0,0 +1,86 @@
#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)
#define D_STMT_NOP D_STMT_START { } D_STMT_END
# 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 */
#include "debug_rtfl.hh"
/* Some extensions for RTFL dealing with static stuff. */
#ifdef DBG_RTFL
#define DBG_OBJ_MSG_S(aspect, prio, msg) \
RTFL_OBJ_PRINT ("msg", "s:s:d:s", "<static>", aspect, prio, msg)
#define DBG_OBJ_MSGF_S(aspect, prio, fmt, ...) \
STMT_START { \
char msg[256]; \
snprintf (msg, sizeof (msg), fmt, __VA_ARGS__); \
RTFL_OBJ_PRINT ("msg", "s:s:d:s", "<static>", aspect, prio, msg) \
} STMT_END
#define DBG_OBJ_ENTER0_S(aspect, prio, funname) \
RTFL_OBJ_PRINT ("enter", "s:s:d:s:", "<static>", aspect, prio, funname);
#define DBG_OBJ_ENTER_S(aspect, prio, funname, fmt, ...) \
STMT_START { \
char args[256]; \
snprintf (args, sizeof (args), fmt, __VA_ARGS__); \
RTFL_OBJ_PRINT ("enter", "s:s:d:s:s", "<static>", aspect, prio, funname, \
args); \
} STMT_END
#define DBG_OBJ_LEAVE_S() \
RTFL_OBJ_PRINT ("leave", "s", "<static>");
#define DBG_OBJ_LEAVE_VAL_S(fmt, ...) \
STMT_START { \
char vals[256]; \
snprintf (vals, sizeof (vals), fmt, __VA_ARGS__); \
RTFL_OBJ_PRINT ("leave", "s:s", "<static>", vals); \
} STMT_END
#else /* DBG_RTFL */
#define STMT_NOP do { } while (0)
#define DBG_IF_RTFL if(0)
#define DBG_OBJ_MSG_S(aspect, prio, msg) STMT_NOP
#define DBG_OBJ_MSGF_S(aspect, prio, fmt, ...) STMT_NOP
#define DBG_OBJ_ENTER0_S(aspect, prio, funname) STMT_NOP
#define DBG_OBJ_ENTER_S(aspect, prio, funname, fmt, ...) STMT_NOP
#define DBG_OBJ_LEAVE_S() STMT_NOP
#define DBG_OBJ_LEAVE_VAL_S(fmt, ...) STMT_NOP
#endif /* DBG_RTFL */
#endif /* __LOUT_DEBUG_H__ */

460
lout/debug_rtfl.hh Normal file
View File

@ -0,0 +1,460 @@
// WARNING: This file has been generated. Do not edit!
/*
* This file is part of RTFL, see <http://home.gna.org/rtfl/>
* for details.
*
* This file (but not RTFL itself) is in the public domain, since it is only a
* simple implementation of a protocol, containing nothing more than trivial
* work. However, it would be nice to keep this notice, along with the URL
* above.
*
* ----------------------------------------------------------------------------
*
* Defines macros for printing RTFL commands. See documentation for detail
* (online at <http://home.gna.org/rtfl/doc/rtfl.html>). These macros are only
* active, when the pre-processor variable DBG_RTFL is defined. If not,
* alternatives are defined, which have no effect.
*
* This variant assumes that __FILE__ is only the base of the source file name,
* so, to get the full path, CUR_WORKING_DIR has to be defined. See RTFL
* documentation for more details.
*/
#ifndef __DEBUG_RTFL_HH__
#define __DEBUG_RTFL_HH__
#ifdef DBG_RTFL
// =======================================
// Used by all modules
// =======================================
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/time.h>
#define DBG_IF_RTFL if(1)
#define STMT_START do
#define STMT_END while (0)
// Prints an RTFL message to stdout. "fmt" contains simple format
// characters how to deal with the additional arguments (no "%"
// preceding, as in printf) or "q" (which additionally
// (double-)quotes quotation marks) or "c" (short for "#%06x" and used
// for colors), or other characters, which are simply printed. No
// quoting: this function cannot be used to print the characters "d",
// "p", "s" and "q" directly.
inline void rtfl_print (const char *module, const char *version,
const char *file, int line, const char *fmt, ...)
{
// "\n" at the beginning just in case that the previous line is not
// finished yet.
printf ("\n[rtfl-%s-%s]%s:%d:%d:", module, version, file, line, getpid ());
va_list args;
va_start (args, fmt);
for (int i = 0; fmt[i]; i++) {
int n;
void *p;
char *s;
switch (fmt[i]) {
case 'd':
n = va_arg(args, int);
printf ("%d", n);
break;
case 'p':
p = va_arg(args, void*);
printf ("%p", p);
break;
case 's':
s = va_arg (args, char*);
for (int j = 0; s[j]; j++) {
if (s[j] == ':' || s[j] == '\\')
putchar ('\\');
putchar (s[j]);
}
break;
case 'q':
s = va_arg (args, char*);
for (int j = 0; s[j]; j++) {
if (s[j] == ':' || s[j] == '\\')
putchar ('\\');
else if (s[j] == '\"')
printf ("\\\\"); // a quoted quoting character
putchar (s[j]);
}
break;
case 'c':
n = va_arg(args, int);
printf ("#%06x", n);
break;
default:
putchar (fmt[i]);
break;
}
}
va_end (args);
putchar ('\n');
fflush (stdout);
}
#define RTFL_PRINT(module, version, cmd, fmt, ...) \
rtfl_print (module, version, CUR_WORKING_DIR "/" __FILE__, __LINE__, \
"s:" fmt, cmd, __VA_ARGS__)
// ==================================
// General module
// ==================================
#define RTFL_GEN_VERSION "1.0"
#define RTFL_GEN_PRINT(cmd, fmt, ...) \
RTFL_PRINT ("gen", RTFL_GEN_VERSION, cmd, fmt, __VA_ARGS__)
#define DBG_GEN_TIME() \
STMT_START { \
struct timeval tv; \
gettimeofday(&tv, NULL); \
char buf[32]; \
snprintf (buf, sizeof (buf), "%ld%06ld", tv.tv_sec, tv.tv_usec); \
RTFL_GEN_PRINT ("time", "s", buf); \
} STMT_END
// ==================================
// Objects module
// ==================================
#define RTFL_OBJ_VERSION "1.0"
#define RTFL_OBJ_PRINT(cmd, fmt, ...) \
RTFL_PRINT ("obj", RTFL_OBJ_VERSION, cmd, fmt, __VA_ARGS__)
#define DBG_OBJ_MSG(aspect, prio, msg) \
DBG_OBJ_MSG_O (aspect, prio, this, msg)
#define DBG_OBJ_MSG_O(aspect, prio, obj, msg) \
RTFL_OBJ_PRINT ("msg", "p:s:d:s", obj, aspect, prio, msg)
#define DBG_OBJ_MSGF(aspect, prio, fmt, ...) \
STMT_START { \
char msg[256]; \
snprintf (msg, sizeof (msg), fmt, __VA_ARGS__); \
DBG_OBJ_MSG (aspect, prio, msg); \
} STMT_END
#define DBG_OBJ_MSGF_O(aspect, prio, obj, fmt, ...) \
STMT_START { \
char msg[256]; \
snprintf (msg, sizeof (msg), fmt, __VA_ARGS__); \
DBG_OBJ_MSG_O (aspect, prio, obj, msg); \
} STMT_END
#define DBG_OBJ_MARK(aspect, prio, mark) \
DBG_OBJ_MARK_O (aspect, prio, this, mark)
#define DBG_OBJ_MARK_O(aspect, prio, obj, mark) \
RTFL_OBJ_PRINT ("mark", "p:s:d:s", obj, aspect, prio, mark)
#define DBG_OBJ_MARKF(aspect, prio, fmt, ...) \
STMT_START { \
char mark[256]; \
snprintf (mark, sizeof (mark), fmt, __VA_ARGS__); \
DBG_OBJ_MARK (aspect, prio, mark); \
} STMT_END
#define DBG_OBJ_MARKF_O(aspect, prio, obj, fmt, ...) \
STMT_START { \
char mark[256]; \
snprintf (mark, sizeof (mark), fmt, __VA_ARGS__); \
DBG_OBJ_MARK_O (aspect, prio, obj, mark); \
} STMT_END
#define DBG_OBJ_MSG_START() \
DBG_OBJ_MSG_START_O (this)
#define DBG_OBJ_MSG_START_O(obj) \
RTFL_OBJ_PRINT ("msg-start", "p", obj)
#define DBG_OBJ_MSG_END() \
DBG_OBJ_MSG_END_O (this)
#define DBG_OBJ_MSG_END_O(obj) \
RTFL_OBJ_PRINT ("msg-end", "p", obj)
#define DBG_OBJ_ENTER0(aspect, prio, funname) \
DBG_OBJ_ENTER0_O (aspect, prio, this, funname)
#define DBG_OBJ_ENTER0_O(aspect, prio, obj, funname) \
RTFL_OBJ_PRINT ("enter", "p:s:d:s:", obj, aspect, prio, funname);
#define DBG_OBJ_ENTER(aspect, prio, funname, fmt, ...) \
STMT_START { \
char args[256]; \
snprintf (args, sizeof (args), fmt, __VA_ARGS__); \
RTFL_OBJ_PRINT ("enter", "p:s:d:s:s", this, aspect, prio, funname, \
args); \
} STMT_END
#define DBG_OBJ_ENTER_O(aspect, prio, obj, funname, fmt, ...) \
STMT_START { \
char args[256]; \
snprintf (args, sizeof (args), fmt, __VA_ARGS__); \
RTFL_OBJ_PRINT ("enter", "p:s:d:s:s", obj, aspect, prio, funname, \
args); \
} STMT_END
#define DBG_OBJ_LEAVE() \
DBG_OBJ_LEAVE_O (this)
#define DBG_OBJ_LEAVE_O(obj) \
RTFL_OBJ_PRINT ("leave", "p", obj);
#define DBG_OBJ_LEAVE_VAL(fmt, ...) \
STMT_START { \
char vals[256]; \
snprintf (vals, sizeof (vals), fmt, __VA_ARGS__); \
RTFL_OBJ_PRINT ("leave", "p:s", this, vals); \
} STMT_END
#define DBG_OBJ_LEAVE_VAL_O(obj, fmt, ...) \
STMT_START { \
char vals[256]; \
snprintf (vals, sizeof (vals), fmt, __VA_ARGS__); \
RTFL_OBJ_PRINT ("leave", "p:s", obj, vals); \
} STMT_END
#define DBG_OBJ_LEAVE_VAL0(val) \
DBG_OBJ_LEAVE_VAL0_O (this, val)
#define DBG_OBJ_LEAVE_VAL0_O(obj, val) \
RTFL_OBJ_PRINT ("leave", "p:s:", obj, val)
#define DBG_OBJ_CREATE(klass) \
DBG_OBJ_CREATE_O (this, klass)
#define DBG_OBJ_CREATE_O(obj, klass) \
RTFL_OBJ_PRINT ("create", "p:s", obj, klass);
#define DBG_OBJ_DELETE() \
DBG_OBJ_DELETE_O (this)
#define DBG_OBJ_DELETE_O(obj) \
RTFL_OBJ_PRINT ("delete", "p", obj);
#define DBG_OBJ_BASECLASS(klass) \
RTFL_OBJ_PRINT ("ident", "p:p", this, (klass*)this);
#define DBG_OBJ_ASSOC(parent, child) \
RTFL_OBJ_PRINT ("assoc", "p:p", parent, child); \
#define DBG_OBJ_ASSOC_PARENT(parent) \
DBG_OBJ_ASSOC (parent, this);
#define DBG_OBJ_ASSOC_CHILD(child) \
DBG_OBJ_ASSOC (this, child);
#define DBG_OBJ_SET_NUM(var, val) \
DBG_OBJ_SET_NUM_O (this, var, val)
#define DBG_OBJ_SET_NUM_O(obj, var, val) \
RTFL_OBJ_PRINT ("set", "p:s:d", obj, var, val)
#define DBG_OBJ_SET_SYM(var, val) \
DBG_OBJ_SET_SYM_O (this, var, val)
#define DBG_OBJ_SET_SYM_O(obj, var, val) \
RTFL_OBJ_PRINT ("set", "p:s:s", obj, var, val)
#define DBG_OBJ_SET_BOOL(var, val) \
DBG_OBJ_SET_BOOL_O (this, var, val)
#define DBG_OBJ_SET_BOOL_O(obj, var, val) \
RTFL_OBJ_PRINT ("set", "p:s:s", obj, var, (val) ? "true" : "false")
#define DBG_OBJ_SET_STR(var, val) \
DBG_OBJ_SET_STR_O (this, var, val)
#define DBG_OBJ_SET_STR_O(obj, var, val) \
RTFL_OBJ_PRINT ("set", "p:s:\"q\"", obj, var, val)
#define DBG_OBJ_SET_PTR(var, val) \
DBG_OBJ_SET_PTR_O (this, var, val)
#define DBG_OBJ_SET_PTR_O(obj, var, val) \
RTFL_OBJ_PRINT ("set", "p:s:p", obj, var, val)
#define DBG_OBJ_SET_COL(var, val) \
DBG_OBJ_SET_COL_O (this, var, val)
#define DBG_OBJ_SET_COL_O(obj, var, val) \
RTFL_OBJ_PRINT ("set", "p:s:c", obj, var, val)
#define DBG_OBJ_ARRSET_NUM(var, ind, val) \
DBG_OBJ_ARRSET_NUM_O (this, var, ind, val)
#define DBG_OBJ_ARRSET_NUM_O(obj, var, ind, val) \
RTFL_OBJ_PRINT ("set", "p:s.d:d", obj, var, ind, val)
#define DBG_OBJ_ARRSET_SYM(var, ind, val) \
DBG_OBJ_ARRSET_SYM_O (this, var, ind, val)
#define DBG_OBJ_ARRSET_SYM_O(obj, var, ind, val) \
RTFL_OBJ_PRINT ("set", "p:s.d:s", obj, var, ind, val)
#define DBG_OBJ_ARRSET_BOOL(var, ind, val) \
DBG_OBJ_ARRSET_BOOL_O (this, var, ind, val)
#define DBG_OBJ_ARRSET_BOOL_O(obj, var, ind, val) \
RTFL_OBJ_PRINT ("set", "p:s.d:s", obj, var, ind, (val) ? "true" : "false")
#define DBG_OBJ_ARRSET_STR(var, ind, val) \
DBG_OBJ_ARRSET_STR_O (this, var, ind, val)
#define DBG_OBJ_ARRSET_STR_O(obj, var, ind, val) \
RTFL_OBJ_PRINT ("set", "p:s.d:\"q\"", obj, var, ind, val)
#define DBG_OBJ_ARRSET_PTR(var, ind, val) \
DBG_OBJ_ARRSET_PTR_O (this, var, ind, val)
#define DBG_OBJ_ARRSET_PTR_O(obj, var, ind, val) \
RTFL_OBJ_PRINT ("set", "p:s.d:p", obj, var, ind, val)
#define DBG_OBJ_ARRSET_COL(var, ind, val) \
DBG_OBJ_ARRSET_COL_O (this, var, ind, val)
#define DBG_OBJ_ARRSET_COL_O(obj, var, ind, val) \
RTFL_OBJ_PRINT ("set", "p:s.d:c", obj, var, ind, val)
#define DBG_OBJ_ARRATTRSET_NUM(var, ind, attr, val) \
DBG_OBJ_ARRATTRSET_NUM_O (this, var, ind, attr, val)
#define DBG_OBJ_ARRATTRSET_NUM_O(obj, var, ind, attr, val) \
RTFL_OBJ_PRINT ("set", "p:s.d.s:d", obj, var, ind, attr, val)
#define DBG_OBJ_ARRATTRSET_SYM(var, ind, attr, val) \
DBG_OBJ_ARRATTRSET_SYM_O (this, var, ind, attr, val)
#define DBG_OBJ_ARRATTRSET_SYM_O(obj, var, ind, attr, val) \
RTFL_OBJ_PRINT ("set", "p:s.d.s:s", obj, var, ind, attr, val)
#define DBG_OBJ_ARRATTRSET_BOOL(var, ind, attr, val) \
DBG_OBJ_ARRATTRSET_BOOL_O (this, var, ind, attr, val)
#define DBG_OBJ_ARRATTRSET_BOOL_O(obj, var, ind, attr, val) \
RTFL_OBJ_PRINT ("set", "p:s.d.s:s", obj, var, ind, attr, \
(val) ? "true" : "false")
#define DBG_OBJ_ARRATTRSET_STR(var, ind, attr, val) \
DBG_OBJ_ARRATTRSET_STR_O (this, var, ind, attr, val)
#define DBG_OBJ_ARRATTRSET_STR_O(obj, var, ind, attr, val) \
RTFL_OBJ_PRINT ("set", "p:s.d.s:\"q\"", obj, var, ind, attr, val)
#define DBG_OBJ_ARRATTRSET_PTR(var, ind, attr, val) \
DBG_OBJ_ARRATTRSET_PTR_O (this, var, ind, attr, val)
#define DBG_OBJ_ARRATTRSET_PTR_O(obj, var, ind, attr, val) \
RTFL_OBJ_PRINT ("set", "p:s.d.s:p", obj, var, ind, attr, val)
#define DBG_OBJ_ARRATTRSET_COL(var, ind, attr, val) \
DBG_OBJ_ARRATTRSET_COL_O (this, var, ind, attr, val)
#define DBG_OBJ_ARRATTRSET_COL_O(obj, var, ind, attr, val) \
RTFL_OBJ_PRINT ("set", "p:s.d.s:c", obj, var, ind, attr, val)
#define DBG_OBJ_CLASS_COLOR(klass, color) \
RTFL_OBJ_PRINT ("class-color", "s:s", klass, color)
#else /* DBG_RTFL */
#define STMT_NOP do { } while (0)
#define DBG_IF_RTFL if(0)
#define DBG_GEN_TIME() STMT_NOP
#define DBG_OBJ_MSG(aspect, prio, msg) STMT_NOP
#define DBG_OBJ_MSG_O(aspect, prio, obj, msg) STMT_NOP
#define DBG_OBJ_MSGF(aspect, prio, fmt, ...) STMT_NOP
#define DBG_OBJ_MSGF_O(aspect, prio, obj, fmt, ...) STMT_NOP
#define DBG_OBJ_MARK(aspect, prio, mark) STMT_NOP
#define DBG_OBJ_MARK_O(aspect, prio, obj, mark) STMT_NOP
#define DBG_OBJ_MARKF(aspect, prio, fmt, ...) STMT_NOP
#define DBG_OBJ_MARKF_O(aspect, prio, obj, fmt, ...) STMT_NOP
#define DBG_OBJ_MSG_START() STMT_NOP
#define DBG_OBJ_MSG_START_O(obj) STMT_NOP
#define DBG_OBJ_MSG_END() STMT_NOP
#define DBG_OBJ_MSG_END_O(obj) STMT_NOP
#define DBG_OBJ_ENTER0(aspect, prio, funname) STMT_NOP
#define DBG_OBJ_ENTER0_O(aspect, prio, obj, funname) STMT_NOP
#define DBG_OBJ_ENTER(aspect, prio, funname, fmt, ...) STMT_NOP
#define DBG_OBJ_ENTER_O(aspect, prio, obj, funname, fmt, ...) STMT_NOP
#define DBG_OBJ_LEAVE() STMT_NOP
#define DBG_OBJ_LEAVE_O(obj) STMT_NOP
#define DBG_OBJ_LEAVE_VAL(fmt, ...) STMT_NOP
#define DBG_OBJ_LEAVE_VAL_O(obj, fmt, ...) STMT_NOP
#define DBG_OBJ_LEAVE_VAL0(val) STMT_NOP
#define DBG_OBJ_LEAVE_VAL0_O(obj, val) STMT_NOP
#define DBG_OBJ_CREATE(klass) STMT_NOP
#define DBG_OBJ_CREATE_O(obj, klass) STMT_NOP
#define DBG_OBJ_DELETE() STMT_NOP
#define DBG_OBJ_DELETE_O(obj) STMT_NOP
#define DBG_OBJ_BASECLASS(klass) STMT_NOP
#define DBG_OBJ_ASSOC(parent, child) STMT_NOP
#define DBG_OBJ_ASSOC_PARENT(parent) STMT_NOP
#define DBG_OBJ_ASSOC_CHILD(child) STMT_NOP
#define DBG_OBJ_SET_NUM(var, val) STMT_NOP
#define DBG_OBJ_SET_NUM_O(obj, var, val) STMT_NOP
#define DBG_OBJ_SET_SYM(var, val) STMT_NOP
#define DBG_OBJ_SET_SYM_O(obj, var, val) STMT_NOP
#define DBG_OBJ_SET_BOOL(var, val) STMT_NOP
#define DBG_OBJ_SET_BOOL_O(obj, var, val) STMT_NOP
#define DBG_OBJ_SET_STR(var, val) STMT_NOP
#define DBG_OBJ_SET_STR_O(obj, var, val) STMT_NOP
#define DBG_OBJ_SET_PTR(var, val) STMT_NOP
#define DBG_OBJ_SET_PTR_O(obj, var, val) STMT_NOP
#define DBG_OBJ_SET_COL(var, val) STMT_NOP
#define DBG_OBJ_SET_COL_O(obj, var, val) STMT_NOP
#define DBG_OBJ_ARRSET_NUM(var, ind, val) STMT_NOP
#define DBG_OBJ_ARRSET_NUM_O(obj, var, ind, val) STMT_NOP
#define DBG_OBJ_ARRSET_SYM(var, ind, val) STMT_NOP
#define DBG_OBJ_ARRSET_SYM_O(obj, var, ind, val) STMT_NOP
#define DBG_OBJ_ARRSET_BOOL(var, ind, val) STMT_NOP
#define DBG_OBJ_ARRSET_BOOL_O(obj, var, ind, val) STMT_NOP
#define DBG_OBJ_ARRSET_STR(var, ind, val) STMT_NOP
#define DBG_OBJ_ARRSET_STR_O(obj, var, ind, val) STMT_NOP
#define DBG_OBJ_ARRSET_PTR(var, ind, val) STMT_NOP
#define DBG_OBJ_ARRSET_PTR_O(obj, var, ind, val) STMT_NOP
#define DBG_OBJ_ARRSET_COL(var, ind, val) STMT_NOP
#define DBG_OBJ_ARRSET_COL_O(obj, var, ind, val) STMT_NOP
#define DBG_OBJ_ARRATTRSET_NUM(var, ind, attr, val) STMT_NOP
#define DBG_OBJ_ARRATTRSET_NUM_O(obj, var, ind, attr, val) STMT_NOP
#define DBG_OBJ_ARRATTRSET_SYM(var, ind, attr, val) STMT_NOP
#define DBG_OBJ_ARRATTRSET_SYM_O(obj, var, ind, attr, val) STMT_NOP
#define DBG_OBJ_ARRATTRSET_BOOL(var, ind, attr, val) STMT_NOP
#define DBG_OBJ_ARRATTRSET_BOOL_O(obj, var, ind, attr, val) STMT_NOP
#define DBG_OBJ_ARRATTRSET_STR(var, ind, attr, val) STMT_NOP
#define DBG_OBJ_ARRATTRSET_STR_O(obj, var, ind, attr, val) STMT_NOP
#define DBG_OBJ_ARRATTRSET_PTR(var, ind, attr, val) STMT_NOP
#define DBG_OBJ_ARRATTRSET_PTR_O(obj, var, ind, attr, val) STMT_NOP
#define DBG_OBJ_ARRATTRSET_COL(var, ind, attr, val) STMT_NOP
#define DBG_OBJ_ARRATTRSET_COL_O(obj, var, ind, attr, val) STMT_NOP
#define DBG_OBJ_CLASS_COLOR(klass, color) STMT_NOP
#endif /* DBG_RTFL */
#endif /* __DEBUG_RTFL_HH__ */

131
lout/identity.cc Normal file
View File

@ -0,0 +1,131 @@
/*
* 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, see <http://www.gnu.org/licenses/>.
*/
#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;
}
void IdentifiableObject::Class::intoStringBuffer(misc::StringBuffer *sb)
{
sb->append ("<class ");
sb->append (className);
sb->append (" (");
sb->appendInt (id);
sb->append (")");
if (parent) {
sb->append (", parent: ");
parent->intoStringBuffer (sb);
}
sb->append (">");
}
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 ");
sb->appendPointer(this);
sb->append(" 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);
*classId = klass->id;
}
this->classId = klass->id;
*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

149
lout/identity.hh Normal file
View File

@ -0,0 +1,149 @@
#ifndef __LOUT_OBJECTX_HH__
#define __LOUT_OBJECTX_HH__
#include "object.hh"
#include "container.hh"
#include "signal.hh"
namespace lout {
/**
* \brief Some stuff to identify classes of objects at run-time.
*/
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 instantiated,
* 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);
void intoStringBuffer(misc::StringBuffer *sb);
};
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 ();
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__

188
lout/misc.cc Normal file
View File

@ -0,0 +1,188 @@
/*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "misc.hh"
#include <ctype.h>
#include <config.h>
#define PRGNAME PACKAGE "/" VERSION
namespace lout {
namespace misc {
const char *prgName = PRGNAME;
// ------------------
// 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;
free(node->data);
delete node;
}
firstNode = lastNode = NULL;
numChars = 0;
strValid = false;
}
// ------------
// BitSet
// ------------
BitSet::BitSet(int initBits)
{
numBits = 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 < numBits; i++)
sb->append(get(i) ? "1" : "0");
sb->append("]");
}
bool BitSet::get(int i) const
{
if (8 * i >= numBytes)
return false;
else
return bits[i / 8] & (1 << (i % 8));
}
void BitSet::set(int i, bool val)
{
if (i > numBits)
numBits = i;
if (8 * i >= numBytes) {
int newNumBytes = numBytes;
while (8 * i >= newNumBytes)
newNumBytes *= 2;
void *vp;
assert((vp = realloc(bits, newNumBytes * sizeof(unsigned char))));
if (!vp) exit(-2); // when NDEBUG is defined
bits = (unsigned char*)vp;
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

698
lout/misc.hh Normal file
View File

@ -0,0 +1,698 @@
/*
* Dillo Widget
*
* Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
* Copyright 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __LOUT_MISC_HH__
#define __LOUT_MISC_HH__
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "dlib/dlib.h"
namespace lout {
/**
* \brief Miscellaneous stuff, which does not fit anywhere else.
*
* Actually, the other parts, beginning with \ref object, depend on this.
*/
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[]);
inline void assertNotReached ()
{
fprintf (stderr, "*** [%s] This should not happen! ***\n", prgName);
abort ();
}
inline void assertNotReached (const char *fmt, ...)
{
va_list argp;
va_start(argp, fmt);
fprintf (stderr, "*** [%s] This should not happen: ", prgName);
vfprintf(stderr, fmt, argp);
fprintf (stderr, "! ***\n");
va_end(argp);
abort ();
}
inline void notImplemented (const char *name)
{
fprintf (stderr, "*** [%s] Not implemented: %s ***\n", prgName, name);
abort ();
}
inline int roundInt(double d)
{
return (int) ((d > 0) ? (d + 0.5) : (d - 0.5));
}
inline int AsciiTolower(char c)
{
return ((c >= 'A' && c <= 'Z') ? c + 0x20 : c);
}
inline int AsciiToupper(char c)
{
return ((c >= 'a' && c <= 'z') ? c - 0x20 : c);
}
inline int AsciiStrcasecmp(const char *s1, const char *s2)
{
int ret = 0;
while ((*s1 || *s2) && !(ret = AsciiTolower(*s1) - AsciiTolower(*s2))) {
s1++;
s2++;
}
return ret;
}
inline const char *boolToStr (bool b) { return b ? "true" : "false"; }
/**
* \brief Simple (simpler than container::untyped::Vector and
* container::typed::Vector) template based vector.
*/
template <class T> class SimpleVector
{
private:
T *array;
int num, numAlloc;
inline void resize ()
{
/* This algorithm was tuned 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 = 1)
{
this->num = 0;
this->numAlloc = initAlloc;
this->array = NULL;
}
inline SimpleVector (const SimpleVector &o) {
this->array = NULL;
this->num = o.num;
this->numAlloc = o.numAlloc;
resize ();
memcpy (this->array, o.array, sizeof (T) * num);
}
inline ~SimpleVector ()
{
if (this->array)
free (this->array);
}
/**
* \brief Return the number of elements put into this vector.
*/
inline int size() const { return this->num; }
inline bool empty() const { return size() == 0; }
inline T* getArray() const { return array; }
inline T* detachArray() {
T* arr = array;
array = NULL;
numAlloc = 0;
num = 0;
return arr;
}
/**
* \brief Increase the vector size by one.
*
* May be necessary before calling misc::SimpleVector::set.
*/
inline void increase() { setSize(this->num + 1); }
/**
* \brief Set the size explicitly.
*
* May be necessary before calling misc::SimpleVector::set.
*/
inline void setSize(int newSize) {
assert (newSize >= 0);
this->num = newSize;
this->resize ();
}
/**
* \brief Set the size explicitly and initialize new values.
*
* May be necessary before calling 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) const {
assert (i >= 0 && this->num - i > 0);
return array + i;
}
/**
* \brief Return the one element, explicitly.
*
* The element is copied, so for complex elements, you should rather used
* misc::SimpleVector::getRef.
*/
inline T get (int i) const {
assert (i >= 0 && this->num - i > 0);
return this->array[i];
}
/**
* \brief Return the reference of the first element (convenience method).
*/
inline T* getFirstRef () const {
assert (this->num > 0);
return this->array;
}
/**
* \brief Return the first element, explicitly.
*/
inline T getFirst () const {
assert (this->num > 0);
return this->array[0];
}
/**
* \brief Return the reference of the last element (convenience method).
*/
inline T* getLastRef () const {
assert (this->num > 0);
return this->array + this->num - 1;
}
/**
* \brief Return the last element, explicitly.
*/
inline T getLast () const {
assert (this->num > 0);
return this->array[this->num - 1];
}
/**
* \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) {
assert (i >= 0 && this->num - i > 0);
this->array[i] = t;
}
/**
* \brief Store an object at the end of the vector.
*/
inline void setLast (T t) {
assert (this->num > 0);
this->array[this->num - 1] = t;
}
/**
* \brief Copies some elements into another vector of the same
* type.
*
* Cannot be used to copy elements within one vector. (For this,
* it would have to be extended to copy backwards in some cases.)
*/
inline void copyTo(SimpleVector<T> *dest, int thisStart = 0,
int thisLast = -1, int destStart = 0) {
assert (dest != this);
if (thisLast == -1)
thisLast = this->size () - 1;
for (int i = thisStart; i <= thisLast; i++)
dest->set (i - thisStart + destStart, get (i));
}
};
/**
* \brief Container similar to lout::misc::SimpleVector, but some cases
* of insertion optimized (used for hyphenation).
*
* For hyphenation, words are often split, so that some space must be
* inserted by the method NotSoSimpleVector::insert. Typically, some
* elements are inserted quite at the beginning (when the word at the
* end of the first or at the beginning of the second line is
* hyphenated), then, a bit further (end of second line/beginning of
* third line) and so on. In the first time, nearly all words must be
* moved; in the second time, a bit less, etc. After all, using a
* simple vector would result in O(n<sup>2</sup>) number of elements
* moved total. With this class, however, the number can be kept at
* O(n).
*
* The basic idea is to keep an extra array (actually two, of which
* the second one is used temporarily), which is inserted in a logical
* way. Since there is only one extra array at max, reading is rather
* simple and fast (see NotSoSimpleVector::getRef): check whether the
* position is before, within, or after the extra array. The first
* insertion is also rather simple, when the extra array has to be
* created. The following sketch illustrates the most complex case,
* when an extra array exists, and something is inserted after it (the
* case for which this class has been optimized):
*
* \image html not-so-simple-container.png
*
* Dotted lines are used to keep the boxes aligned.
*
* As you see, only a relatively small fraction of elements has to be
* moved.
*
* There are some other cases, which have to be documented.
*/
template <class T> class NotSoSimpleVector
{
private:
T *arrayMain, *arrayExtra1, *arrayExtra2;
int numMain, numExtra, numAllocMain, numAllocExtra, startExtra;
inline void resizeMain ()
{
/* This algorithm was tuned for memory&speed with this huge page:
* http://downloads.mysql.com/docs/refman-6.0-en.html.tar.gz
*/
if (arrayMain == NULL) {
this->numAllocMain = 1;
this->arrayMain = (T*) malloc (sizeof (T));
}
if (this->numAllocMain < this->numMain) {
this->numAllocMain = (this->numMain < 100) ?
this->numMain : this->numMain + this->numMain/10;
this->arrayMain =
(T*) realloc(this->arrayMain, (this->numAllocMain * sizeof (T)));
}
}
inline void resizeExtra ()
{
/* This algorithm was tuned for memory&speed with this huge page:
* http://downloads.mysql.com/docs/refman-6.0-en.html.tar.gz
*/
if (arrayExtra1 == NULL) {
this->numAllocExtra = 1;
this->arrayExtra1 = (T*) malloc (sizeof (T));
this->arrayExtra2 = (T*) malloc (sizeof (T));
}
if (this->numAllocExtra < this->numExtra) {
this->numAllocExtra = (this->numExtra < 100) ?
this->numExtra : this->numExtra + this->numExtra/10;
this->arrayExtra1 =
(T*) realloc(this->arrayExtra1, (this->numAllocExtra * sizeof (T)));
this->arrayExtra2 =
(T*) realloc(this->arrayExtra2, (this->numAllocExtra * sizeof (T)));
}
}
void consolidate ()
{
if (startExtra != -1) {
numMain += numExtra;
resizeMain ();
memmove (arrayMain + startExtra + numExtra, arrayMain + startExtra,
(numMain - (startExtra + numExtra)) * sizeof (T));
memmove (arrayMain + startExtra, arrayExtra1, numExtra * sizeof (T));
startExtra = -1;
numExtra = 0;
}
}
public:
inline NotSoSimpleVector (int initAlloc)
{
this->numMain = this->numExtra = 0;
this->numAllocMain = initAlloc;
this->numAllocExtra = initAlloc;
this->arrayMain = this->arrayExtra1 = this->arrayExtra2 = NULL;
this->startExtra = -1;
}
inline ~NotSoSimpleVector ()
{
if (this->arrayMain)
free (this->arrayMain);
if (this->arrayExtra1)
free (this->arrayExtra1);
if (this->arrayExtra2)
free (this->arrayExtra2);
}
inline int size() const { return this->numMain + this->numExtra; }
inline bool empty() const { return size() == 0; }
inline void increase() { setSize(size() + 1); }
inline void setSize(int newSize)
{
assert (newSize >= 0);
this->numMain = newSize - numExtra;
this->resizeMain ();
}
void insert (int index, int numInsert)
{
assert (numInsert >= 0);
// The following lines are a simple (but inefficient) replacement.
//setSize (numMain + numInsert);
//memmove (arrayMain + index + numInsert, arrayMain + index,
// (numMain - index - numInsert) * sizeof (T));
//return;
if (this->startExtra == -1) {
// simple case
this->numExtra = numInsert;
this->startExtra = index;
resizeExtra ();
} else {
if (index < startExtra) {
consolidate ();
insert (index, numInsert);
} else if (index < startExtra + numExtra) {
int oldNumExtra = numExtra;
numExtra += numInsert;
resizeExtra ();
int toMove = startExtra + oldNumExtra - index;
memmove (arrayExtra1 + numExtra - toMove,
arrayExtra1 + index - startExtra,
toMove * sizeof (T));
} else {
int oldNumExtra = numExtra;
numExtra += numInsert;
resizeExtra ();
// Note: index refers to the *logical* address, not to the
// *physical* one.
int diff = index - this->startExtra - oldNumExtra;
T *arrayMainI = arrayMain + this->startExtra;
for (int i = diff + oldNumExtra - 1; i >= 0; i--) {
T *src = i < oldNumExtra ?
this->arrayExtra1 + i : arrayMainI + (i - oldNumExtra);
T *dest = i < diff ?
arrayMainI + i : arrayExtra2 + (i - diff);
*dest = *src;
}
memcpy (arrayExtra1, arrayExtra2, sizeof (T) * oldNumExtra);
startExtra = index - oldNumExtra;
}
}
}
/**
* \brief Return the reference of one element.
*
* \sa misc::SimpleVector::get
*/
inline T* getRef (int i) const
{
if (this->startExtra == -1) {
assert (i >= 0 && i < this->numMain);
return this->arrayMain + i;
} else {
if (i < this->startExtra) {
assert (i >= 0);
return this->arrayMain + i;
} else if (i >= this->startExtra + this->numExtra) {
// The original assertion
///
// "assert (i < this->numMain + this->numExtra)"
//
// causes this warnung in dw::Textblock::breakAdded:
//
// "assuming signed overflow does not occur when assuming that
// (X - c) > X is always false [-Wstrict-overflow]"
//
// Subtracting numExtra from both sides solves this,
// interrestingly.
assert (i - this->numExtra < this->numMain);
return this->arrayMain + i - this->numExtra;
} else
return this->arrayExtra1 + i - this->startExtra;
}
}
/**
* \brief Return the one element, explicitly.
*
* The element is copied, so for complex elements, you should rather used
* misc::SimpleVector::getRef.
*/
inline T get (int i) const
{
return *(this->getRef(i));
}
/**
* \brief Return the reference of the first element (convenience method).
*/
inline T* getFirstRef () const {
assert (size () > 0);
return this->getRef(0);
}
/**
* \brief Return the first element, explicitly.
*/
inline T getFirst () const {
return *(this->getFirstRef());
}
/**
* \brief Return the reference of the last element (convenience method).
*/
inline T* getLastRef () const {
assert (size () > 0);
return this->getRef(size () - 1);
}
/**
* \brief Return the last element, explicitly.
*/
inline T getLast () const {
return *(this->getLastRef());
}
/**
* \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) {
*(this->getRef(i)) = t;
}
/**
* \brief Store an object at the end of the vector.
*/
inline void setLast (T t) {
*(this->getLastRef()) = 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(dStrdup(str)); }
inline void appendInt(int n)
{ char buf[32]; sprintf (buf, "%d", n); append (buf); }
inline void appendPointer(void *p)
{ char buf[32]; sprintf (buf, "%p", p); append (buf); }
inline void appendBool(bool b) { append (b ? "true" : "false"); }
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 numBits, 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) const;
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__

38
lout/msg.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef __MSG_H__
#define __MSG_H__
#include <stdio.h>
#define prefs_show_msg 1
#define D_STMT_START do
#define D_STMT_END while (0)
/*
* You can disable any MSG* macro by adding the '_' prefix.
*/
#define _MSG(...)
#define _MSG_WARN(...)
#define MSG(...) \
D_STMT_START { \
if (prefs_show_msg){ \
printf(__VA_ARGS__); \
fflush (stdout); \
} \
} D_STMT_END
#define MSG_WARN(...) \
D_STMT_START { \
if (prefs_show_msg) \
printf("** WARNING **: " __VA_ARGS__); \
} D_STMT_END
#define MSG_ERR(...) \
D_STMT_START { \
if (prefs_show_msg) \
printf("** ERROR **: " __VA_ARGS__); \
} D_STMT_END
#endif /* __MSG_H__ */

386
lout/object.cc Normal file
View File

@ -0,0 +1,386 @@
/*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "object.hh"
#include "dlib/dlib.h"
#include <stdio.h>
#include <stdint.h>
#include <config.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 = dStrdup(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 ");
sb->appendPointer(this);
sb->append(">");
}
/**
* \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*);
}
// ----------------
// Comparator
// ----------------
Comparator *Comparator::compareFunComparator = NULL;
/**
* \brief This static method may be used as compare function for
* qsort(3) and bsearch(3), for an array of Object* (Object*[] or
* Object**).
*
* "compareFunComparator" should be set before.
*
* \todo Not reentrant. Consider switching to reentrant variants
* (qsort_r), and compare function with an additional argument.
*/
int Comparator::compareFun(const void *p1, const void *p2)
{
return compareFunComparator->compare (*(Object**)p1, *(Object**)p2);
}
// ------------------------
// StandardComparator
// ------------------------
int StandardComparator::compare(Object *o1, Object *o2)
{
if (o1 && o2)
return ((Comparable*)o1)->compareTo ((Comparable*)o2);
else if (o1)
return 1;
else if (o2)
return -1;
else
return 0;
}
StandardComparator standardComparator;
// -------------
// 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
// Assuming that sizeof(void*) == sizeof(int); on 32 bit systems.
return (int)value;
#else
// Assuming that sizeof(void*) == 2 * sizeof(int); on 64 bit
// systems (int is still 32 bit).
// Combine both parts of the pointer value *itself*, not what it
// points to, by first referencing it (operator "&"), then
// dereferencing it again (operator "[]").
return ((intptr_t)value >> 32) ^ ((intptr_t)value);
#endif
}
void Pointer::intoStringBuffer(misc::StringBuffer *sb)
{
char buf[64];
snprintf(buf, sizeof(buf), "%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;
}
// -------------
// Boolean
// -------------
bool Boolean::equals(Object *other)
{
bool value2 = ((Boolean*)other)->value;
// TODO Does "==" work?
return (value && value2) || (!value && value2);
}
int Boolean::hashValue()
{
return value ? 1 : 0;
}
void Boolean::intoStringBuffer(misc::StringBuffer *sb)
{
sb->append(value ? "true" : "false");
}
int Boolean::compareTo(Comparable *other)
{
return (value ? 1 : 0) - (((Boolean*)other)->value ? 1 : 0);
}
// -----------------
// 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 ? dStrdup(str) : NULL)
{
}
String::~String ()
{
if (str)
free((char *)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

238
lout/object.hh Normal file
View File

@ -0,0 +1,238 @@
#ifndef __LOUT_OBJECT_HH__
#define __LOUT_OBJECT_HH__
#include <stdlib.h>
#include <string.h>
#include "misc.hh"
namespace lout {
/**
* \brief Here, some common classes (or interfaces) are defined, to standardize
* the access to other classes.
*/
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 Instances of a sub class of may be compared (less, greater).
*
* Used for sorting etc.
*/
class Comparable: public Object
{
public:
/**
* \brief Compare two objects, this and other.
*
* Return a value < 0, when this is less than other, a value > 0,
* when this is greater than other, or 0, when this and other are
* equal.
*
* If c1.equals(c2) (as defined in Object), 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;
};
/**
* \brief Used for other orders as the one defined by Comparable.
*
* Compared objects must not necessarily be instances of Comparable.
*/
class Comparator: public Object
{
public:
/**
* \brief Compare two objects o1 and o2.
*
* Return a value < 0, when o1 is less than o2, a value > 0, when o1
* is greater than o2, or 0, when o1 and o2 are equal.
*
* If o1.equals(o2) (as defined in Object), compare(o1, o2) 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 compare(Object *o1, Object *o2) = 0;
static Comparator *compareFunComparator;
static int compareFun(const void *p1, const void *p2);
};
class StandardComparator: public Comparator
{
public:
int compare(Object *o1, Object *o2);
};
extern StandardComparator standardComparator;
/**
* \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 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 bool's.
*/
class Boolean: public Comparable
{
bool value;
public:
Boolean(bool value) { this->value = value; }
bool equals(Object *other);
int hashValue();
void intoStringBuffer(misc::StringBuffer *sb);
int compareTo(Comparable *other);
inline bool 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 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__

170
lout/signal.cc Normal file
View File

@ -0,0 +1,170 @@
/*
* 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, see <http://www.gnu.org/licenses/>.
*/
#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

310
lout/signal.hh Normal file
View File

@ -0,0 +1,310 @@
#ifndef __LOUT_SIGNALS_HH__
#define __LOUT_SIGNALS_HH__
#include "object.hh"
#include "container.hh"
namespace lout {
/**
* \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 instantiate 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 instantiated 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 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 \ref object can be used.)
* </ul>
*
* \sa \ref 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 \ref 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__

180
lout/unicode.cc Normal file
View File

@ -0,0 +1,180 @@
/*
* Dillo Widget
*
* Copyright 2012, 2013 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, see <http://www.gnu.org/licenses/>.
*/
#include "unicode.hh"
#include "misc.hh"
using namespace lout::misc;
namespace lout {
namespace unicode {
static unsigned char alpha[0x500] = {
// 0000-007F: C0 Controls and Basic Latin
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfe, 0xff, 0xff, 0x07, 0xfe, 0xff, 0xff, 0x07,
// 0080-00FF: C1 Controls and Latin-1 Supplement
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff,
// 0100-017F: Latin Extended-A
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
// 0180-024F: Latin Extended-B
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff,
// 025002AF: IPA Extensions
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
// 02B002FF: Spacing Modifier Letters
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
// 0300036F: Combining Diacritical Marks
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 037003FF: Greek and Coptic
0xcf, 0x00, 0x40, 0x7d, 0xff, 0xff, 0xfb, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
// 040004FF: Cyrillic
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x03, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
/**
* Returns whether a given unicode character is an alphabetic character.
*/
bool isAlpha (int ch)
{
return ch < 0x500 && (alpha[ch / 8] & (1 << (ch & 7)));
}
int decodeUtf8 (const char *s)
{
if((s[0] & 0x80) == 0)
return s[0];
else if((s[0] & 0xe0) == 0xc0 && (s[1] & 0xc0) == 0x80)
return ((s[0] & 0x1f) << 6) | (s[1] & 0x3f);
else if((s[0] & 0xf0) == 0xe0 && (s[1] & 0xc0) == 0x80
&& (s[2] & 0xc0) == 0x80)
return ((s[0] & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f);
else if((s[0] & 0xf8) == 0xf0 && (s[1] & 0xc0) == 0x80
&& (s[2] & 0xc0) == 0x80 && (s[3] & 0xc0) == 0x80)
return ((s[0] & 0x0f) << 18) | ((s[1] & 0x3f) << 12)
| ((s[2] & 0x3f) << 6) | (s[3] & 0x3f);
else
// Treat as ISO-8859-1 / ISO-8859-15 / Windows-1252
return s[0];
}
int decodeUtf8 (const char *s, int len)
{
if(len >= 1 && (s[0] & 0x80) == 0)
return s[0];
else if(len >= 2 && (s[0] & 0xe0) == 0xc0 && (s[1] & 0xc0) == 0x80)
return ((s[0] & 0x1f) << 6) | (s[1] & 0x3f);
else if(len >= 3 && (s[0] & 0xf0) == 0xe0 && (s[1] & 0xc0) == 0x80
&& (s[2] & 0xc0) == 0x80)
return ((s[0] & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f);
else if(len >= 4 && (s[0] & 0xf8) == 0xf0 && (s[1] & 0xc0) == 0x80
&& (s[2] & 0xc0) == 0x80 && (s[3] & 0xc0) == 0x80)
return ((s[0] & 0x0f) << 18) | ((s[1] & 0x3f) << 12)
| ((s[2] & 0x3f) << 6) | (s[3] & 0x3f);
else
// Treat as ISO-8859-1 / ISO-8859-15 / Windows-1252
return s[0];
}
const char *nextUtf8Char (const char *s)
{
const char *r;
if (s == NULL || s[0] == 0)
r = NULL;
else if((s[0] & 0x80) == 0)
r = s + 1;
else if((s[0] & 0xe0) == 0xc0 && (s[1] & 0xc0) == 0x80)
r = s + 2;
else if((s[0] & 0xf0) == 0xe0 && (s[1] & 0xc0) == 0x80
&& (s[2] & 0xc0) == 0x80)
r = s + 3;
else if((s[0] & 0xf8) == 0xf0 && (s[1] & 0xc0) == 0x80
&& (s[2] & 0xc0) == 0x80 && (s[3] & 0xc0) == 0x80)
r = s + 4;
else
// invalid UTF-8 sequence: treat as one byte.
r = s + 1;
if (r && r[0] == 0)
return NULL;
else
return r;
}
const char *nextUtf8Char (const char *s, int len)
{
const char *r;
if (s == NULL || len <= 0)
r = NULL;
else if(len >= 1 && (s[0] & 0x80) == 0)
r = s + 1;
else if(len >= 2 && (s[0] & 0xe0) == 0xc0 && (s[1] & 0xc0) == 0x80)
r = s + 2;
else if(len >= 3 && (s[0] & 0xf0) == 0xe0 && (s[1] & 0xc0) == 0x80
&& (s[2] & 0xc0) == 0x80)
r = s + 3;
else if(len >= 4 && (s[0] & 0xf8) == 0xf0 && (s[1] & 0xc0) == 0x80
&& (s[2] & 0xc0) == 0x80 && (s[3] & 0xc0) == 0x80)
r = s + 4;
else
// invalid UTF-8 sequence: treat as one byte.
r = s + 1;
if (r && r - s >= len)
return NULL;
else
return r;
}
int numUtf8Chars (const char *s)
{
int numUtf8 = 0;
for (const char *r = s; r; r = nextUtf8Char (r))
numUtf8++;
return numUtf8;
}
int numUtf8Chars (const char *s, int len)
{
int numUtf8 = 0;
for (const char *r = s; len > 0 && r; r = nextUtf8Char (r, len))
numUtf8++;
return numUtf8;
}
} // namespace lout
} // namespace unicode

30
lout/unicode.hh Normal file
View File

@ -0,0 +1,30 @@
#ifndef __UNICODE_HH__
#define __UNICODE_HH__
namespace lout {
/**
* \brief Stuff dealing with Unicode characters: UTF-8, character classes etc.
*
*/
namespace unicode {
bool isAlpha (int ch);
int decodeUtf8 (const char *s);
int decodeUtf8 (const char *s, int len);
const char *nextUtf8Char (const char *s);
const char *nextUtf8Char (const char *s, int len);
int numUtf8Chars (const char *s);
int numUtf8Chars (const char *s, int len);
} // namespace lout
} // namespace unicode
#endif // __UNICODE_HH__