Initial import of Dillo
This commit is contained in:
22
lout/Makefile.am
Normal file
22
lout/Makefile.am
Normal 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
809
lout/container.cc
Normal 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
572
lout/container.hh
Normal 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
86
lout/debug.hh
Normal 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
460
lout/debug_rtfl.hh
Normal 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
131
lout/identity.cc
Normal 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
149
lout/identity.hh
Normal 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
188
lout/misc.cc
Normal 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
698
lout/misc.hh
Normal 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
38
lout/msg.h
Normal 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
386
lout/object.cc
Normal 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
238
lout/object.hh
Normal 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
170
lout/signal.cc
Normal 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
310
lout/signal.hh
Normal 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
180
lout/unicode.cc
Normal 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,
|
||||
// 0250–02AF: IPA Extensions
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
// 02B0–02FF: Spacing Modifier Letters
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
// 0300–036F: Combining Diacritical Marks
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0370–03FF: Greek and Coptic
|
||||
0xcf, 0x00, 0x40, 0x7d, 0xff, 0xff, 0xfb, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
// 0400–04FF: 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
30
lout/unicode.hh
Normal 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__
|
Reference in New Issue
Block a user