mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-26 20:03:10 +00:00
1130 lines
39 KiB
C++
1130 lines
39 KiB
C++
![]() |
/***************************************************************************
|
||
|
Copyright 2007 David Nolden <david.nolden.kdevelop@art-master.de>
|
||
|
***************************************************************************/
|
||
|
|
||
|
/***************************************************************************
|
||
|
* *
|
||
|
* 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 2 of the License, or *
|
||
|
* (at your option) any later version. *
|
||
|
* *
|
||
|
***************************************************************************/
|
||
|
|
||
|
#include "setrepository.h"
|
||
|
#include <list>
|
||
|
#include <QtCore/QString>
|
||
|
#include <util/kdevvarlengtharray.h>
|
||
|
#include <iostream>
|
||
|
#include <limits>
|
||
|
#include <language/duchain/repositories/itemrepository.h>
|
||
|
#include <QtCore/QMutex>
|
||
|
#include <QtCore/QStack>
|
||
|
#include <algorithm>
|
||
|
|
||
|
//#define DEBUG
|
||
|
|
||
|
#include "somehash.cpp"
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
#define ifDebug(X) X
|
||
|
#else
|
||
|
#define ifDebug(x)
|
||
|
#undef Q_ASSERT
|
||
|
#define Q_ASSERT(x)
|
||
|
#endif
|
||
|
|
||
|
#ifndef DEBUG
|
||
|
#define CHECK_SPLIT_POSITION(Node)
|
||
|
#else
|
||
|
#define CHECK_SPLIT_POSITION(node) Q_ASSERT(!(node).leftNode || (getLeftNode(&node)->end() <= splitPositionForRange((node).start, (node).end) && getRightNode(&node)->start() >= splitPositionForRange((node).start, (node).end)))
|
||
|
#endif
|
||
|
|
||
|
namespace Utils {
|
||
|
|
||
|
/**
|
||
|
* To achieve a maximum re-usage of nodes, we make sure that sub-nodes of a node always split at specific boundaries.
|
||
|
* For each range we can compute a position where that range should be split into its child-nodes.
|
||
|
* When creating a new node with 2 sub-nodes, we re-create those child-nodes if their boundaries don't represent those split-positions.
|
||
|
*
|
||
|
* We pick the split-positions deterministically, they are in order of priority:
|
||
|
* ((1<<31)*n, n = [0,...]
|
||
|
* ((1<<30)*n, n = [0,...]
|
||
|
* ((1<<29)*n, n = [0,...]
|
||
|
* ((1<<...)*n, n = [0,...]
|
||
|
* ...
|
||
|
* */
|
||
|
|
||
|
|
||
|
typedef BasicSetRepository::Index Index;
|
||
|
|
||
|
///The returned split position shall be the end of the first sub-range, and the start of the second
|
||
|
///@param splitBit should be initialized with 31, unless you know better. The value can then be used on while computing child split positions.
|
||
|
///In the end, it will contain the bit used to split the range. It will also contain zero if no split-position exists(length 1)
|
||
|
uint splitPositionForRange(uint start, uint end, uchar& splitBit) {
|
||
|
|
||
|
if(end-start == 1) {
|
||
|
splitBit = 0;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
while(true) {
|
||
|
uint position = ((end-1) >> splitBit) << splitBit; //Round to the split-position in this interval that is smaller than end
|
||
|
if(position > start && position < end)
|
||
|
return position;
|
||
|
Q_ASSERT(splitBit != 0);
|
||
|
--splitBit;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
uint SetNodeData::calculateHash() const {
|
||
|
//A hash to reduce the amount of clashes
|
||
|
return hashlittle(this, sizeof(uint)*4, 1748231);
|
||
|
}
|
||
|
|
||
|
|
||
|
uint splitPositionForRange(uint start, uint end) {
|
||
|
uchar splitBit = 31;
|
||
|
return splitPositionForRange(start, end, splitBit);
|
||
|
}
|
||
|
|
||
|
class SetNodeDataRequest;
|
||
|
|
||
|
#define getLeftNode(node) repository.itemFromIndex(node->leftNode())
|
||
|
#define getRightNode(node) repository.itemFromIndex(node->rightNode())
|
||
|
#define nodeFromIndex(index) repository.itemFromIndex(index)
|
||
|
struct SetRepositoryAlgorithms {
|
||
|
SetRepositoryAlgorithms(SetDataRepository& _repository, BasicSetRepository* _setRepository) : repository(_repository), setRepository(_setRepository) {
|
||
|
}
|
||
|
|
||
|
///Expensive
|
||
|
Index count(const SetNodeData* node) const;
|
||
|
|
||
|
void localCheck(const SetNodeData* node);
|
||
|
|
||
|
void check(uint node);
|
||
|
|
||
|
void check(const SetNodeData* node);
|
||
|
|
||
|
QString shortLabel(const SetNodeData& node) const;
|
||
|
|
||
|
uint set_union(uint firstNode, uint secondNode, const SetNodeData* first, const SetNodeData* second, uchar splitBit = 31);
|
||
|
uint createSetFromNodes(uint leftNode, uint rightNode, const SetNodeData* left = 0, const SetNodeData* right = 0);
|
||
|
uint computeSetFromNodes(uint leftNode, uint rightNode, const SetNodeData* left, const SetNodeData* right, uchar splitBit);
|
||
|
uint set_intersect(uint firstNode, uint secondNode, const SetNodeData* first, const SetNodeData* second, uchar splitBit = 31);
|
||
|
bool set_contains(const SetNodeData* node, Index index);
|
||
|
uint set_subtract(uint firstNode, uint secondNode, const SetNodeData* first, const SetNodeData* second, uchar splitBit = 31);
|
||
|
|
||
|
//Required both nodes to be split correctly
|
||
|
bool set_equals(const SetNodeData* lhs, const SetNodeData* rhs);
|
||
|
|
||
|
QString dumpDotGraph(uint node) const;
|
||
|
|
||
|
///Finds or inserts the given ranges into the repository, and returns the set-index that represents them
|
||
|
uint setForIndices(std::vector<uint>::const_iterator begin, std::vector<uint>::const_iterator end, uchar splitBit = 31) {
|
||
|
Q_ASSERT(begin != end);
|
||
|
|
||
|
uint startIndex = *begin;
|
||
|
uint endIndex = *(end-1)+1;
|
||
|
|
||
|
if(endIndex == startIndex+1) {
|
||
|
SetNodeData data(startIndex, endIndex);
|
||
|
|
||
|
return repository.index( SetNodeDataRequest(&data, repository, setRepository) );
|
||
|
}
|
||
|
|
||
|
uint split = splitPositionForRange(startIndex, endIndex, splitBit);
|
||
|
Q_ASSERT(split);
|
||
|
|
||
|
std::vector<uint>::const_iterator splitIterator = std::lower_bound(begin, end, split);
|
||
|
Q_ASSERT(*splitIterator >= split);
|
||
|
Q_ASSERT(splitIterator > begin);
|
||
|
Q_ASSERT(*(splitIterator-1) < split);
|
||
|
|
||
|
return createSetFromNodes(setForIndices(begin, splitIterator, splitBit), setForIndices(splitIterator, end, splitBit));
|
||
|
}
|
||
|
|
||
|
|
||
|
private:
|
||
|
QString dumpDotGraphInternal(uint node, bool master=false) const;
|
||
|
|
||
|
SetDataRepository& repository;
|
||
|
BasicSetRepository* setRepository;
|
||
|
};
|
||
|
|
||
|
void SetNodeDataRequest::destroy(SetNodeData* data, KDevelop::AbstractItemRepository& _repository) {
|
||
|
SetDataRepository& repository(static_cast<SetDataRepository&>(_repository));
|
||
|
|
||
|
if(repository.setRepository->delayedDeletion()) {
|
||
|
|
||
|
if(data->leftNode()){
|
||
|
SetDataRepositoryBase::MyDynamicItem left = repository.dynamicItemFromIndex(data->leftNode());
|
||
|
SetDataRepositoryBase::MyDynamicItem right = repository.dynamicItemFromIndex(data->rightNode());
|
||
|
Q_ASSERT(left->m_refCount > 0);
|
||
|
--left->m_refCount;
|
||
|
Q_ASSERT(right->m_refCount > 0);
|
||
|
--right->m_refCount;
|
||
|
}else {
|
||
|
//Deleting a leaf
|
||
|
Q_ASSERT(data->end() - data->start() == 1);
|
||
|
repository.setRepository->itemRemovedFromSets(data->start());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SetNodeDataRequest::SetNodeDataRequest(const SetNodeData* _data, SetDataRepository& _repository, BasicSetRepository* _setRepository) : data(*_data), m_hash(_data->hash()), repository(_repository), setRepository(_setRepository), m_created(false) {
|
||
|
ifDebug( SetRepositoryAlgorithms alg(repository); alg.check(_data) );
|
||
|
}
|
||
|
|
||
|
SetNodeDataRequest::~SetNodeDataRequest() {
|
||
|
//Eventually increase the reference-count of direct children
|
||
|
if(m_created) {
|
||
|
if(data.leftNode())
|
||
|
++repository.dynamicItemFromIndex(data.leftNode())->m_refCount;
|
||
|
if(data.rightNode())
|
||
|
++repository.dynamicItemFromIndex(data.rightNode())->m_refCount;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Should create an item where the information of the requested item is permanently stored. The pointer
|
||
|
//@param item equals an allocated range with the size of itemSize().
|
||
|
void SetNodeDataRequest::createItem(SetNodeData* item) const {
|
||
|
Q_ASSERT((data.rightNode() && data.leftNode()) || (!data.rightNode() && !data.leftNode()));
|
||
|
|
||
|
m_created = true;
|
||
|
|
||
|
*item = data;
|
||
|
|
||
|
Q_ASSERT((item->rightNode() && item->leftNode()) || (!item->rightNode() && !item->leftNode()));
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
//Make sure we split at the correct split position
|
||
|
if(item->hasSlaves()) {
|
||
|
uint split = splitPositionForRange(data.start, data.end);
|
||
|
const SetNodeData* left = repository.itemFromIndex(item->leftNode());
|
||
|
const SetNodeData* right = repository.itemFromIndex(item->rightNode());
|
||
|
Q_ASSERT(split >= left->end() && split <= right->start());
|
||
|
}
|
||
|
#endif
|
||
|
if(!data.leftNode() && setRepository) {
|
||
|
for(uint a = item->start(); a < item->end(); ++a)
|
||
|
setRepository->itemAddedToSets(a);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool SetNodeDataRequest::equals(const SetNodeData* item) const {
|
||
|
Q_ASSERT((item->rightNode() && item->leftNode()) || (!item->rightNode() && !item->leftNode()));
|
||
|
//Just compare child nodes, since data must be correctly split, this is perfectly ok
|
||
|
//Since this happens in very tight loops, we don't call an additional function here, but just do the check.
|
||
|
return item->leftNode() == data.leftNode() && item->rightNode() == data.rightNode() && item->start() == data.start() && item->end() == data.end();
|
||
|
}
|
||
|
|
||
|
class BasicSetRepository::Private {
|
||
|
public:
|
||
|
|
||
|
Private(QString _name) : name(_name) {
|
||
|
}
|
||
|
~Private() {
|
||
|
}
|
||
|
|
||
|
QString name;
|
||
|
private:
|
||
|
};
|
||
|
|
||
|
Set::Set() : m_tree(0), m_repository(0) {
|
||
|
}
|
||
|
|
||
|
Set::~Set() {
|
||
|
}
|
||
|
|
||
|
unsigned int Set::count() const {
|
||
|
if(!m_repository || !m_tree)
|
||
|
return 0;
|
||
|
QMutexLocker lock(m_repository->m_mutex);
|
||
|
|
||
|
SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository);
|
||
|
return alg.count(m_repository->dataRepository.itemFromIndex(m_tree));
|
||
|
}
|
||
|
|
||
|
Set::Set(uint treeNode, BasicSetRepository* repository) : m_tree(treeNode), m_repository(repository) {
|
||
|
}
|
||
|
|
||
|
|
||
|
Set::Set(const Set& rhs) {
|
||
|
m_repository = rhs.m_repository;
|
||
|
m_tree = rhs.m_tree;
|
||
|
}
|
||
|
|
||
|
Set& Set::operator=(const Set& rhs) {
|
||
|
m_repository = rhs.m_repository;
|
||
|
m_tree = rhs.m_tree;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
QString Set::dumpDotGraph() const {
|
||
|
if(!m_repository || !m_tree)
|
||
|
return QString();
|
||
|
|
||
|
QMutexLocker lock(m_repository->m_mutex);
|
||
|
|
||
|
SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository);
|
||
|
return alg.dumpDotGraph(m_tree);
|
||
|
}
|
||
|
|
||
|
Index SetRepositoryAlgorithms::count(const SetNodeData* node) const {
|
||
|
if(node->leftNode() && node->rightNode())
|
||
|
return count(getLeftNode(node)) + count(getRightNode(node));
|
||
|
else
|
||
|
return node->end() - node->start();
|
||
|
}
|
||
|
|
||
|
void SetRepositoryAlgorithms::localCheck(const SetNodeData* ifDebug(node) ) {
|
||
|
// Q_ASSERT(node->start() > 0);
|
||
|
Q_ASSERT(node->start() < node->end());
|
||
|
Q_ASSERT((node->leftNode() && node->rightNode()) || (!node->leftNode() && !node->rightNode()));
|
||
|
Q_ASSERT(!node->leftNode() || (getLeftNode(node())->start() == node->start() && getRightNode(node)->end() == node->end()));
|
||
|
Q_ASSERT(!node->leftNode() || (getLeftNode(node())->end() <= getRightNode(node)->start()));
|
||
|
}
|
||
|
|
||
|
void SetRepositoryAlgorithms::check(uint node) {
|
||
|
if(!node)
|
||
|
return;
|
||
|
|
||
|
check(nodeFromIndex(node));
|
||
|
}
|
||
|
|
||
|
void SetRepositoryAlgorithms::check(const SetNodeData* node) {
|
||
|
localCheck(node);
|
||
|
if(node->leftNode())
|
||
|
check(getLeftNode(node));
|
||
|
if(node->rightNode())
|
||
|
check(getRightNode(node));
|
||
|
// CHECK_SPLIT_POSITION(*node); Re-enable this
|
||
|
}
|
||
|
|
||
|
QString SetRepositoryAlgorithms::shortLabel(const SetNodeData& node) const {
|
||
|
return QString("n%1_%2").arg(node.start()).arg(node.end());
|
||
|
}
|
||
|
|
||
|
QString SetRepositoryAlgorithms::dumpDotGraphInternal(uint nodeIndex, bool master) const {
|
||
|
if(!nodeIndex)
|
||
|
return QString("empty node");
|
||
|
|
||
|
const SetNodeData& node(*repository.itemFromIndex(nodeIndex));
|
||
|
|
||
|
QString color = "blue";
|
||
|
if(master)
|
||
|
color = "red";
|
||
|
|
||
|
QString label = QString("%1 -> %2").arg(node.start()).arg(node.end());
|
||
|
if(!node.contiguous())
|
||
|
label += ", with gaps";
|
||
|
|
||
|
QString ret = QString("%1[label=\"%2\", color=\"%3\"];\n").arg(shortLabel(node)).arg(label).arg(color);
|
||
|
|
||
|
if(node.leftNode()) {
|
||
|
const SetNodeData& left(*repository.itemFromIndex(node.leftNode()));
|
||
|
const SetNodeData& right(*repository.itemFromIndex(node.rightNode()));
|
||
|
Q_ASSERT(node.rightNode());
|
||
|
ret += QString("%1 -> %2;\n").arg(shortLabel(node)).arg(shortLabel(left));
|
||
|
ret += QString("%1 -> %2;\n").arg(shortLabel(node)).arg(shortLabel(right));
|
||
|
ret += dumpDotGraphInternal(node.leftNode());
|
||
|
ret += dumpDotGraphInternal(node.rightNode());
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
QString SetRepositoryAlgorithms::dumpDotGraph(uint nodeIndex) const {
|
||
|
QString ret = "digraph Repository {\n";
|
||
|
ret += dumpDotGraphInternal(nodeIndex, true);
|
||
|
ret += "}\n";
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
const int nodeStackAlloc = 500;
|
||
|
|
||
|
class Set::Iterator::IteratorPrivate {
|
||
|
public:
|
||
|
|
||
|
IteratorPrivate() : nodeStackSize(0), currentIndex(0), repository(0) {
|
||
|
nodeStackData.resize(nodeStackAlloc);
|
||
|
nodeStack = nodeStackData.data();
|
||
|
}
|
||
|
|
||
|
IteratorPrivate(const IteratorPrivate& rhs) : nodeStackData(rhs.nodeStackData), nodeStackSize(rhs.nodeStackSize), currentIndex(rhs.currentIndex), repository(rhs.repository) {
|
||
|
nodeStack = nodeStackData.data();
|
||
|
}
|
||
|
|
||
|
void resizeNodeStack() {
|
||
|
nodeStackData.resize(nodeStackSize + 1);
|
||
|
nodeStack = nodeStackData.data();
|
||
|
}
|
||
|
|
||
|
KDevVarLengthArray<const SetNodeData*, nodeStackAlloc> nodeStackData;
|
||
|
const SetNodeData** nodeStack;
|
||
|
int nodeStackSize;
|
||
|
Index currentIndex;
|
||
|
BasicSetRepository* repository;
|
||
|
|
||
|
/**
|
||
|
* Pushes the noed on top of the stack, changes currentIndex, and goes as deep as necessary for iteration.
|
||
|
* */
|
||
|
void startAtNode(const SetNodeData* node) {
|
||
|
Q_ASSERT(node->start() != node->end());
|
||
|
currentIndex = node->start();
|
||
|
|
||
|
do {
|
||
|
nodeStack[nodeStackSize++] = node;
|
||
|
|
||
|
if(nodeStackSize >= nodeStackAlloc)
|
||
|
resizeNodeStack();
|
||
|
|
||
|
if(node->contiguous())
|
||
|
break; //We need no finer granularity, because the range is contiguous
|
||
|
node = Set::Iterator::getDataRepository(repository).itemFromIndex(node->leftNode());
|
||
|
} while(node);
|
||
|
Q_ASSERT(currentIndex >= nodeStack[0]->start());
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
std::set<Index> Set::stdSet() const
|
||
|
{
|
||
|
Set::Iterator it = iterator();
|
||
|
std::set<Index> ret;
|
||
|
|
||
|
while(it) {
|
||
|
Q_ASSERT(ret.find(*it) == ret.end());
|
||
|
ret.insert(*it);
|
||
|
++it;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
Set::Iterator::Iterator(const Iterator& rhs) : d(new IteratorPrivate(*rhs.d)) {
|
||
|
}
|
||
|
|
||
|
Set::Iterator& Set::Iterator::operator=(const Iterator& rhs) {
|
||
|
delete d;
|
||
|
d = new IteratorPrivate(*rhs.d);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
Set::Iterator::Iterator() : d(new IteratorPrivate) {
|
||
|
}
|
||
|
|
||
|
Set::Iterator::~Iterator() {
|
||
|
delete d;
|
||
|
}
|
||
|
|
||
|
Set::Iterator::operator bool() const {
|
||
|
return d->nodeStackSize;
|
||
|
}
|
||
|
|
||
|
Set::Iterator& Set::Iterator::operator++() {
|
||
|
|
||
|
Q_ASSERT(d->nodeStackSize);
|
||
|
|
||
|
if(d->repository->m_mutex)
|
||
|
d->repository->m_mutex->lockInline();
|
||
|
|
||
|
++d->currentIndex;
|
||
|
|
||
|
//const SetNodeData** currentNode = &d->nodeStack[d->nodeStackSize - 1];
|
||
|
if(d->currentIndex >= d->nodeStack[d->nodeStackSize - 1]->end()) {
|
||
|
//Advance to the next node
|
||
|
while(d->nodeStackSize && d->currentIndex >= d->nodeStack[d->nodeStackSize - 1]->end()) {
|
||
|
--d->nodeStackSize;
|
||
|
}
|
||
|
|
||
|
if(!d->nodeStackSize) {
|
||
|
//ready
|
||
|
}else{
|
||
|
//++d->nodeStackSize;
|
||
|
//We were iterating the left slave of the node, now continue with the right.
|
||
|
ifDebug( const SetNodeData& left = *d->repository->dataRepository.itemFromIndex(d->nodeStack[d->nodeStackSize - 1]->leftNode()); Q_ASSERT(left.end == d->currentIndex); )
|
||
|
|
||
|
const SetNodeData& right = *d->repository->dataRepository.itemFromIndex(d->nodeStack[d->nodeStackSize - 1]->rightNode());
|
||
|
|
||
|
d->startAtNode(&right);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Q_ASSERT(d->nodeStackSize == 0 || d->currentIndex < d->nodeStack[0]->end());
|
||
|
|
||
|
if(d->repository->m_mutex)
|
||
|
d->repository->m_mutex->unlockInline();
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
BasicSetRepository::Index Set::Iterator::operator*() const {
|
||
|
return d->currentIndex;
|
||
|
}
|
||
|
|
||
|
Set::Iterator Set::iterator() const {
|
||
|
if(!m_tree || !m_repository)
|
||
|
return Iterator();
|
||
|
|
||
|
QMutexLocker lock(m_repository->m_mutex);
|
||
|
|
||
|
Iterator ret;
|
||
|
ret.d->repository = m_repository;
|
||
|
|
||
|
if(m_tree)
|
||
|
ret.d->startAtNode(m_repository->dataRepository.itemFromIndex(m_tree));
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
//Creates a set item with the given children., they must be valid, and they must be split around their split-position.
|
||
|
uint SetRepositoryAlgorithms::createSetFromNodes(uint leftNode, uint rightNode, const SetNodeData* left, const SetNodeData* right) {
|
||
|
|
||
|
if(!left)
|
||
|
left = nodeFromIndex(leftNode);
|
||
|
if(!right)
|
||
|
right = nodeFromIndex(rightNode);
|
||
|
|
||
|
Q_ASSERT(left->end() <= right->start());
|
||
|
|
||
|
SetNodeData set(left->start(), right->end(), leftNode, rightNode);
|
||
|
|
||
|
Q_ASSERT(set.start() < set.end());
|
||
|
|
||
|
uint ret = repository.index(SetNodeDataRequest(&set, repository, setRepository));
|
||
|
Q_ASSERT(set.leftNode() >= 0x10000);
|
||
|
Q_ASSERT(set.rightNode() >= 0x10000);
|
||
|
Q_ASSERT(ret == repository.findIndex(SetNodeDataRequest(&set, repository, setRepository)));
|
||
|
ifDebug( check(ret) );
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
//Constructs a set node from the given two sub-nodes. Those must be valid, they must not intersect, and they must have a correct split-hierarchy.
|
||
|
//The do not need to be split around their computed split-position.
|
||
|
uint SetRepositoryAlgorithms::computeSetFromNodes(uint leftNode, uint rightNode, const SetNodeData* left, const SetNodeData* right, uchar splitBit)
|
||
|
{
|
||
|
Q_ASSERT(left->end() <= right->start());
|
||
|
uint splitPosition = splitPositionForRange(left->start(), right->end(), splitBit);
|
||
|
|
||
|
Q_ASSERT(splitPosition);
|
||
|
|
||
|
if(splitPosition < left->end()) {
|
||
|
//The split-position intersects the left node
|
||
|
uint leftLeftNode = left->leftNode();
|
||
|
uint leftRightNode = left->rightNode();
|
||
|
|
||
|
const SetNodeData* leftLeft = this->getLeftNode(left);
|
||
|
const SetNodeData* leftRight = this->getRightNode(left);
|
||
|
|
||
|
Q_ASSERT(splitPosition >= leftLeft->end() && splitPosition <= leftRight->start());
|
||
|
|
||
|
//Create a new set from leftLeft, and from leftRight + right. That set will have the correct split-position.
|
||
|
uint newRightNode = computeSetFromNodes(leftRightNode, rightNode, leftRight, right, splitBit);
|
||
|
|
||
|
return createSetFromNodes(leftLeftNode, newRightNode, leftLeft);
|
||
|
|
||
|
}else if(splitPosition > right->start()) {
|
||
|
//The split-position intersects the right node
|
||
|
uint rightLeftNode = right->leftNode();
|
||
|
uint rightRightNode = right->rightNode();
|
||
|
|
||
|
const SetNodeData* rightLeft = this->getLeftNode(right);
|
||
|
const SetNodeData* rightRight = this->getRightNode(right);
|
||
|
|
||
|
Q_ASSERT(splitPosition >= rightLeft->end() && splitPosition <= rightRight->start());
|
||
|
|
||
|
//Create a new set from left + rightLeft, and from rightRight. That set will have the correct split-position.
|
||
|
uint newLeftNode = computeSetFromNodes(leftNode, rightLeftNode, left, rightLeft, splitBit);
|
||
|
|
||
|
return createSetFromNodes(newLeftNode, rightRightNode, 0, rightRight);
|
||
|
}else{
|
||
|
return createSetFromNodes(leftNode, rightNode, left, right);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
uint SetRepositoryAlgorithms::set_union(uint firstNode, uint secondNode, const SetNodeData* first, const SetNodeData* second, uchar splitBit)
|
||
|
{
|
||
|
if(firstNode == secondNode)
|
||
|
return firstNode;
|
||
|
|
||
|
uint firstStart = first->start(), secondEnd = second->end();
|
||
|
|
||
|
if(firstStart >= secondEnd)
|
||
|
return computeSetFromNodes(secondNode, firstNode, second, first, splitBit);
|
||
|
|
||
|
uint firstEnd = first->end(), secondStart = second->start();
|
||
|
|
||
|
if(secondStart >= firstEnd)
|
||
|
return computeSetFromNodes(firstNode, secondNode, first, second, splitBit);
|
||
|
|
||
|
//The ranges of first and second do intersect
|
||
|
|
||
|
uint newStart = firstStart < secondStart ? firstStart : secondStart;
|
||
|
uint newEnd = firstEnd > secondEnd ? firstEnd : secondEnd;
|
||
|
|
||
|
//Compute the split-position for the resulting merged node
|
||
|
uint splitPosition = splitPositionForRange(newStart, newEnd, splitBit);
|
||
|
|
||
|
//Since the ranges overlap, we can be sure that either first or second contain splitPosition.
|
||
|
//The node that contains it, will also be split by it.
|
||
|
|
||
|
if(splitPosition > firstStart && splitPosition < firstEnd && splitPosition > secondStart && splitPosition < secondEnd) {
|
||
|
//The split-position intersect with both first and second. Continue the union on both sides of the split-position, and merge it.
|
||
|
|
||
|
uint firstLeftNode = first->leftNode();
|
||
|
uint firstRightNode = first->rightNode();
|
||
|
uint secondLeftNode = second->leftNode();
|
||
|
uint secondRightNode = second->rightNode();
|
||
|
|
||
|
const SetNodeData* firstLeft = repository.itemFromIndex(firstLeftNode);
|
||
|
const SetNodeData* firstRight = repository.itemFromIndex(firstRightNode);
|
||
|
const SetNodeData* secondLeft = repository.itemFromIndex(secondLeftNode);
|
||
|
const SetNodeData* secondRight = repository.itemFromIndex(secondRightNode);
|
||
|
|
||
|
Q_ASSERT(splitPosition >= firstLeft->end() && splitPosition <= firstRight->start());
|
||
|
Q_ASSERT(splitPosition >= secondLeft->end() && splitPosition <= secondRight->start());
|
||
|
|
||
|
return createSetFromNodes( set_union(firstLeftNode, secondLeftNode, firstLeft, secondLeft, splitBit), set_union(firstRightNode, secondRightNode, firstRight, secondRight, splitBit) );
|
||
|
|
||
|
}else if(splitPosition > firstStart && splitPosition < firstEnd) {
|
||
|
|
||
|
uint firstLeftNode = first->leftNode();
|
||
|
uint firstRightNode = first->rightNode();
|
||
|
|
||
|
const SetNodeData* firstLeft = repository.itemFromIndex(firstLeftNode);
|
||
|
const SetNodeData* firstRight = repository.itemFromIndex(firstRightNode);
|
||
|
|
||
|
Q_ASSERT(splitPosition >= firstLeft->end() && splitPosition <= firstRight->start());
|
||
|
|
||
|
//splitPosition does not intersect second. That means that second is completely on one side of it.
|
||
|
//So we only need to union that side of first with second.
|
||
|
|
||
|
if(secondEnd <= splitPosition) {
|
||
|
return createSetFromNodes( set_union(firstLeftNode, secondNode, firstLeft, second, splitBit), firstRightNode, 0, firstRight );
|
||
|
}else{
|
||
|
Q_ASSERT(secondStart >= splitPosition);
|
||
|
return createSetFromNodes( firstLeftNode, set_union(firstRightNode, secondNode, firstRight, second, splitBit), firstLeft );
|
||
|
}
|
||
|
|
||
|
}else if(splitPosition > secondStart && splitPosition < secondEnd) {
|
||
|
|
||
|
uint secondLeftNode = second->leftNode();
|
||
|
uint secondRightNode = second->rightNode();
|
||
|
|
||
|
const SetNodeData* secondLeft = repository.itemFromIndex(secondLeftNode);
|
||
|
const SetNodeData* secondRight = repository.itemFromIndex(secondRightNode);
|
||
|
|
||
|
Q_ASSERT(splitPosition >= secondLeft->end() && splitPosition <= secondRight->start());
|
||
|
|
||
|
if(firstEnd <= splitPosition) {
|
||
|
return createSetFromNodes( set_union(secondLeftNode, firstNode, secondLeft, first, splitBit), secondRightNode, 0, secondRight );
|
||
|
}else{
|
||
|
Q_ASSERT(firstStart >= splitPosition);
|
||
|
return createSetFromNodes( secondLeftNode, set_union(secondRightNode, firstNode, secondRight, first, splitBit), secondLeft );
|
||
|
}
|
||
|
|
||
|
}else{
|
||
|
//We would have stopped earlier of first and second don't intersect
|
||
|
ifDebug( uint test = repository.findIndex(SetNodeDataRequest(first, repository, setRepository)); kDebug() << "found index:" << test; )
|
||
|
Q_ASSERT(0);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool SetRepositoryAlgorithms::set_equals(const SetNodeData* lhs, const SetNodeData* rhs)
|
||
|
{
|
||
|
if(lhs->leftNode() != rhs->leftNode() || lhs->rightNode() != rhs->rightNode())
|
||
|
return false;
|
||
|
else
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
uint SetRepositoryAlgorithms::set_intersect(uint firstNode, uint secondNode, const SetNodeData* first, const SetNodeData* second, uchar splitBit)
|
||
|
{
|
||
|
if(firstNode == secondNode)
|
||
|
return firstNode;
|
||
|
|
||
|
if(first->start() >= second->end())
|
||
|
return 0;
|
||
|
|
||
|
if(second->start() >= first->end())
|
||
|
return 0;
|
||
|
|
||
|
//The ranges of first and second do intersect
|
||
|
uint firstStart = first->start(), firstEnd = first->end(), secondStart = second->start(), secondEnd = second->end();
|
||
|
|
||
|
uint newStart = firstStart < secondStart ? firstStart : secondStart;
|
||
|
uint newEnd = firstEnd > secondEnd ? firstEnd : secondEnd;
|
||
|
|
||
|
//Compute the split-position for the resulting merged node
|
||
|
uint splitPosition = splitPositionForRange(newStart, newEnd, splitBit);
|
||
|
|
||
|
//Since the ranges overlap, we can be sure that either first or second contain splitPosition.
|
||
|
//The node that contains it, will also be split by it.
|
||
|
|
||
|
|
||
|
if(splitPosition > firstStart && splitPosition < firstEnd && splitPosition > secondStart && splitPosition < secondEnd) {
|
||
|
//The split-position intersect with both first and second. Continue the intersection on both sides
|
||
|
|
||
|
uint firstLeftNode = first->leftNode();
|
||
|
uint firstRightNode = first->rightNode();
|
||
|
|
||
|
uint secondLeftNode = second->leftNode();
|
||
|
uint secondRightNode = second->rightNode();
|
||
|
|
||
|
const SetNodeData* firstLeft = repository.itemFromIndex(firstLeftNode);
|
||
|
const SetNodeData* firstRight = repository.itemFromIndex(firstRightNode);
|
||
|
const SetNodeData* secondLeft = repository.itemFromIndex(secondLeftNode);
|
||
|
const SetNodeData* secondRight = repository.itemFromIndex(secondRightNode);
|
||
|
|
||
|
Q_ASSERT(splitPosition >= firstLeft->end() && splitPosition <= firstRight->start());
|
||
|
Q_ASSERT(splitPosition >= secondLeft->end() && splitPosition <= secondRight->start());
|
||
|
|
||
|
uint newLeftNode = set_intersect(firstLeftNode, secondLeftNode, firstLeft, secondLeft, splitBit);
|
||
|
uint newRightNode = set_intersect(firstRightNode, secondRightNode, firstRight, secondRight, splitBit);
|
||
|
|
||
|
if(newLeftNode && newRightNode)
|
||
|
return createSetFromNodes( newLeftNode, newRightNode );
|
||
|
else if(newLeftNode)
|
||
|
return newLeftNode;
|
||
|
else
|
||
|
return newRightNode;
|
||
|
|
||
|
}else if(splitPosition > firstStart && splitPosition < firstEnd) {
|
||
|
|
||
|
uint firstLeftNode = first->leftNode();
|
||
|
uint firstRightNode = first->rightNode();
|
||
|
|
||
|
const SetNodeData* firstLeft = repository.itemFromIndex(firstLeftNode);
|
||
|
const SetNodeData* firstRight = repository.itemFromIndex(firstRightNode);
|
||
|
|
||
|
Q_ASSERT(splitPosition >= firstLeft->end() && splitPosition <= firstRight->start());
|
||
|
|
||
|
//splitPosition does not intersect second. That means that second is completely on one side of it.
|
||
|
//So we can completely ignore the other side of first.
|
||
|
|
||
|
if(secondEnd <= splitPosition) {
|
||
|
return set_intersect(firstLeftNode, secondNode, firstLeft, second, splitBit);
|
||
|
}else{
|
||
|
Q_ASSERT(secondStart >= splitPosition);
|
||
|
return set_intersect(firstRightNode, secondNode, firstRight, second, splitBit);
|
||
|
}
|
||
|
}else if(splitPosition > secondStart && splitPosition < secondEnd) {
|
||
|
|
||
|
uint secondLeftNode = second->leftNode();
|
||
|
uint secondRightNode = second->rightNode();
|
||
|
|
||
|
const SetNodeData* secondLeft = repository.itemFromIndex(secondLeftNode);
|
||
|
const SetNodeData* secondRight = repository.itemFromIndex(secondRightNode);
|
||
|
|
||
|
Q_ASSERT(splitPosition >= secondLeft->end() && splitPosition <= secondRight->start());
|
||
|
|
||
|
if(firstEnd <= splitPosition) {
|
||
|
return set_intersect(secondLeftNode, firstNode, secondLeft, first, splitBit);
|
||
|
}else{
|
||
|
Q_ASSERT(firstStart >= splitPosition);
|
||
|
return set_intersect(secondRightNode, firstNode, secondRight, first, splitBit);
|
||
|
}
|
||
|
}else{
|
||
|
//We would have stopped earlier of first and second don't intersect
|
||
|
Q_ASSERT(0);
|
||
|
return 0;
|
||
|
}
|
||
|
Q_ASSERT(0);
|
||
|
}
|
||
|
|
||
|
bool SetRepositoryAlgorithms::set_contains(const SetNodeData* node, Index index)
|
||
|
{
|
||
|
while(true) {
|
||
|
if(node->start() > index || node->end() <= index)
|
||
|
return false;
|
||
|
|
||
|
if(node->contiguous())
|
||
|
return true;
|
||
|
|
||
|
const SetNodeData* leftNode = nodeFromIndex(node->leftNode());
|
||
|
|
||
|
if(index < leftNode->end())
|
||
|
node = leftNode;
|
||
|
else {
|
||
|
const SetNodeData* rightNode = nodeFromIndex(node->rightNode());
|
||
|
node = rightNode;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
uint SetRepositoryAlgorithms::set_subtract(uint firstNode, uint secondNode, const SetNodeData* first, const SetNodeData* second, uchar splitBit)
|
||
|
{
|
||
|
if(firstNode == secondNode)
|
||
|
return 0;
|
||
|
|
||
|
if(first->start() >= second->end() || second->start() >= first->end())
|
||
|
return firstNode;
|
||
|
|
||
|
//The ranges of first and second do intersect
|
||
|
uint firstStart = first->start(), firstEnd = first->end(), secondStart = second->start(), secondEnd = second->end();
|
||
|
|
||
|
uint newStart = firstStart < secondStart ? firstStart : secondStart;
|
||
|
uint newEnd = firstEnd > secondEnd ? firstEnd : secondEnd;
|
||
|
|
||
|
//Compute the split-position for the resulting merged node
|
||
|
uint splitPosition = splitPositionForRange(newStart, newEnd, splitBit);
|
||
|
|
||
|
//Since the ranges overlap, we can be sure that either first or second contain splitPosition.
|
||
|
//The node that contains it, will also be split by it.
|
||
|
|
||
|
if(splitPosition > firstStart && splitPosition < firstEnd && splitPosition > secondStart && splitPosition < secondEnd) {
|
||
|
//The split-position intersect with both first and second. Continue the subtract on both sides of the split-position, and merge it.
|
||
|
|
||
|
uint firstLeftNode = first->leftNode();
|
||
|
uint firstRightNode = first->rightNode();
|
||
|
|
||
|
uint secondLeftNode = second->leftNode();
|
||
|
uint secondRightNode = second->rightNode();
|
||
|
|
||
|
const SetNodeData* firstLeft = repository.itemFromIndex(firstLeftNode);
|
||
|
const SetNodeData* firstRight = repository.itemFromIndex(firstRightNode);
|
||
|
const SetNodeData* secondLeft = repository.itemFromIndex(secondLeftNode);
|
||
|
const SetNodeData* secondRight = repository.itemFromIndex(secondRightNode);
|
||
|
|
||
|
|
||
|
Q_ASSERT(splitPosition >= firstLeft->end() && splitPosition <= firstRight->start());
|
||
|
Q_ASSERT(splitPosition >= secondLeft->end() && splitPosition <= secondRight->start());
|
||
|
|
||
|
uint newLeftNode = set_subtract(firstLeftNode, secondLeftNode, firstLeft, secondLeft, splitBit);
|
||
|
uint newRightNode = set_subtract(firstRightNode, secondRightNode, firstRight, secondRight, splitBit);
|
||
|
|
||
|
if(newLeftNode && newRightNode)
|
||
|
return createSetFromNodes(newLeftNode, newRightNode);
|
||
|
else if(newLeftNode)
|
||
|
return newLeftNode;
|
||
|
else
|
||
|
return newRightNode;
|
||
|
|
||
|
}else if(splitPosition > firstStart && splitPosition < firstEnd) {
|
||
|
|
||
|
// Q_ASSERT(splitPosition >= firstLeft->end() && splitPosition <= firstRight->start());
|
||
|
|
||
|
uint firstLeftNode = first->leftNode();
|
||
|
uint firstRightNode = first->rightNode();
|
||
|
|
||
|
const SetNodeData* firstLeft = repository.itemFromIndex(firstLeftNode);
|
||
|
const SetNodeData* firstRight = repository.itemFromIndex(firstRightNode);
|
||
|
|
||
|
//splitPosition does not intersect second. That means that second is completely on one side of it.
|
||
|
//So we only need to subtract that side of first with second.
|
||
|
|
||
|
uint newLeftNode = firstLeftNode, newRightNode = firstRightNode;
|
||
|
|
||
|
if(secondEnd <= splitPosition) {
|
||
|
newLeftNode = set_subtract(firstLeftNode, secondNode, firstLeft, second, splitBit);
|
||
|
}else{
|
||
|
Q_ASSERT(secondStart >= splitPosition);
|
||
|
newRightNode = set_subtract(firstRightNode, secondNode, firstRight, second, splitBit);
|
||
|
}
|
||
|
|
||
|
if(newLeftNode && newRightNode)
|
||
|
return createSetFromNodes(newLeftNode, newRightNode);
|
||
|
else if(newLeftNode)
|
||
|
return newLeftNode;
|
||
|
else
|
||
|
return newRightNode;
|
||
|
|
||
|
}else if(splitPosition > secondStart && splitPosition < secondEnd) {
|
||
|
|
||
|
uint secondLeftNode = second->leftNode();
|
||
|
uint secondRightNode = second->rightNode();
|
||
|
|
||
|
const SetNodeData* secondLeft = repository.itemFromIndex(secondLeftNode);
|
||
|
const SetNodeData* secondRight = repository.itemFromIndex(secondRightNode);
|
||
|
|
||
|
Q_ASSERT(splitPosition >= secondLeft->end() && splitPosition <= secondRight->start());
|
||
|
|
||
|
if(firstEnd <= splitPosition) {
|
||
|
return set_subtract(firstNode, secondLeftNode, first, secondLeft, splitBit);
|
||
|
}else{
|
||
|
Q_ASSERT(firstStart >= splitPosition);
|
||
|
return set_subtract(firstNode, secondRightNode, first, secondRight, splitBit);
|
||
|
}
|
||
|
|
||
|
}else{
|
||
|
//We would have stopped earlier of first and second don't intersect
|
||
|
Q_ASSERT(0);
|
||
|
return 0;
|
||
|
}
|
||
|
Q_ASSERT(0);
|
||
|
}
|
||
|
|
||
|
Set BasicSetRepository::createSetFromIndices(const std::vector<Index>& indices) {
|
||
|
|
||
|
QMutexLocker lock(m_mutex);
|
||
|
|
||
|
if(indices.empty())
|
||
|
return Set();
|
||
|
|
||
|
SetRepositoryAlgorithms alg(dataRepository, this);
|
||
|
|
||
|
return Set(alg.setForIndices(indices.begin(), indices.end()), this);
|
||
|
}
|
||
|
|
||
|
Set BasicSetRepository::createSet(Index i) {
|
||
|
QMutexLocker lock(m_mutex);
|
||
|
SetNodeData data(i, i+1);
|
||
|
|
||
|
return Set(dataRepository.index( SetNodeDataRequest(&data, dataRepository, this) ), this);
|
||
|
}
|
||
|
|
||
|
Set BasicSetRepository::createSet(const std::set<Index>& indices) {
|
||
|
|
||
|
if(indices.empty())
|
||
|
return Set();
|
||
|
|
||
|
QMutexLocker lock(m_mutex);
|
||
|
|
||
|
std::vector<Index> indicesVector;
|
||
|
indicesVector.reserve(indices.size());
|
||
|
|
||
|
for( std::set<Index>::const_iterator it = indices.begin(); it != indices.end(); ++it )
|
||
|
indicesVector.push_back(*it);
|
||
|
|
||
|
return createSetFromIndices(indicesVector);
|
||
|
}
|
||
|
|
||
|
BasicSetRepository::BasicSetRepository(QString name, KDevelop::ItemRepositoryRegistry* registry, bool delayedDeletion) : d(new Private(name)), dataRepository(this, name, registry), m_mutex(0), m_delayedDeletion(delayedDeletion) {
|
||
|
m_mutex = dataRepository.mutex();
|
||
|
}
|
||
|
|
||
|
struct StatisticsVisitor {
|
||
|
StatisticsVisitor(const SetDataRepository& _rep) : nodeCount(0), badSplitNodeCount(0), zeroRefCountNodes(0), rep(_rep) {
|
||
|
}
|
||
|
bool operator() (const SetNodeData* item) {
|
||
|
if(item->m_refCount == 0)
|
||
|
++zeroRefCountNodes;
|
||
|
++nodeCount;
|
||
|
uint split = splitPositionForRange(item->start(), item->end());
|
||
|
if(item->hasSlaves())
|
||
|
if(split < rep.itemFromIndex(item->leftNode())->end() || split > rep.itemFromIndex(item->rightNode())->start())
|
||
|
++badSplitNodeCount;
|
||
|
return true;
|
||
|
}
|
||
|
uint nodeCount;
|
||
|
uint badSplitNodeCount;
|
||
|
uint zeroRefCountNodes;
|
||
|
const SetDataRepository& rep;
|
||
|
};
|
||
|
|
||
|
void BasicSetRepository::printStatistics() const {
|
||
|
|
||
|
StatisticsVisitor stats(dataRepository);
|
||
|
dataRepository.visitAllItems<StatisticsVisitor>(stats);
|
||
|
kDebug() << "count of nodes:" << stats.nodeCount << "count of nodes with bad split:" << stats.badSplitNodeCount << "count of nodes with zero reference-count:" << stats.zeroRefCountNodes;
|
||
|
}
|
||
|
|
||
|
BasicSetRepository::~BasicSetRepository() {
|
||
|
|
||
|
delete d;
|
||
|
}
|
||
|
|
||
|
void BasicSetRepository::itemRemovedFromSets(uint /*index*/) {
|
||
|
}
|
||
|
|
||
|
void BasicSetRepository::itemAddedToSets(uint /*index*/) {
|
||
|
}
|
||
|
|
||
|
////////////Set convenience functions//////////////////
|
||
|
|
||
|
bool Set::contains(Index index) const
|
||
|
{
|
||
|
if(!m_tree || !m_repository)
|
||
|
return false;
|
||
|
|
||
|
QMutexLocker lock(m_repository->m_mutex);
|
||
|
|
||
|
SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository);
|
||
|
return alg.set_contains(m_repository->dataRepository.itemFromIndex(m_tree), index);
|
||
|
}
|
||
|
|
||
|
Set Set::operator +(const Set& first) const
|
||
|
{
|
||
|
if(!first.m_tree)
|
||
|
return *this;
|
||
|
else if(!m_tree || !m_repository)
|
||
|
return first;
|
||
|
|
||
|
Q_ASSERT(m_repository == first.m_repository);
|
||
|
|
||
|
QMutexLocker lock(m_repository->m_mutex);
|
||
|
|
||
|
SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository);
|
||
|
|
||
|
uint retNode = alg.set_union(m_tree, first.m_tree, m_repository->dataRepository.itemFromIndex(m_tree), m_repository->dataRepository.itemFromIndex(first.m_tree));
|
||
|
|
||
|
ifDebug(alg.check(retNode));
|
||
|
|
||
|
return Set(retNode, m_repository);
|
||
|
}
|
||
|
|
||
|
Set& Set::operator +=(const Set& first) {
|
||
|
if(!first.m_tree)
|
||
|
return *this;
|
||
|
else if(!m_tree || !m_repository) {
|
||
|
m_tree = first.m_tree;
|
||
|
m_repository = first.m_repository;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
QMutexLocker lock(m_repository->m_mutex);
|
||
|
|
||
|
SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository);
|
||
|
|
||
|
m_tree = alg.set_union(m_tree, first.m_tree, m_repository->dataRepository.itemFromIndex(m_tree), m_repository->dataRepository.itemFromIndex(first.m_tree));
|
||
|
|
||
|
ifDebug(alg.check(m_tree));
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
Set Set::operator &(const Set& first) const {
|
||
|
if(!first.m_tree || !m_tree)
|
||
|
return Set();
|
||
|
|
||
|
Q_ASSERT(m_repository);
|
||
|
|
||
|
QMutexLocker lock(m_repository->m_mutex);
|
||
|
|
||
|
SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository);
|
||
|
|
||
|
Set ret( alg.set_intersect(m_tree, first.m_tree, m_repository->dataRepository.itemFromIndex(m_tree), m_repository->dataRepository.itemFromIndex(first.m_tree)), m_repository );
|
||
|
|
||
|
ifDebug(alg.check(ret.m_tree));
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
Set& Set::operator &=(const Set& first) {
|
||
|
if(!first.m_tree || !m_tree) {
|
||
|
m_tree = 0;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
Q_ASSERT(m_repository);
|
||
|
|
||
|
QMutexLocker lock(m_repository->m_mutex);
|
||
|
|
||
|
SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository);
|
||
|
|
||
|
m_tree = alg.set_intersect(m_tree, first.m_tree, m_repository->dataRepository.itemFromIndex(m_tree), m_repository->dataRepository.itemFromIndex(first.m_tree));
|
||
|
ifDebug(alg.check(m_tree));
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
Set Set::operator -(const Set& rhs) const {
|
||
|
if(!m_tree || !rhs.m_tree)
|
||
|
return *this;
|
||
|
|
||
|
Q_ASSERT(m_repository);
|
||
|
|
||
|
QMutexLocker lock(m_repository->m_mutex);
|
||
|
|
||
|
SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository);
|
||
|
|
||
|
Set ret( alg.set_subtract(m_tree, rhs.m_tree, m_repository->dataRepository.itemFromIndex(m_tree), m_repository->dataRepository.itemFromIndex(rhs.m_tree)), m_repository );
|
||
|
ifDebug( alg.check(ret.m_tree) );
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
Set& Set::operator -=(const Set& rhs) {
|
||
|
if(!m_tree || !rhs.m_tree)
|
||
|
return *this;
|
||
|
|
||
|
Q_ASSERT(m_repository);
|
||
|
|
||
|
QMutexLocker lock(m_repository->m_mutex);
|
||
|
|
||
|
SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository);
|
||
|
|
||
|
m_tree = alg.set_subtract(m_tree, rhs.m_tree, m_repository->dataRepository.itemFromIndex(m_tree), m_repository->dataRepository.itemFromIndex(rhs.m_tree));
|
||
|
|
||
|
ifDebug(alg.check(m_tree));
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
BasicSetRepository* Set::repository() const {
|
||
|
return m_repository;
|
||
|
}
|
||
|
|
||
|
void Set::staticRef() {
|
||
|
if(!m_tree)
|
||
|
return;
|
||
|
|
||
|
QMutexLocker lock(m_repository->m_mutex);
|
||
|
SetNodeData* data = m_repository->dataRepository.dynamicItemFromIndexSimple(m_tree);
|
||
|
++data->m_refCount;
|
||
|
}
|
||
|
|
||
|
///Mutex must be locked
|
||
|
void Set::unrefNode(uint current) {
|
||
|
SetNodeData* data = m_repository->dataRepository.dynamicItemFromIndexSimple(current);
|
||
|
Q_ASSERT(data->m_refCount);
|
||
|
--data->m_refCount;
|
||
|
if(!m_repository->delayedDeletion()) {
|
||
|
if(data->m_refCount == 0) {
|
||
|
|
||
|
if(data->leftNode()){
|
||
|
Q_ASSERT(data->rightNode());
|
||
|
unrefNode(data->rightNode());
|
||
|
unrefNode(data->leftNode());
|
||
|
}else {
|
||
|
//Deleting a leaf
|
||
|
Q_ASSERT(data->end() - data->start() == 1);
|
||
|
m_repository->itemRemovedFromSets(data->start());
|
||
|
}
|
||
|
|
||
|
m_repository->dataRepository.deleteItem(current);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
///Decrease the static reference-count of this set by one. This set must have a reference-count > 1.
|
||
|
///If this set reaches the reference-count zero, it will be deleted, and all sub-nodes that also reach the reference-count zero
|
||
|
///will be deleted as well. @warning Either protect ALL your sets by using reference-counting, or don't use it at all.
|
||
|
void Set::staticUnref() {
|
||
|
if(!m_tree)
|
||
|
return;
|
||
|
|
||
|
QMutexLocker lock(m_repository->m_mutex);
|
||
|
|
||
|
unrefNode(m_tree);
|
||
|
}
|
||
|
|
||
|
StringSetRepository::StringSetRepository(QString name) : Utils::BasicSetRepository(name) {
|
||
|
}
|
||
|
|
||
|
void StringSetRepository::itemRemovedFromSets(uint index) {
|
||
|
///Call the IndexedString destructor with enabled reference-counting
|
||
|
KDevelop::IndexedString string = KDevelop::IndexedString::fromIndex(index);
|
||
|
|
||
|
KDevelop::enableDUChainReferenceCounting(&string, sizeof(KDevelop::IndexedString));
|
||
|
string.~IndexedString(); //Call destructor with enabled reference-counting
|
||
|
KDevelop::disableDUChainReferenceCounting(&string);
|
||
|
}
|
||
|
|
||
|
void StringSetRepository::itemAddedToSets(uint index) {
|
||
|
///Call the IndexedString constructor with enabled reference-counting
|
||
|
|
||
|
KDevelop::IndexedString string = KDevelop::IndexedString::fromIndex(index);
|
||
|
|
||
|
char data[sizeof(KDevelop::IndexedString)];
|
||
|
|
||
|
KDevelop::enableDUChainReferenceCounting(data, sizeof(KDevelop::IndexedString));
|
||
|
new (data) KDevelop::IndexedString(string); //Call constructor with enabled reference-counting
|
||
|
KDevelop::disableDUChainReferenceCounting(data);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|