diff -r 000000000000 -r 1918ee327afb src/gui/text/qfragmentmap_p.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gui/text/qfragmentmap_p.h Mon Jan 11 14:00:40 2010 +0000 @@ -0,0 +1,883 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFRAGMENTMAP_P_H +#define QFRAGMENTMAP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "QtCore/qglobal.h" +#include +#include + +QT_BEGIN_NAMESPACE + + +template +class QFragment +{ +public: + quint32 parent; + quint32 left; + quint32 right; + quint32 color; + quint32 size_left_array[N]; + quint32 size_array[N]; + enum {size_array_max = N }; +}; + +template +class QFragmentMapData +{ + enum Color { Red, Black }; +public: + QFragmentMapData(); + ~QFragmentMapData(); + + void init(); + + class Header + { + public: + quint32 root; // this relies on being at the same position as parent in the fragment struct + quint32 tag; + quint32 freelist; + quint32 node_count; + quint32 allocated; + }; + + + enum {fragmentSize = sizeof(Fragment) }; + + + int length(uint field = 0) const; + + + inline Fragment *fragment(uint index) { + return (fragments + index); + } + inline const Fragment *fragment(uint index) const { + return (fragments + index); + } + + + inline Fragment &F(uint index) { return fragments[index] ; } + inline const Fragment &F(uint index) const { return fragments[index] ; } + + inline bool isRoot(uint index) const { + return !fragment(index)->parent; + } + + inline uint position(uint node, uint field = 0) const { + Q_ASSERT(field < Fragment::size_array_max); + const Fragment *f = fragment(node); + uint offset = f->size_left_array[field]; + while (f->parent) { + uint p = f->parent; + f = fragment(p); + if (f->right == node) + offset += f->size_left_array[field] + f->size_array[field]; + node = p; + } + return offset; + } + inline uint sizeRight(uint node, uint field = 0) const { + Q_ASSERT(field < Fragment::size_array_max); + uint sr = 0; + const Fragment *f = fragment(node); + node = f->right; + while (node) { + f = fragment(node); + sr += f->size_left_array[field] + f->size_array[field]; + node = f->right; + } + return sr; + } + inline uint sizeLeft(uint node, uint field = 0) const { + Q_ASSERT(field < Fragment::size_array_max); + return fragment(node)->size_left_array[field]; + } + + + inline uint size(uint node, uint field = 0) const { + Q_ASSERT(field < Fragment::size_array_max); + return fragment(node)->size_array[field]; + } + + inline void setSize(uint node, int new_size, uint field = 0) { + Q_ASSERT(field < Fragment::size_array_max); + Fragment *f = fragment(node); + int diff = new_size - f->size_array[field]; + f->size_array[field] = new_size; + while (f->parent) { + uint p = f->parent; + f = fragment(p); + if (f->left == node) + f->size_left_array[field] += diff; + node = p; + } + } + + + uint findNode(int k, uint field = 0) const; + + uint insert_single(int key, uint length); + uint erase_single(uint f); + + uint minimum(uint n) const { + while (n && fragment(n)->left) + n = fragment(n)->left; + return n; + } + + uint maximum(uint n) const { + while (n && fragment(n)->right) + n = fragment(n)->right; + return n; + } + + uint next(uint n) const; + uint previous(uint n) const; + + inline uint root() const { + Q_ASSERT(!head->root || !fragment(head->root)->parent); + return head->root; + } + inline void setRoot(uint new_root) { + Q_ASSERT(!head->root || !fragment(new_root)->parent); + head->root = new_root; + } + + union { + Header *head; + Fragment *fragments; + }; + +private: + + void rotateLeft(uint x); + void rotateRight(uint x); + void rebalance(uint x); + void removeAndRebalance(uint z); + + uint createFragment(); + void freeFragment(uint f); + +}; + +template +QFragmentMapData::QFragmentMapData() + : fragments(0) +{ + init(); +} + +template +void QFragmentMapData::init() +{ + // the following code will realloc an existing fragment or create a new one. + // it will also ignore errors when shrinking an existing fragment. + Fragment *newFragments = (Fragment *)realloc(fragments, 64*fragmentSize); + if (newFragments) { + fragments = newFragments; + head->allocated = 64; + } + Q_CHECK_PTR(fragments); + + head->tag = (((quint32)'p') << 24) | (((quint32)'m') << 16) | (((quint32)'a') << 8) | 'p'; //TAG('p', 'm', 'a', 'p'); + head->root = 0; + head->freelist = 1; + head->node_count = 0; + // mark all items to the right as unused + F(head->freelist).right = 0; +} + +template +QFragmentMapData::~QFragmentMapData() +{ + free(fragments); +} + +template +uint QFragmentMapData::createFragment() +{ + Q_ASSERT(head->freelist <= head->allocated); + + uint freePos = head->freelist; + if (freePos == head->allocated) { + // need to create some free space + uint needed = qAllocMore((freePos+1)*fragmentSize, 0); + Q_ASSERT(needed/fragmentSize > head->allocated); + Fragment *newFragments = (Fragment *)realloc(fragments, needed); + Q_CHECK_PTR(newFragments); + fragments = newFragments; + head->allocated = needed/fragmentSize; + F(freePos).right = 0; + } + + uint nextPos = F(freePos).right; + if (!nextPos) { + nextPos = freePos+1; + if (nextPos < head->allocated) + F(nextPos).right = 0; + } + + head->freelist = nextPos; + + ++head->node_count; + + return freePos; +} + +template +void QFragmentMapData::freeFragment(uint i) +{ + F(i).right = head->freelist; + head->freelist = i; + + --head->node_count; +} + + +template +uint QFragmentMapData::next(uint n) const { + Q_ASSERT(n); + if (F(n).right) { + n = F(n).right; + while (F(n).left) + n = F(n).left; + } else { + uint y = F(n).parent; + while (F(n).parent && n == F(y).right) { + n = y; + y = F(y).parent; + } + n = y; + } + return n; +} + +template +uint QFragmentMapData::previous(uint n) const { + if (!n) + return maximum(root()); + + if (F(n).left) { + n = F(n).left; + while (F(n).right) + n = F(n).right; + } else { + uint y = F(n).parent; + while (F(n).parent && n == F(y).left) { + n = y; + y = F(y).parent; + } + n = y; + } + return n; +} + + +/* + x y + \ / \ + y --> x b + / \ \ + a b a +*/ +template +void QFragmentMapData::rotateLeft(uint x) +{ + uint p = F(x).parent; + uint y = F(x).right; + + + if (y) { + F(x).right = F(y).left; + if (F(y).left) + F(F(y).left).parent = x; + F(y).left = x; + F(y).parent = p; + } else { + F(x).right = 0; + } + if (!p) { + Q_ASSERT(head->root == x); + head->root = y; + } + else if (x == F(p).left) + F(p).left = y; + else + F(p).right = y; + F(x).parent = y; + for (uint field = 0; field < Fragment::size_array_max; ++field) + F(y).size_left_array[field] += F(x).size_left_array[field] + F(x).size_array[field]; +} + + +/* + x y + / / \ + y --> a x + / \ / + a b b +*/ +template +void QFragmentMapData::rotateRight(uint x) +{ + uint y = F(x).left; + uint p = F(x).parent; + + if (y) { + F(x).left = F(y).right; + if (F(y).right) + F(F(y).right).parent = x; + F(y).right = x; + F(y).parent = p; + } else { + F(x).left = 0; + } + if (!p) { + Q_ASSERT(head->root == x); + head->root = y; + } + else if (x == F(p).right) + F(p).right = y; + else + F(p).left = y; + F(x).parent = y; + for (uint field = 0; field < Fragment::size_array_max; ++field) + F(x).size_left_array[field] -= F(y).size_left_array[field] + F(y).size_array[field]; +} + + +template +void QFragmentMapData::rebalance(uint x) +{ + F(x).color = Red; + + while (F(x).parent && F(F(x).parent).color == Red) { + uint p = F(x).parent; + uint pp = F(p).parent; + Q_ASSERT(pp); + if (p == F(pp).left) { + uint y = F(pp).right; + if (y && F(y).color == Red) { + F(p).color = Black; + F(y).color = Black; + F(pp).color = Red; + x = pp; + } else { + if (x == F(p).right) { + x = p; + rotateLeft(x); + p = F(x).parent; + pp = F(p).parent; + } + F(p).color = Black; + if (pp) { + F(pp).color = Red; + rotateRight(pp); + } + } + } else { + uint y = F(pp).left; + if (y && F(y).color == Red) { + F(p).color = Black; + F(y).color = Black; + F(pp).color = Red; + x = pp; + } else { + if (x == F(p).left) { + x = p; + rotateRight(x); + p = F(x).parent; + pp = F(p).parent; + } + F(p).color = Black; + if (pp) { + F(pp).color = Red; + rotateLeft(pp); + } + } + } + } + F(root()).color = Black; +} + + +template +uint QFragmentMapData::erase_single(uint z) +{ + uint w = previous(z); + uint y = z; + uint x; + uint p; + + if (!F(y).left) { + x = F(y).right; + } else if (!F(y).right) { + x = F(y).left; + } else { + y = F(y).right; + while (F(y).left) + y = F(y).left; + x = F(y).right; + } + + if (y != z) { + F(F(z).left).parent = y; + F(y).left = F(z).left; + for (uint field = 0; field < Fragment::size_array_max; ++field) + F(y).size_left_array[field] = F(z).size_left_array[field]; + if (y != F(z).right) { + /* + z y + / \ / \ + a b a b + / / + ... --> ... + / / + y x + / \ + 0 x + */ + p = F(y).parent; + if (x) + F(x).parent = p; + F(p).left = x; + F(y).right = F(z).right; + F(F(z).right).parent = y; + uint n = p; + while (n != y) { + for (uint field = 0; field < Fragment::size_array_max; ++field) + F(n).size_left_array[field] -= F(y).size_array[field]; + n = F(n).parent; + } + } else { + /* + z y + / \ / \ + a y --> a x + / \ + 0 x + */ + p = y; + } + uint zp = F(z).parent; + if (!zp) { + Q_ASSERT(head->root == z); + head->root = y; + } else if (F(zp).left == z) { + F(zp).left = y; + for (uint field = 0; field < Fragment::size_array_max; ++field) + F(zp).size_left_array[field] -= F(z).size_array[field]; + } else { + F(zp).right = y; + } + F(y).parent = zp; + // Swap the colors + uint c = F(y).color; + F(y).color = F(z).color; + F(z).color = c; + y = z; + } else { + /* + p p p p + / / \ \ + z --> x z --> x + | | + x x + */ + p = F(z).parent; + if (x) + F(x).parent = p; + if (!p) { + Q_ASSERT(head->root == z); + head->root = x; + } else if (F(p).left == z) { + F(p).left = x; + for (uint field = 0; field < Fragment::size_array_max; ++field) + F(p).size_left_array[field] -= F(z).size_array[field]; + } else { + F(p).right = x; + } + } + uint n = z; + while (F(n).parent) { + uint p = F(n).parent; + if (F(p).left == n) { + for (uint field = 0; field < Fragment::size_array_max; ++field) + F(p).size_left_array[field] -= F(z).size_array[field]; + } + n = p; + } + + freeFragment(z); + + + if (F(y).color != Red) { + while (F(x).parent && (x == 0 || F(x).color == Black)) { + if (x == F(p).left) { + uint w = F(p).right; + if (F(w).color == Red) { + F(w).color = Black; + F(p).color = Red; + rotateLeft(p); + w = F(p).right; + } + if ((F(w).left == 0 || F(F(w).left).color == Black) && + (F(w).right == 0 || F(F(w).right).color == Black)) { + F(w).color = Red; + x = p; + p = F(x).parent; + } else { + if (F(w).right == 0 || F(F(w).right).color == Black) { + if (F(w).left) + F(F(w).left).color = Black; + F(w).color = Red; + rotateRight(F(p).right); + w = F(p).right; + } + F(w).color = F(p).color; + F(p).color = Black; + if (F(w).right) + F(F(w).right).color = Black; + rotateLeft(p); + break; + } + } else { + uint w = F(p).left; + if (F(w).color == Red) { + F(w).color = Black; + F(p).color = Red; + rotateRight(p); + w = F(p).left; + } + if ((F(w).right == 0 || F(F(w).right).color == Black) && + (F(w).left == 0 || F(F(w).left).color == Black)) { + F(w).color = Red; + x = p; + p = F(x).parent; + } else { + if (F(w).left == 0 || F(F(w).left).color == Black) { + if (F(w).right) + F(F(w).right).color = Black; + F(w).color = Red; + rotateLeft(F(p).left); + w = F(p).left; + } + F(w).color = F(p).color; + F(p).color = Black; + if (F(w).left) + F(F(w).left).color = Black; + rotateRight(p); + break; + } + } + } + if (x) + F(x).color = Black; + } + + return w; +} + +template +uint QFragmentMapData::findNode(int k, uint field) const +{ + Q_ASSERT(field < Fragment::size_array_max); + uint x = root(); + + uint s = k; + while (x) { + if (sizeLeft(x, field) <= s) { + if (s < sizeLeft(x, field) + size(x, field)) + return x; + s -= sizeLeft(x, field) + size(x, field); + x = F(x).right; + } else { + x = F(x).left; + } + } + return 0; +} + +template +uint QFragmentMapData::insert_single(int key, uint length) +{ + Q_ASSERT(!findNode(key) || (int)this->position(findNode(key)) == key); + + uint z = createFragment(); + + F(z).left = 0; + F(z).right = 0; + F(z).size_array[0] = length; + for (uint field = 1; field < Fragment::size_array_max; ++field) + F(z).size_array[field] = 1; + for (uint field = 0; field < Fragment::size_array_max; ++field) + F(z).size_left_array[field] = 0; + + uint y = 0; + uint x = root(); + + Q_ASSERT(!x || F(x).parent == 0); + + uint s = key; + bool right = false; + while (x) { + y = x; + if (s <= F(x).size_left_array[0]) { + x = F(x).left; + right = false; + } else { + s -= F(x).size_left_array[0] + F(x).size_array[0]; + x = F(x).right; + right = true; + } + } + + F(z).parent = y; + if (!y) { + head->root = z; + } else if (!right) { + F(y).left = z; + for (uint field = 0; field < Fragment::size_array_max; ++field) + F(y).size_left_array[field] = F(z).size_array[field]; + } else { + F(y).right = z; + } + while (y && F(y).parent) { + uint p = F(y).parent; + if (F(p).left == y) { + for (uint field = 0; field < Fragment::size_array_max; ++field) + F(p).size_left_array[field] += F(z).size_array[field]; + } + y = p; + } + rebalance(z); + + return z; +} + + +template +int QFragmentMapData::length(uint field) const { + uint root = this->root(); + return root ? sizeLeft(root, field) + size(root, field) + sizeRight(root, field) : 0; +} + + +template // NOTE: must inherit QFragment +class QFragmentMap +{ +public: + class Iterator + { + public: + QFragmentMap *pt; + quint32 n; + + Iterator() : pt(0), n(0) {} + Iterator(QFragmentMap *p, int node) : pt(p), n(node) {} + Iterator(const Iterator& it) : pt(it.pt), n(it.n) {} + + inline bool atEnd() const { return !n; } + + bool operator==(const Iterator& it) const { return pt == it.pt && n == it.n; } + bool operator!=(const Iterator& it) const { return pt != it.pt || n != it.n; } + bool operator<(const Iterator &it) const { return position() < it.position(); } + + Fragment *operator*() { Q_ASSERT(!atEnd()); return pt->fragment(n); } + const Fragment *operator*() const { Q_ASSERT(!atEnd()); return pt->fragment(n); } + Fragment *operator->() { Q_ASSERT(!atEnd()); return pt->fragment(n); } + const Fragment *operator->() const { Q_ASSERT(!atEnd()); return pt->fragment(n); } + + int position() const { Q_ASSERT(!atEnd()); return pt->data.position(n); } + const Fragment *value() const { Q_ASSERT(!atEnd()); return pt->fragment(n); } + Fragment *value() { Q_ASSERT(!atEnd()); return pt->fragment(n); } + + Iterator& operator++() { + n = pt->data.next(n); + return *this; + } + Iterator& operator--() { + n = pt->data.previous(n); + return *this; + } + + }; + + + class ConstIterator + { + public: + const QFragmentMap *pt; + quint32 n; + + /** + * Functions + */ + ConstIterator() : pt(0), n(0) {} + ConstIterator(const QFragmentMap *p, int node) : pt(p), n(node) {} + ConstIterator(const ConstIterator& it) : pt(it.pt), n(it.n) {} + ConstIterator(const Iterator& it) : pt(it.pt), n(it.n) {} + + inline bool atEnd() const { return !n; } + + bool operator==(const ConstIterator& it) const { return pt == it.pt && n == it.n; } + bool operator!=(const ConstIterator& it) const { return pt != it.pt || n != it.n; } + bool operator<(const ConstIterator &it) const { return position() < it.position(); } + + const Fragment *operator*() const { Q_ASSERT(!atEnd()); return pt->fragment(n); } + const Fragment *operator->() const { Q_ASSERT(!atEnd()); return pt->fragment(n); } + + int position() const { Q_ASSERT(!atEnd()); return pt->data.position(n); } + int size() const { Q_ASSERT(!atEnd()); return pt->data.size(n); } + const Fragment *value() const { Q_ASSERT(!atEnd()); return pt->fragment(n); } + + ConstIterator& operator++() { + n = pt->data.next(n); + return *this; + } + ConstIterator& operator--() { + n = pt->data.previous(n); + return *this; + } + }; + + + QFragmentMap() {} + ~QFragmentMap() + { + if (!data.fragments) + return; // in case of out-of-memory, we won't have fragments + for (Iterator it = begin(); !it.atEnd(); ++it) + it.value()->free(); + } + + inline void clear() { + for (Iterator it = begin(); !it.atEnd(); ++it) + it.value()->free(); + data.init(); + } + + inline Iterator begin() { return Iterator(this, data.minimum(data.root())); } + inline Iterator end() { return Iterator(this, 0); } + inline ConstIterator begin() const { return ConstIterator(this, data.minimum(data.root())); } + inline ConstIterator end() const { return ConstIterator(this, 0); } + + inline ConstIterator last() const { return ConstIterator(this, data.maximum(data.root())); } + + inline bool isEmpty() const { return data.head->node_count == 0; } + inline int numNodes() const { return data.head->node_count; } + int length(uint field = 0) const { return data.length(field); } + + Iterator find(int k, uint field = 0) { return Iterator(this, data.findNode(k, field)); } + ConstIterator find(int k, uint field = 0) const { return ConstIterator(this, data.findNode(k, field)); } + + uint findNode(int k, uint field = 0) const { return data.findNode(k, field); } + + uint insert_single(int key, uint length) + { + uint f = data.insert_single(key, length); + if (f != 0) { + Fragment *frag = fragment(f); + Q_ASSERT(frag); + frag->initialize(); + } + return f; + } + uint erase_single(uint f) + { + if (f != 0) { + Fragment *frag = fragment(f); + Q_ASSERT(frag); + frag->free(); + } + return data.erase_single(f); + } + + inline Fragment *fragment(uint index) { + Q_ASSERT(index != 0); + return data.fragment(index); + } + inline const Fragment *fragment(uint index) const { + Q_ASSERT(index != 0); + return data.fragment(index); + } + inline uint position(uint node, uint field = 0) const { return data.position(node, field); } + inline uint next(uint n) const { return data.next(n); } + inline uint previous(uint n) const { return data.previous(n); } + inline uint size(uint node, uint field = 0) const { return data.size(node, field); } + inline void setSize(uint node, int new_size, uint field = 0) + { data.setSize(node, new_size, field); + if (node != 0 && field == 0) { + Fragment *frag = fragment(node); + Q_ASSERT(frag); + frag->invalidate(); + } + } + + inline int firstNode() const { return data.minimum(data.root()); } + +private: + friend class Iterator; + friend class ConstIterator; + + QFragmentMapData data; + + QFragmentMap(const QFragmentMap& m); + QFragmentMap& operator= (const QFragmentMap& m); +}; + +QT_END_NAMESPACE + +#endif // QFRAGMENTMAP_P_H