--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qtmobility/src/publishsubscribe/sharedmemorylayer.cpp Fri Apr 16 15:51:22 2010 +0300
@@ -0,0 +1,3394 @@
+/****************************************************************************
+**
+** 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 Qt Mobility Components.
+**
+** $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$
+**
+****************************************************************************/
+
+#include "qvaluespace_p.h"
+#include "qmallocpool_p.h"
+#include "qvaluespacepublisher.h"
+#include "qsystemreadwritelock_p.h"
+#include "qpacketprotocol_p.h"
+
+#include <QSet>
+#include <QCoreApplication>
+#include <QLocalSocket>
+#include <QLocalServer>
+#include <QDir>
+#include <QVariant>
+
+#include <QMutex>
+#include <QSharedMemory>
+#include <QTime>
+#include <QThread>
+
+QTM_BEGIN_NAMESPACE
+
+#define VERSION_TABLE_ENTRIES 8191
+#define ROOT_VERSION_ENTRY 0
+
+#define MAX_PATH_SIZE 16384
+#define MAX_DATA_SIZE 16384
+#define INVALID_HANDLE 0xFFFF
+
+#define ALIGN4b(n) ((n) + (((n) % 4)?(4 - ((n) % 4)):0))
+
+// #define QVALUESPACE_UPDATE_STATS
+
+static inline QDataStream& operator<<(QDataStream& stream, unsigned long v)
+{
+ if (sizeof(unsigned long) == sizeof(uint)) {
+ stream << (uint)v;
+ return stream;
+ } else {
+ stream << (quint64)v;
+ return stream;
+ }
+}
+
+static inline QDataStream& operator>>(QDataStream& stream, unsigned long& v)
+{
+ if (sizeof(unsigned long) == sizeof(uint)) {
+ uint v32;
+ stream >> v32;
+ v = (unsigned long)v32;
+ } else {
+ quint64 v64;
+ stream >> v64;
+ v = (unsigned long)v64;
+ }
+ return stream;
+}
+
+static int vsmemcmp(const char * s1, int l1, const char * s2, int l2)
+{
+ if (l1 < l2)
+ return -1;
+ if (l1 > l2)
+ return 1;
+ return ::memcmp(s1, s2, l1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// declare VersionTable
+///////////////////////////////////////////////////////////////////////////////
+struct VersionTable {
+
+ struct Entry {
+ Entry(unsigned int v, unsigned int n) : version(v), nodePtr(n) {}
+
+ unsigned int version;
+ union {
+ unsigned int nodePtr;
+ unsigned int nxtFreeBlk; // 0 means no next free block
+ // (as root is always 0)
+ };
+ };
+
+ unsigned int nxtFreeBlk;
+ unsigned int nextCreationId;
+
+ Entry entries[VERSION_TABLE_ENTRIES];
+};
+
+
+// declare NodeOwner
+/*
+ Owner of data in a node.
+
+ Overhead: 8
+ */
+struct NodeOwner {
+ unsigned long data1;
+ unsigned long data2;
+};
+
+
+// declare NodeDatum
+/*
+ Encapsulates the data and data owner associated with a Node.
+
+ Overhead: 12 + data size
+ */
+struct NodeDatum {
+ NodeOwner owner;
+ enum Type { Boolean = QVariant::Bool,
+ Int = QVariant::Int,
+ UInt = QVariant::UInt,
+ LongLong = QVariant::LongLong,
+ ULongLong = QVariant::ULongLong,
+ Double = QVariant::Double,
+ Char = QVariant::Char,
+ String = QVariant::String,
+ ByteArray = QVariant::ByteArray,
+ SerializedType /* Catch all for everything else */ };
+ //Type type;
+ unsigned short type;
+ unsigned short len;
+ char data[0];
+};
+
+// declare NodeWatch
+/*
+ Node write watcher.
+
+ Overhead: 8
+*/
+struct NodeWatch {
+ unsigned long data1;
+ unsigned long data2;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// declare Node
+///////////////////////////////////////////////////////////////////////////////
+/*
+ In memory representation of a node.
+
+ In memory, nodes are structured in one of the following three ways.
+ Virtual (No Data) Node:
+ <Node (dataCount == 0)><const char [] name>
+ Single data node:
+ <Node (dataCount == 1)><char [] name><NodeDatum><char [] data>
+ Multiple data node:
+ <Node (dataCount >= 2)><char [] name><unsigned int dataListPtr>
+ With single watch:
+ <Node (watchCount == 1)><char [] name><Rest of node...><NodeWatch>
+ With multiple watch:
+ <Node (watchCount >= 2)><char [] name><Rest of node...><unsigned int nodeWatchPtr>
+
+ Overhead: 16 bytes + name size
+ */
+struct Node {
+ unsigned short parent; /* Id for my parent */
+ unsigned int creationId; /* Unique id set when I was constructed */
+
+ unsigned short subNodes; /* Sub node count */
+ unsigned int subNodePtr; /* Ptr to malloc'd array of sub node ptrs */
+
+ unsigned short watchCount;
+ unsigned short dataCount; /* Amount of data associated with this node */
+ unsigned short nameLen; /* Length of name in bytes */
+ unsigned short dummy; /* dummy for 4 byte alignment */
+
+ char name[0]; /* My name - NO trailing null */
+
+ bool valid() {
+ return 0 != watchCount || 0 != dataCount || 0 != subNodes;
+ }
+ unsigned int dataBlockSize()
+ {
+ if(0 == dataCount) {
+ return 0;
+ } else if(1 == dataCount) {
+ NodeDatum * datum = (NodeDatum *)dataBegin();
+ return sizeof(NodeDatum) + datum->len;
+ } else if(2 >= dataCount) {
+ return sizeof(unsigned int);
+ }
+ }
+
+ unsigned int watchBlockSize()
+ {
+ if(0 == watchCount)
+ return 0;
+ else if(1 == watchCount)
+ return sizeof(NodeWatch);
+ else if(2 >= watchCount)
+ return sizeof(unsigned int);
+ return 0;
+ }
+
+ unsigned int size()
+ {
+ unsigned int rv = sizeof(Node);
+ rv += ALIGN4b(nameLen);
+ if(watchCount == 1) {
+ rv += sizeof(NodeWatch);
+ } else if(watchCount >= 2) {
+ rv += sizeof(unsigned int);
+ }
+
+ if(dataCount == 1) {
+ NodeDatum * datum = (NodeDatum *)dataBegin();
+ rv += sizeof(NodeDatum) + ALIGN4b(datum->len);
+ } else if(dataCount >= 2) {
+ rv += sizeof(unsigned int);
+ }
+
+ return rv;
+ }
+
+ void * dataBegin()
+ {
+ return (void *)(name + ALIGN4b(nameLen));
+ }
+
+ void * watchBegin()
+ {
+ if(0 == dataCount) {
+ return dataBegin();
+ } else if(1 == dataCount) {
+ NodeDatum * datum = (NodeDatum *)dataBegin();
+ return datum->data + ALIGN4b(datum->len);
+ } else {
+ return name + ALIGN4b(nameLen) + sizeof(unsigned int);
+ }
+ }
+};
+
+bool operator==(const NodeOwner &lhs, const NodeOwner &rhs)
+{
+ return 0 == ::memcmp(&lhs, &rhs, sizeof(NodeOwner));
+}
+bool operator!=(const NodeOwner &lhs, const NodeOwner &rhs)
+{
+ return 0 != ::memcmp(&lhs, &rhs, sizeof(NodeOwner));
+}
+bool operator==(const NodeWatch &lhs, const NodeWatch &rhs)
+{
+ return 0 == ::memcmp(&lhs, &rhs, sizeof(NodeWatch));
+}
+bool operator!=(const NodeWatch &lhs, const NodeWatch &rhs)
+{
+ return 0 != ::memcmp(&lhs, &rhs, sizeof(NodeWatch));
+}
+static const NodeWatch INVALID_WATCH = {0,0};
+static const NodeOwner INVALID_OWNER = {0,0};
+
+///////////////////////////////////////////////////////////////////////////////
+// declare FixedMemoryTree
+///////////////////////////////////////////////////////////////////////////////
+class FixedMemoryTree : public QObject
+{
+ Q_OBJECT
+public:
+ FixedMemoryTree(void * mem, unsigned int size, bool initMem);
+
+ // Returns the closest node to the specified path
+ unsigned short findClosest(const char * path,
+ const char ** matched);
+ unsigned short findClosest(unsigned int from,
+ const char * subPath,
+ const char ** matched);
+
+ NodeDatum * data(unsigned short node);
+
+ bool addWatch(const char * path, NodeWatch owner);
+ bool remWatch(const char * path, NodeWatch owner);
+
+ bool insert(const char * path,
+ NodeDatum::Type type,
+ const char * data,
+ unsigned int dataLen,
+ NodeOwner owner);
+
+ unsigned short offsetOfSubNode(unsigned short node,
+ unsigned short subNode);
+
+ bool remove(const char * path, NodeOwner owner);
+
+ inline Node * getNode(unsigned int);
+ inline Node * subNode(unsigned int, unsigned short);
+ inline unsigned int version(unsigned int);
+
+ inline void * fromPtr(unsigned int ptr);
+
+#ifdef QVALUESPACE_UPDATE_STATS
+ inline QMallocPool *mallocPool() const { return pool; }
+#endif
+
+ typedef void (*NodeChangeFunction)(unsigned short node, void *ctxt);
+ void setNodeChangeFunction(NodeChangeFunction, void *);
+
+private:
+ static unsigned int growListSize(unsigned int currentSize);
+ static unsigned int shrunkListSize(unsigned int currentSize,
+ unsigned int toSize);
+
+ bool setWatch(unsigned short node, NodeWatch owner);
+ bool remWatch(unsigned short node, NodeWatch owner);
+ bool setData(unsigned short node, NodeOwner owner,
+ NodeDatum::Type type, const char * data,
+ unsigned int dataLen);
+
+ void removeFrom(unsigned short from, unsigned short subNodeNumber);
+
+ unsigned short findRecur(unsigned short node, const char * path,
+ const char ** consumed);
+
+ bool insertRecur(unsigned short node, const char * path,
+ NodeDatum::Type type, const char * data,
+ unsigned int dataLen, NodeOwner owner);
+
+ bool addWatchRecur(unsigned short node, const char * path,
+ NodeWatch owner);
+
+ bool remWatchRecur(unsigned short node, const char * path,
+ NodeWatch owner);
+ bool removeRecur(unsigned short node, const char * path,
+ NodeOwner owner);
+ unsigned short removeRecur(unsigned short, NodeOwner owner);
+
+ unsigned int locateInNode(Node * node, const char * name,
+ unsigned int len, bool * match);
+
+ inline unsigned int ptr(void * mem);
+ inline VersionTable * versionTable();
+ inline void bump(unsigned short node);
+
+ unsigned short newNode(const char * name, unsigned int len,
+ NodeWatch owner);
+ unsigned short newNode(const char * name,
+ unsigned int len,
+ NodeOwner owner = INVALID_OWNER,
+ NodeDatum::Type type = (NodeDatum::Type)0,
+ const char * data = 0,
+ unsigned int dataLen = 0);
+
+ inline Node * node(unsigned int);
+
+ char * poolMem;
+ QMallocPool * pool;
+ bool changed;
+ NodeChangeFunction changeFunc;
+ void * changeFuncContext;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// define FixedMemoryTree
+///////////////////////////////////////////////////////////////////////////////
+FixedMemoryTree::FixedMemoryTree(void * mem, unsigned int size,
+ bool initMemory )
+: poolMem((char *)mem), pool(0), changed(false), changeFunc(0)
+{
+ Q_ASSERT(size);
+ Q_ASSERT(poolMem);
+
+ if(initMemory) {
+ /* Initialize layer version table - would be worth randomizing the
+ nextFreeBlk chain to distribute entries across the 128-bit change
+ notification. */
+ ::memset(poolMem,0, sizeof(VersionTable));
+ for(int ii = 0; ii < VERSION_TABLE_ENTRIES; ++ii)
+ versionTable()->entries[ii].nxtFreeBlk =
+ (ii + 1) % VERSION_TABLE_ENTRIES;
+
+ /* Create malloc pool on the non-version table portion of the layer */
+ pool =
+ new QMallocPool(poolMem + sizeof(VersionTable),
+ size - sizeof(VersionTable),
+ QMallocPool::Owned, QLatin1String("FixedMemoryTree"));
+
+ /* Create root node and fixup free block pointer */
+ versionTable()->nxtFreeBlk =
+ versionTable()->entries[ROOT_VERSION_ENTRY].nxtFreeBlk;
+ Node * root = (Node *)pool->malloc(sizeof(Node));
+ ::memset(root,0, sizeof(Node));
+ root->creationId = versionTable()->nextCreationId++;
+ root->parent = INVALID_HANDLE;
+ Q_ASSERT((char *)root > poolMem);
+ versionTable()->entries[ROOT_VERSION_ENTRY] =
+ VersionTable::Entry(1, ptr(root));
+ }
+}
+
+unsigned short FixedMemoryTree::findClosest(const char * path,
+ const char ** matched)
+{
+ Q_ASSERT(path);
+ Q_ASSERT(matched);
+ Q_ASSERT(path[0] == '/');
+
+ ++path; // Skip initial '/'
+
+ if('\0' == *path)
+ return ROOT_VERSION_ENTRY;
+ else
+ return findRecur(ROOT_VERSION_ENTRY, path, matched);
+}
+
+unsigned short FixedMemoryTree::findClosest(unsigned int from,
+ const char * subPath,
+ const char ** matched)
+{
+ Q_ASSERT(subPath);
+ Q_ASSERT(matched);
+ Q_ASSERT(subPath[0] == '/');
+
+ ++subPath; // Skip initial '/'
+ if('\0' == *subPath)
+ return from;
+ else
+ return findRecur(from, subPath, matched);
+}
+
+/*!
+ Returns the default datum for \a node, or NULL if one doesn't exist.
+ */
+NodeDatum * FixedMemoryTree::data(unsigned short node)
+{
+ Node * n = this->node(node);
+ if(0 == n->dataCount) {
+ return 0;
+ } else if(1 == n->dataCount) {
+ return (NodeDatum *)n->dataBegin();
+ } else {
+ unsigned int * valueList =
+ (unsigned int *)fromPtr(*(unsigned int *)n->dataBegin());
+ return (NodeDatum *)fromPtr(*valueList);
+ }
+}
+
+/*!
+ Inserts a watch for \a owner at the specified \a path
+*/
+bool FixedMemoryTree::addWatch(const char * path, NodeWatch owner)
+{
+ Q_ASSERT(path);
+ Q_ASSERT(path[0] == '/');
+ Q_ASSERT(INVALID_WATCH != owner);
+
+ ++path; // Skip initial '/'
+
+ if(*path == '\0')
+ return setWatch(ROOT_VERSION_ENTRY, owner);
+ else
+ return addWatchRecur(ROOT_VERSION_ENTRY, path, owner);
+}
+
+/*!
+ Removes an existing watch for \a owner from the specified \a path
+*/
+bool FixedMemoryTree::remWatch(const char * path, NodeWatch owner)
+{
+ Q_ASSERT(path);
+ Q_ASSERT(path[0] == '/');
+ Q_ASSERT(INVALID_WATCH != owner);
+
+ ++path; // Skip initial '/'
+
+ if(*path == '\0')
+ return remWatch((unsigned short)ROOT_VERSION_ENTRY, owner);
+ else
+ return remWatchRecur(ROOT_VERSION_ENTRY, path, owner);
+}
+
+/*!
+ Inserts \a data of length \a dataLen at \a path with the set \a owner.
+ Returns true if the tree has visibly changed.
+ */
+bool FixedMemoryTree::insert(const char * path, NodeDatum::Type type,
+ const char * data, unsigned int dataLen,
+ NodeOwner owner)
+{
+ Q_ASSERT(path);
+ Q_ASSERT(path[0] == '/');
+ Q_ASSERT(data || !dataLen);
+ Q_ASSERT(INVALID_OWNER != owner);
+
+ if(dataLen > MAX_DATA_SIZE)
+ return false;
+
+ ++path; // Skip initial '/'
+
+ // Special root case - no one can insert
+ if(*path == '\0') return false;
+
+ return insertRecur(ROOT_VERSION_ENTRY, path, type, data, dataLen, owner);
+}
+
+/*!
+ Returns the linear offset of \a subNode within \a node. Asserts if not
+ found.
+ */
+unsigned short FixedMemoryTree::offsetOfSubNode(unsigned short node,
+ unsigned short subNode)
+{
+ Node * me = getNode(node);
+ if(!me)
+ return INVALID_HANDLE;
+
+ unsigned short * const subNodes =
+ (unsigned short *)fromPtr(me->subNodePtr);
+ for(unsigned short ii = 0; ii < me->subNodes; ++ii) {
+ if(subNodes[ii] == subNode)
+ return ii;
+ }
+
+ qFatal("SharedMemoryLayer: Unable to find sub-node %x of %x.", subNode, node);
+ return 0;
+}
+
+bool FixedMemoryTree::remove(const char * path, NodeOwner owner)
+{
+ Q_ASSERT(path);
+ Q_ASSERT(path[0] == '/');
+ Q_ASSERT(INVALID_OWNER != owner);
+
+ ++path; // Skip initial '/'
+
+ if(*path == '\0')
+ return INVALID_HANDLE != removeRecur(ROOT_VERSION_ENTRY, owner);
+ else
+ return removeRecur(ROOT_VERSION_ENTRY, path, owner);
+}
+
+/*!
+ Given a list of size \a currentSize that needs another element, returns a
+ new size designated by the following growth strategy:
+
+ \list
+ \i Initially (currentSize == 0) allocate a list of one entry.
+ \i Up to 64 entries, double the size each allocation.
+ \i From 64 onwards, grow in chunks of 64
+ \endlist
+ */
+unsigned int FixedMemoryTree::growListSize(unsigned int currentSize)
+{
+ if(0 == currentSize)
+ return 1;
+ if(currentSize <= 32)
+ return currentSize * 2;
+ return currentSize + 64;
+}
+
+/*!
+ Given that we have an allocated list of size \a currentSize, that needs to
+ contain contentSize elements, returns an ideal list size designated by the
+ following shrink strategy:
+
+ \list
+ \i For current size > 64, reduce in blocks of 64
+ \i For currentSize < 64, reduce by half if possible
+ \i Otherwise, remain the same
+ \endlist
+ */
+unsigned int FixedMemoryTree::shrunkListSize(unsigned int currentSize,
+ unsigned int contentSize)
+{
+ if(currentSize > 64) {
+ return currentSize - 64 * ((currentSize - contentSize) / 64);
+ } else if(contentSize < (currentSize / 2)) {
+ return currentSize / 2;
+ }
+
+ return currentSize;
+}
+
+/*!
+ Attempts to locate the sub node \a name (of length \a len) in \a node. If
+ found, returns the offset and sets \a *match to true. If not found,
+ sets \a *match to false and returns the offset where the node would be
+ found, should it exist. Moving all nodes up one notch and inserting the
+ new node at the returned offset maintains order.
+ */
+unsigned int FixedMemoryTree::locateInNode(Node * node, const char * name,
+ unsigned int len, bool * match)
+{
+ Q_ASSERT(node && name && match);
+
+ // This is a linear search. It would be possible to replace this with a
+ // binary search, which may or may not improve performance depending on the
+ // number of sub nodes.
+ unsigned short counter = 0;
+ unsigned short * subNodes = (unsigned short *)fromPtr(node->subNodePtr);
+
+ *match = false;
+ for(counter = 0; counter < node->subNodes; ++counter) {
+ Node * currentNode = this->node(subNodes[counter]);
+
+ int memcmprv = vsmemcmp(name, len, currentNode->name,
+ currentNode->nameLen);
+ if(memcmprv < 0) {
+ continue;
+ } else if(memcmprv == 0) {
+ *match = true;
+ break;
+ } else if(memcmprv > 0) {
+ break;
+ }
+ }
+
+ return counter;
+}
+
+/*!
+ \internal
+ Recursive implementation of FixedMemoryTree::findClosest
+ */
+unsigned short FixedMemoryTree::findRecur(unsigned short node,
+ const char * path,
+ const char ** consumed)
+{
+ // Locate end of section
+ const char * endOfSection = path;
+ while(*endOfSection != '/' && *endOfSection != '\0') ++endOfSection;
+ const unsigned int sectionLen = (endOfSection - path);
+
+ bool match;
+ Node * const me = this->node(node);
+ const unsigned int matchId = locateInNode(me, path, sectionLen, &match);
+ Q_ASSERT((match && matchId < me->subNodes) ||
+ (!match && matchId <= me->subNodes));
+
+ if(!match) {
+ *consumed = path - 1;
+ return node;
+ }
+
+ unsigned short * const subNodes =
+ (unsigned short *)fromPtr(me->subNodePtr);
+ if('\0' == *endOfSection) {
+ *consumed = NULL;
+ return subNodes[matchId];
+ }
+
+ ++endOfSection;
+ return findRecur(subNodes[matchId], endOfSection, consumed);
+}
+
+/*! Remove all nodes under node that match owner (or no-owner with no
+ dependents). Returns the last node removed, which will be \a node if
+ it was removed, or INVALID_HANDLE if no nodes were removed. This can
+ be used by the parent to determine whether to increment the version
+ number and whether to remove the sub node.
+ */
+unsigned short FixedMemoryTree::removeRecur(unsigned short node,
+ NodeOwner owner)
+{
+ Node * me = this->node(node);
+
+ unsigned short * const subNodes =
+ (unsigned short*)fromPtr(me->subNodePtr);
+
+ unsigned short removedAny = INVALID_HANDLE;
+ unsigned int removedSubNodes = 0;
+
+ for(unsigned short ii = 0; ii < me->subNodes; ++ii) {
+
+ unsigned short id = subNodes[ii];
+ // Blank recur
+ unsigned short removedId = removeRecur(id, owner);
+
+ if(removedId != INVALID_HANDLE)
+ removedAny = removedId;
+
+ if(removedId == id) {
+ // This node was removed
+ ++removedSubNodes;
+ subNodes[ii] = INVALID_HANDLE;
+ }
+
+ }
+
+ // If removed any sub nodes we need to collapse our subnode list
+ if(removedSubNodes == me->subNodes) {
+ // All removed!
+ pool->free(subNodes);
+ me->subNodes = 0;
+ me->subNodePtr = 0;
+ } else if(removedSubNodes) {
+ // Some removed
+ unsigned int fillUpto = 0;
+
+ for(unsigned int sourceUpto = 0;
+ sourceUpto < me->subNodes;
+ ++sourceUpto) {
+
+ if(INVALID_HANDLE != subNodes[sourceUpto]) {
+ subNodes[fillUpto] = subNodes[sourceUpto];
+ ++fillUpto;
+ }
+ }
+
+ me->subNodes = fillUpto;
+ }
+
+ // Now to remove data and possibly me as necessary
+ if(1 == me->dataCount) {
+
+ NodeDatum * datum = (NodeDatum *)me->dataBegin();
+ if(datum->owner == owner ||
+ (owner.data2 = 0xFFFFFFFF && owner.data1 == datum->owner.data1))
+ me->dataCount = 0;
+
+ } else if(me->dataCount > 1) {
+
+ // Data list time - sigh
+ unsigned int * list =
+ (unsigned int *)fromPtr(*((unsigned int *)me->dataBegin()));
+ unsigned int removed = 0;
+ for(int ii = 0; ii < me->dataCount; ++ii) {
+ NodeDatum * datum = (NodeDatum *)fromPtr(list[ii]);
+ if(datum->owner == owner ||
+ (owner.data2 == 0xFFFFFFFF &&
+ owner.data1 == datum->owner.data1)) {
+ // Remove this datum
+ ++removed;
+ pool->free(datum);
+ list[ii] = 0xFFFFFFFF;
+ }
+ }
+
+ if(removed) {
+ // Three cases: Only one left in list - move into node
+ // All removed - remove list
+ // Some removed - shorten list if necessary
+ if((removed + 1)== me->dataCount) {
+ // Find remaining node
+ bool found = false;
+ for(int ii = 0; !found && ii < me->dataCount; ++ii) {
+ if(0xFFFFFFFF != list[ii]) {
+ NodeDatum * datum = (NodeDatum *)fromPtr(list[ii]);
+ me->dataCount = 0;
+ setData(node, datum->owner,
+ (NodeDatum::Type)datum->type,
+ datum->data, datum->len);
+ me = this->node(node);
+ found = true;
+ }
+ }
+ Q_ASSERT(found);
+ pool->free(list);
+ } else if(removed == me->dataCount) {
+ // Easy!
+ me->dataCount = 0;
+ pool->free(list);
+ } else {
+ // Some removed
+ unsigned int * newList = list;
+ unsigned int allocatedListSize = pool->size_of(list) /
+ sizeof(unsigned int);
+ unsigned int newListSize = shrunkListSize(allocatedListSize,
+ me->dataCount - removed);
+ if(newListSize != allocatedListSize) {
+ newList = (unsigned int *)pool->malloc(newListSize *
+ sizeof(unsigned int));
+ } else {
+ newList = list;
+ }
+
+ unsigned int newListCntr = 0;
+ for(int ii = 0; ii < me->dataCount; ++ii) {
+ if(list[ii] != 0xFFFFFFFF)
+ newList[newListCntr++] = list[ii];
+ }
+
+ me->dataCount = me->dataCount - removed;
+ if(list != newList) {
+ pool->free(list);
+ *((unsigned int *)me->dataBegin()) = ptr(newList);
+ }
+ }
+ }
+ }
+
+ // Attempt watch cleanup
+ if(1 == me->watchCount) {
+ NodeWatch * watch = (NodeWatch *)me->watchBegin();
+ if(watch->data1 == owner.data1 &&
+ (owner.data2 == 0xFFFFFFFF || watch->data2 == owner.data2))
+ me->watchCount = 0;
+
+ } else if(me->watchCount > 1) {
+ // Watch list time...
+ NodeWatch * watch =
+ (NodeWatch *)fromPtr(*(unsigned int *)me->watchBegin());
+
+ unsigned int lastSpot = 0;
+ unsigned int watchers = 0;
+ for(unsigned int ii = 0; ii < me->watchCount; ++ii) {
+ if(watch[ii].data1 == owner.data1 &&
+ (owner.data2 == 0xFFFFFFFF || watch[ii].data2 == owner.data2)) {
+ // Do nothing
+ } else {
+ ++watchers;
+ if(ii != lastSpot)
+ watch[lastSpot] = watch[ii];
+ ++lastSpot;
+ }
+ }
+
+ if(watchers == me->watchCount) {
+ // Nothing to do
+ } else if(0 == watchers) {
+ // Remove list and set watchCount appropriately
+ pool->free(watch);
+ me->watchCount = 0;
+ } else if(1 == watchers) {
+ // Set in main node
+ me->watchCount = 0;
+ setWatch(node, watch[0]);
+ pool->free(watch);
+ Q_ASSERT(1 == me->watchCount);
+ } else {
+ // Check if we need to shrink the list
+ unsigned int allocatedLength = pool->size_of(watch) / sizeof(NodeWatch);
+ unsigned int newLength = shrunkListSize(allocatedLength, watchers);
+ if(newLength != allocatedLength) {
+ NodeWatch * newList =
+ (NodeWatch *)pool->malloc(sizeof(NodeWatch) * newLength);
+ ::memcpy(newList, watch, sizeof(NodeWatch) * watchers);
+ *(unsigned int *)me->watchBegin() = ptr(newList);
+ pool->free(watch);
+ }
+ me->watchCount = watchers;
+ }
+ }
+
+ if(!me->valid() && node != ROOT_VERSION_ENTRY) {
+ // Remove me :(
+ VersionTable::Entry * const entry =
+ &(versionTable()->entries[node]);
+ entry->nxtFreeBlk = versionTable()->nxtFreeBlk;
+ versionTable()->nxtFreeBlk = node;
+ pool->free(me);
+ bump(node);
+ return node;
+ } else if(removedAny != INVALID_HANDLE) {
+ bump(node);
+ }
+
+ return removedAny;
+}
+
+/*!
+ Removes sub node number \a subNodeNumber from \a from's sub node list. Does
+ bump \a from's version number. Does not touch removed sub node.
+ */
+void FixedMemoryTree::removeFrom(unsigned short from,
+ unsigned short subNodeNumber)
+{
+ Node * const me = node(from);
+
+ Q_ASSERT(subNodeNumber < me->subNodes);
+
+ unsigned short * const subNodes =
+ (unsigned short *)fromPtr(me->subNodePtr);
+
+ unsigned int currentSize = pool->size_of(subNodes) / sizeof(unsigned short);
+ unsigned int scaleSize = shrunkListSize(currentSize, me->subNodes - 1);
+ if(scaleSize != currentSize) {
+ // Resize
+ unsigned short * newSubNodes =
+ (unsigned short *)pool->malloc(scaleSize * sizeof(unsigned short));
+ ::memcpy(newSubNodes, subNodes, subNodeNumber * sizeof(unsigned short));
+ ::memcpy(newSubNodes + subNodeNumber, subNodes + subNodeNumber + 1,
+ (me->subNodes - subNodeNumber - 1) * sizeof(unsigned short));
+ me->subNodePtr = ptr(newSubNodes);
+ pool->free(subNodes);
+ } else {
+ // Do not resize
+ ::memmove(subNodes + subNodeNumber, subNodes + subNodeNumber + 1,
+ (me->subNodes - subNodeNumber - 1) * sizeof(unsigned short));
+ }
+ --me->subNodes;
+
+ // Bump version number
+ bump(from);
+}
+
+bool FixedMemoryTree::remWatchRecur(unsigned short node, const char * path,
+ NodeWatch owner)
+{
+ // Locate end of section
+ const char * endOfSection = path;
+ while(*endOfSection != '/' && *endOfSection != '\0') ++endOfSection;
+ unsigned int sectionLen = (endOfSection - path);
+
+ Node * const me = this->node(node);
+ bool match;
+ unsigned int matchId = locateInNode(me, path, sectionLen, &match);
+ Q_ASSERT((match && matchId < me->subNodes) ||
+ (!match && matchId <= me->subNodes));
+
+ if(!match)
+ return false;
+
+ unsigned short * const subNodes =
+ (unsigned short *)fromPtr(me->subNodePtr);
+
+ if('\0' == *endOfSection) {
+ unsigned int id = subNodes[matchId];
+
+ // We must remove this node
+ if(remWatch((unsigned short)id, owner)) {
+ bump(node);
+ Node * them = this->node(id);
+ if(!them->valid()) {
+ // Remove from node list
+ removeFrom(node, matchId);
+ return true;
+ }
+ return false;
+ }
+ return false;
+ } else {
+ ++endOfSection;
+ bool rv = remWatchRecur((unsigned short)subNodes[matchId], endOfSection, owner);
+
+ if(rv) {
+ bump(node);
+ Node * const subNode = this->node(subNodes[matchId]);
+ if(!subNode->valid()) {
+ // Remove this one too
+ if(subNode->subNodePtr)
+ pool->free(fromPtr(subNode->subNodePtr));
+ pool->free(subNode);
+ removeFrom(node, matchId);
+ }
+ }
+ return rv;
+ }
+}
+
+bool FixedMemoryTree::removeRecur(unsigned short node, const char * path,
+ NodeOwner owner)
+{
+ // Locate end of section
+ const char * endOfSection = path;
+ while(*endOfSection != '/' && *endOfSection != '\0') ++endOfSection;
+ unsigned int sectionLen = (endOfSection - path);
+
+ Node * const me = this->node(node);
+ bool match;
+ unsigned int matchId = locateInNode(me, path, sectionLen, &match);
+ Q_ASSERT((match && matchId < me->subNodes) ||
+ (!match && matchId <= me->subNodes));
+
+ if(!match)
+ return false;
+
+ unsigned short * const subNodes =
+ (unsigned short *)fromPtr(me->subNodePtr);
+
+ if('\0' == *endOfSection) {
+
+ unsigned int id = subNodes[matchId];
+
+ // We must remove this node
+ unsigned short removeId = removeRecur(id, owner);
+
+ if(removeId == id) {
+ // Remove from node list
+ removeFrom(node, matchId);
+ // Bump my version
+ bump(node);
+ return true;
+ } else if(INVALID_HANDLE != removeId) {
+ // Bump my version
+ bump(node);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ ++endOfSection;
+ bool rv = removeRecur(subNodes[matchId], endOfSection, owner);
+ if(rv) {
+ bump(node);
+ Node * const subNode = this->node(subNodes[matchId]);
+ if(!subNode->valid()) {
+ // Remove this one too
+ if(subNode->subNodePtr)
+ pool->free(fromPtr(subNode->subNodePtr));
+ pool->free(subNode);
+ removeFrom(node, matchId);
+ }
+ }
+ return rv;
+ }
+}
+
+/*!
+ Removes \a owner as a watch on \a node. Returns true if owner was removed,
+ false if owner was not a watch.
+ */
+bool FixedMemoryTree::remWatch(unsigned short node, NodeWatch owner)
+{
+ Node * me = this->node(node);
+
+ if(0 == me->watchCount) {
+ return false;
+ } else if(1 == me->watchCount) {
+ NodeWatch * watch = (NodeWatch *)me->watchBegin();
+ if(*watch == owner) {
+ me->watchCount = 0;
+ return true;
+ } else {
+ return false;
+ }
+ } else if(me->watchCount == 2) {
+ NodeWatch * list = (NodeWatch *)fromPtr(*(unsigned int *)me->watchBegin());
+ for(int ii = 0; ii < me->watchCount; ++ii) {
+ if(list[ii] == owner) {
+ // Found!
+ int remaining = (ii = 0)?1:0;
+ me->watchCount = 0;
+ setWatch(node, list[remaining]);
+ pool->free(list);
+ return true;
+ }
+ }
+ return false;
+ } else if(me->watchCount > 2) {
+ NodeWatch * list = (NodeWatch *)fromPtr(*(unsigned int *)me->watchBegin());
+ for(unsigned short ii = 0; ii < me->watchCount; ++ii) {
+ if(list[ii] == owner) {
+ // Found!
+ unsigned int currentListSize =
+ pool->size_of(list) / sizeof(NodeWatch);
+ unsigned int newListSize =
+ shrunkListSize(currentListSize, me->watchCount - 1);
+ if(currentListSize != newListSize) {
+ // Create a new list
+ NodeWatch *newList = (NodeWatch *)pool->malloc(newListSize * sizeof(NodeWatch));
+ ::memcpy(newList, list, ii * sizeof(NodeWatch));
+ ::memcpy(newList + ii, list + ii + 1,
+ (me->watchCount - 1 - ii) * sizeof(NodeWatch));
+ *(unsigned int *)me->watchBegin() = ptr(newList);
+ pool->free(list);
+ } else {
+ // Simply left shift everything
+ ::memmove(list + ii, list + ii + 1,
+ (me->watchCount - 1 - ii) * sizeof(NodeWatch));
+ }
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/*!
+ Inserts \a owner as a watch on \a node.
+ */
+bool FixedMemoryTree::setWatch(unsigned short node, NodeWatch owner)
+{
+ Node * me = this->node(node);
+
+ /*
+ if 0 == node->watchCount
+ add to end of node
+ else if 1 == node->watchCount
+ move existing watch into new watch list and append to list
+ else
+ append to existing watch list
+ */
+
+ if(0 == me->watchCount) {
+ unsigned int spareLen = pool->size_of(me) - me->size();
+
+ if(spareLen < sizeof(NodeWatch)) {
+ // Need a new node
+ Node * newNode =
+ (Node *)pool->malloc(me->size() + sizeof(NodeWatch));
+ ::memcpy(newNode, me, me->size());
+ versionTable()->entries[node].nodePtr = ptr(newNode);
+ pool->free(me);
+ me = newNode;
+ }
+ me->watchCount = 1;
+ *(NodeWatch *)(me->watchBegin()) = owner;
+ } else if(1 == me->watchCount) {
+ // Need to create a list
+ NodeWatch * list = (NodeWatch *)pool->malloc(2 * sizeof(NodeWatch));
+ list[0] = *(NodeWatch *)(me->watchBegin());
+ list[1] = owner;
+ *(unsigned int *)(me->watchBegin()) = ptr(list);
+ ++me->watchCount;
+ } else {
+ // Append to list
+ NodeWatch * list = (NodeWatch *)fromPtr(*(unsigned int *)me->watchBegin());
+ int availableSize = pool->size_of(list) / sizeof(NodeWatch);
+ if(availableSize < me->watchCount + 1) {
+ // Reallocate
+ NodeWatch * newList =
+ (NodeWatch *)pool->malloc(growListSize(availableSize) *
+ sizeof(NodeWatch));
+ ::memcpy(newList, list, me->watchCount * sizeof(NodeWatch));
+ pool->free(list);
+ list = newList;
+ *(unsigned int *)me->watchBegin() = ptr(newList);
+ }
+ list[me->watchCount] = owner;
+ ++me->watchCount;
+ }
+ return true;
+}
+
+/*!
+ Inserts \a data of length \a dataLen into \a node. Returns true if the
+ version number of \a node has increased to handle this set.
+ */
+bool FixedMemoryTree::setData(unsigned short node,
+ NodeOwner owner,
+ NodeDatum::Type type,
+ const char * data,
+ unsigned int dataLen)
+{
+ Q_ASSERT(!dataLen || data);
+
+ Node * me = this->node(node);
+
+ /*
+ if 0 == node->dataCount
+ insert directly into node
+ else if 1 == node->dataCount && node->dataOwner == owner
+ insert directly into node
+ else if 1 == node->dataCount
+ move existing data into new data list and append new data to list
+ else
+ locate or append data to existing data list
+ */
+
+ if (0 == me->dataCount ||
+ (1 == me->dataCount && ((NodeDatum *)me->dataBegin())->owner == owner)) {
+ unsigned int allocatedDataLen =
+ pool->size_of(me) -
+ sizeof(Node) -
+ me->watchBlockSize() -
+ ALIGN4b(me->nameLen);
+
+ if(allocatedDataLen > sizeof(NodeDatum))
+ allocatedDataLen -= sizeof(NodeDatum);
+ else
+ allocatedDataLen = 0;
+
+ if(dataLen <= allocatedDataLen) {
+ // Sweet, reuse existing
+ NodeDatum * datum = (NodeDatum *)me->dataBegin();
+ datum->owner = owner;
+ datum->type = type;
+ if(me->watchBlockSize())
+ ::memcpy(datum->data + ALIGN4b(dataLen), me->watchBegin(),
+ me->watchBlockSize());
+ datum->len = dataLen;
+ ::memcpy(datum->data, data, dataLen);
+ me->dataCount = 1;
+ } else {
+ // Need new node
+ Node * newNode =
+ (Node *)pool->malloc(sizeof(Node) + ALIGN4b(me->nameLen) +
+ sizeof(NodeDatum) + ALIGN4b(dataLen) +
+ me->watchBlockSize());
+ ::memcpy(newNode, me, sizeof(Node) + ALIGN4b(me->nameLen));
+ versionTable()->entries[node].nodePtr = ptr(newNode);
+
+ NodeDatum * datum = (NodeDatum *)newNode->dataBegin();
+ datum->owner = owner;
+ datum->type = type;
+ if(me->watchBlockSize())
+ ::memcpy(datum->data + ALIGN4b(dataLen), me->watchBegin(),
+ me->watchBlockSize());
+ datum->len = dataLen;
+ ::memcpy(datum->data, data, dataLen);
+ me->dataCount = 1;
+ pool->free(me);
+ me = newNode;
+ }
+
+ bump(node);
+
+ return true;
+ } else if(1 == me->dataCount) {
+ // Damn, need to create a value chain list
+ NodeDatum * existingDatum = (NodeDatum *)me->dataBegin();
+ unsigned int * list =
+ (unsigned int *)pool->malloc(2 * sizeof(unsigned int));
+ NodeDatum * currentDatum =
+ (NodeDatum *)pool->malloc(sizeof(NodeDatum) + existingDatum->len);
+ NodeDatum * newDatum =
+ (NodeDatum *)pool->malloc(sizeof(NodeDatum) + dataLen);
+ list[0] = ptr(currentDatum);
+ list[1] = ptr(newDatum);
+
+ // Copy existing data
+ ::memcpy(currentDatum, existingDatum,
+ sizeof(NodeDatum) + existingDatum->len);
+ // Copy new data
+ newDatum->owner = owner;
+ newDatum->type = type;
+ newDatum->len = dataLen;
+ ::memcpy(newDatum->data, data, dataLen);
+ if(me->watchBlockSize())
+ ::memcpy(((unsigned int *)me->dataBegin()) + 1, me->watchBegin(),
+ me->watchBlockSize());
+ // Add value chain list to node
+ *(unsigned int *)me->dataBegin() = ptr(list);
+ me->dataCount = 2;
+ return false;
+ } else {
+ // Attempt to locate in existing chain
+ unsigned int * list =
+ (unsigned int *)fromPtr(*(unsigned int *)me->dataBegin());
+ for(int ii = 0; ii < me->dataCount; ++ii) {
+ NodeDatum * ndata = (NodeDatum *)fromPtr(list[ii]);
+ if(ndata->owner == owner) {
+ // Found - but is it big enough?
+ unsigned int allocatedDataLen =
+ pool->size_of(ndata) - sizeof(NodeDatum);
+ if(allocatedDataLen >= dataLen) {
+ // Copy over the top
+ ::memcpy(ndata->data, data, dataLen);
+ ndata->len = dataLen;
+ ndata->type = type;
+ } else {
+ // Reallocate
+ NodeDatum * newData =
+ (NodeDatum *)pool->malloc(sizeof(NodeDatum) +
+ dataLen);
+ newData->owner = owner;
+ newData->type = type;
+ newData->len = dataLen;
+ ::memcpy(newData->data, data, dataLen);
+ list[ii] = ptr(newData);
+ pool->free(ndata);
+ }
+ return ii == 0;
+ }
+ }
+
+ // Not found - append
+ unsigned int allocatedListLen =
+ pool->size_of(list) / sizeof(unsigned int);
+ if(allocatedListLen >= (unsigned int)me->dataCount + 1) {
+ // We fit!
+ } else {
+ // We don't fit
+ unsigned int * newList =
+ (unsigned int *)pool->malloc(growListSize(allocatedListLen) *
+ sizeof(unsigned int));
+ ::memcpy(newList, list, allocatedListLen * sizeof(unsigned int));
+ pool->free(list);
+ list = newList;
+ *(unsigned int *)me->dataBegin() = ptr(newList);
+ }
+
+ NodeDatum * datum = (NodeDatum *)pool->malloc(sizeof(NodeDatum) +
+ dataLen);
+ datum->owner = owner;
+ datum->len = dataLen;
+ datum->type = type;
+ ::memcpy(datum->data, data, dataLen);
+ list[me->dataCount] = ptr(datum);
+ ++me->dataCount;
+
+ return false;
+ }
+}
+
+bool FixedMemoryTree::addWatchRecur(unsigned short node, const char * path,
+ NodeWatch owner)
+{
+ // Locate end of section
+ const char * endOfSection = path;
+ while(*endOfSection != '/' && *endOfSection != '\0') ++endOfSection;
+ const unsigned int sectionLen = (endOfSection - path);
+
+ // We operate on structures, not numbers
+ Node * const me = this->node(node);
+ unsigned short * subNodes =
+ me->subNodePtr?(unsigned short *)fromPtr(me->subNodePtr):0;
+
+ // Attempt to locate the sub node within the current node
+ bool match;
+ const unsigned int matchId = locateInNode(me, path, sectionLen, &match);
+ Q_ASSERT((match && matchId < me->subNodes) ||
+ (!match && matchId <= me->subNodes));
+
+ if(match) {
+
+ // There is a sub node of the correct name
+ const unsigned short id = subNodes[matchId];
+
+ if('\0' == *endOfSection) {
+ // Update the node. Bumps *their* version if necessary.
+ bool rv = setWatch(id, owner);
+ if(rv) bump(node);
+ return rv;
+ }
+ ++endOfSection;
+
+ bool rv = addWatchRecur(id, endOfSection, owner);
+ if(rv) bump(node);
+ return rv;
+
+ } else { /* !match */
+
+ // We *may* need to lengthen our list
+ unsigned short maxSubNodesLen = subNodes?
+ pool->size_of(subNodes) / sizeof(unsigned short):0;
+ unsigned int newSubNode = 0;
+
+ if('\0' == *endOfSection)
+ newSubNode = newNode(path, sectionLen, owner);
+ else
+ newSubNode = newNode(path, sectionLen);
+
+ if (newSubNode == ROOT_VERSION_ENTRY)
+ return false;
+
+ Node * const newNode = this->node(newSubNode);
+ newNode->parent = node;
+
+ if(maxSubNodesLen < me->subNodes + 1) {
+ // Grow list
+ unsigned short * newSubNodes =
+ (unsigned short *)pool->malloc(growListSize(me->subNodes) *
+ sizeof(unsigned short));
+ ::memcpy(newSubNodes, subNodes, matchId * sizeof(unsigned short));
+ ::memcpy(newSubNodes + matchId + 1, subNodes + matchId,
+ (me->subNodes - matchId) * sizeof(unsigned short));
+ pool->free(fromPtr(me->subNodePtr));
+ me->subNodePtr = ptr(newSubNodes);
+ subNodes = newSubNodes;
+ } else {
+ // Split list
+ ::memmove(subNodes + matchId + 1, subNodes + matchId,
+ (me->subNodes - matchId) * sizeof(unsigned short));
+ }
+ subNodes[matchId] = newSubNode;
+ ++me->subNodes;
+
+ // Bump my version
+ bump(node);
+
+ if('\0' != *endOfSection) {
+ ++endOfSection;
+ addWatchRecur(newSubNode, endOfSection, owner);
+ }
+ return true;
+ }
+}
+
+/*!
+ Returns true if the insert should force a version bump.
+ */
+bool FixedMemoryTree::insertRecur(unsigned short node,
+ const char * path,
+ NodeDatum::Type type,
+ const char * data,
+ unsigned int dataLen,
+ NodeOwner owner)
+{
+ // Locate end of section
+ const char * endOfSection = path;
+ while(*endOfSection != '/' && *endOfSection != '\0') ++endOfSection;
+ const unsigned int sectionLen = (endOfSection - path);
+
+ // We operate on structures, not numbers
+ Node * const me = this->node(node);
+ unsigned short * subNodes =
+ me->subNodePtr?(unsigned short *)fromPtr(me->subNodePtr):0;
+
+ // Attempt to locate the sub node within the current node
+ bool match;
+ const unsigned int matchId = locateInNode(me, path, sectionLen, &match);
+ Q_ASSERT((match && matchId < me->subNodes) ||
+ (!match && matchId <= me->subNodes));
+
+ if(match) {
+
+ // There is a sub node of the correct name
+ const unsigned short id = subNodes[matchId];
+
+ if('\0' == *endOfSection) {
+ // Update the node. Bumps *their* version if necessary.
+ bool rv = setData(id, owner, type, data, dataLen);
+ if(rv) bump(node);
+ return rv;
+ }
+ ++endOfSection;
+
+ bool rv = insertRecur(id, endOfSection, type, data, dataLen, owner);
+ if(rv) bump(node);
+ return rv;
+
+ } else { /* !match */
+
+ // We *may* need to lengthen our list
+ unsigned short maxSubNodesLen = subNodes?
+ pool->size_of(subNodes) / sizeof(unsigned short):0;
+ unsigned int newSubNode = 0;
+
+ if('\0' == *endOfSection)
+ newSubNode = newNode(path, sectionLen, owner, type, data, dataLen);
+ else
+ newSubNode = newNode(path, sectionLen);
+
+ if (newSubNode == ROOT_VERSION_ENTRY)
+ return false;
+
+ Node * const newNode = this->node(newSubNode);
+ newNode->parent = node;
+
+ if(maxSubNodesLen < me->subNodes + 1) {
+ // Grow list
+ unsigned short * newSubNodes =
+ (unsigned short *)pool->malloc(growListSize(me->subNodes) *
+ sizeof(unsigned short));
+ ::memcpy(newSubNodes, subNodes, matchId * sizeof(unsigned short));
+ ::memcpy(newSubNodes + matchId + 1, subNodes + matchId,
+ (me->subNodes - matchId) * sizeof(unsigned short));
+ pool->free(fromPtr(me->subNodePtr));
+ me->subNodePtr = ptr(newSubNodes);
+ subNodes = newSubNodes;
+ } else {
+ // Split list
+ ::memmove(subNodes + matchId + 1, subNodes + matchId,
+ (me->subNodes - matchId) * sizeof(unsigned short));
+ }
+ subNodes[matchId] = newSubNode;
+ ++me->subNodes;
+
+ // Bump my version
+ bump(node);
+
+ if('\0' != *endOfSection) {
+ ++endOfSection;
+ insertRecur(newSubNode, endOfSection, type, data, dataLen, owner);
+ }
+ return true;
+ }
+}
+
+VersionTable * FixedMemoryTree::versionTable()
+{
+ return (VersionTable *)poolMem;
+}
+
+void * FixedMemoryTree::fromPtr(unsigned int ptr)
+{
+ if(!ptr)
+ return 0;
+ return (void *)(poolMem + ptr);
+}
+
+void FixedMemoryTree::setNodeChangeFunction(NodeChangeFunction func, void *ctxt)
+{
+ changeFunc = func;
+ changeFuncContext = ctxt;
+}
+
+unsigned int FixedMemoryTree::ptr(void * mem)
+{
+ Q_ASSERT(mem > poolMem);
+ return (unsigned int)((char *)mem - poolMem);
+}
+
+/*! Increment \a node's version number */
+void FixedMemoryTree::bump(unsigned short node)
+{
+ ++(versionTable()->entries[node].version);
+ if(changeFunc)
+ changeFunc(node, changeFuncContext);
+}
+
+Node * FixedMemoryTree::node(unsigned int entry)
+{
+ Q_ASSERT(entry < VERSION_TABLE_ENTRIES);
+ return (Node *)fromPtr(versionTable()->entries[entry].nodePtr);
+}
+
+/*! Returns the node identified by \a entry, or null if there is no node in that
+ slot. This is different to FixedMemoryTree::node() in that it checks the
+ return value (and is thus slower).
+ */
+Node * FixedMemoryTree::getNode(unsigned int entry)
+{
+ Q_ASSERT(entry < VERSION_TABLE_ENTRIES);
+ void * rv = fromPtr(versionTable()->entries[entry].nodePtr);
+ if(rv < poolMem + sizeof(VersionTable)) {
+ return 0;
+ } else {
+ return (Node *)rv;
+ }
+}
+
+Node * FixedMemoryTree::subNode(unsigned int node, unsigned short sub)
+{
+ Q_ASSERT(node < VERSION_TABLE_ENTRIES);
+ Node * me = this->node(node);
+ Q_ASSERT(sub < me->subNodes);
+ unsigned short * const subNodes =
+ (unsigned short *)fromPtr(me->subNodePtr);
+ return this->node(subNodes[sub]);
+}
+
+unsigned int FixedMemoryTree::version(unsigned int entry)
+{
+ Q_ASSERT(entry < VERSION_TABLE_ENTRIES);
+ return versionTable()->entries[entry].version;
+}
+
+/*!
+ Creates a new node of \a name (name length \a len) and sets a single \a watch on it. Returns
+ ROOT_VERSION_ENTRY if the new node could not be created.
+*/
+unsigned short FixedMemoryTree::newNode(const char * name, unsigned int len,
+ NodeWatch owner)
+{
+ Q_ASSERT(owner != INVALID_WATCH);
+
+ // Find an empty node entry
+ unsigned int node = versionTable()->nxtFreeBlk;
+ if (node == ROOT_VERSION_ENTRY)
+ return node;
+
+ // Reset the next free blk
+ versionTable()->nxtFreeBlk = versionTable()->entries[node].nxtFreeBlk;
+
+ int nodeSize = sizeof(Node) + ALIGN4b(len) + sizeof(NodeWatch);
+
+ // Allocate a new node
+ Node * nodePtr = (Node *)pool->malloc(nodeSize);
+ Q_ASSERT((char *)nodePtr > poolMem);
+ ::memset(nodePtr,0, sizeof(Node));
+ nodePtr->creationId = versionTable()->nextCreationId++;
+
+ // Setup name
+ nodePtr->nameLen = len;
+ ::memcpy(nodePtr->name, name, len);
+
+ // Setup watch
+ nodePtr->watchCount = 1;
+ *(NodeWatch *)(nodePtr->name + ALIGN4b(len)) = owner;
+
+ // Enter node
+ versionTable()->entries[node] = VersionTable::Entry(1, ptr(nodePtr));
+ // Bump version
+ bump(node);
+
+ // Done
+ return node;
+
+}
+
+/*!
+ Creates a new node of \a name (name length \a len) and pre-fills it with \a data of length
+ \a dataLen owned by \a owner. If \a owner is an invalid owner, the node is a virtual node and
+ not prefilled with data. Returns ROOT_VERSION_ENTRY if the new node could not be created.
+*/
+unsigned short FixedMemoryTree::newNode(const char * name, unsigned int len,
+ NodeOwner owner, NodeDatum::Type type,
+ const char * data, unsigned int dataLen)
+{
+ Q_ASSERT(owner != INVALID_OWNER || (!data && 0 == dataLen));
+
+ // Find an empty node entry
+ unsigned int node = versionTable()->nxtFreeBlk;
+ if (node == ROOT_VERSION_ENTRY)
+ return node;
+
+ // Reset the next free blk
+ versionTable()->nxtFreeBlk = versionTable()->entries[node].nxtFreeBlk;
+
+ int nodeSize = sizeof(Node) + ALIGN4b(len);
+ if(INVALID_OWNER != owner)
+ nodeSize += sizeof(NodeDatum) + ALIGN4b(dataLen);
+
+ // Allocate a new node
+ Node * nodePtr = (Node *)pool->malloc(nodeSize);
+ Q_ASSERT((char *)nodePtr > poolMem);
+ ::memset(nodePtr, 0, sizeof(Node));
+ nodePtr->creationId = versionTable()->nextCreationId++;
+
+ // Setup name
+ nodePtr->nameLen = len;
+ ::memcpy(nodePtr->name, name, len);
+
+ if(INVALID_OWNER != owner) {
+ // Setup data
+ nodePtr->dataCount = 1;
+ NodeDatum * datum = (NodeDatum *)nodePtr->dataBegin();
+ datum->owner = owner;
+ datum->len = dataLen;
+ datum->type = type;
+ if(dataLen)
+ ::memcpy(datum->data, data, dataLen);
+ }
+
+ // Enter node
+ versionTable()->entries[node] = VersionTable::Entry(1, ptr(nodePtr));
+ // Bump version
+ bump(node);
+
+ // Done
+ return node;
+}
+
+// Use aggregation/composition - the SharedMemoryLayer "has a" server socket
+// rather than "is a" server socket - also gets around multiple inheritance
+// issues with QObject since the QLocalServer in Qt is a QObject
+class ALServerImpl : public QLocalServer
+{
+public:
+ ALServerImpl( QObject *p ) : QLocalServer( p ) {}
+ virtual ~ALServerImpl();
+protected:
+ virtual void incomingConnection(quintptr socketDescriptor);
+};
+
+ALServerImpl::~ALServerImpl()
+{
+}
+
+// declare SharedMemoryLayer
+class SharedMemoryLayer : public QAbstractValueSpaceLayer
+{
+ Q_OBJECT
+public:
+ SharedMemoryLayer();
+ virtual ~SharedMemoryLayer();
+
+ /* Common functions */
+ QString name();
+
+ bool startup(Type);
+
+ QUuid id();
+ unsigned int order();
+
+ Handle item(Handle parent, const QString &);
+ void removeHandle(Handle);
+ void setProperty(Handle handle, Properties);
+
+ bool value(Handle, QVariant *);
+ bool value(Handle, const QString &, QVariant *);
+ QSet<QString> children(Handle);
+
+ QValueSpace::LayerOptions layerOptions() const;
+
+ /* QValueSpaceSubscriber functions */
+ bool supportsInterestNotification() const;
+ bool notifyInterest(Handle handle, bool interested);
+
+ /* QValueSpacePublisher functions */
+ bool setValue(QValueSpacePublisher *creator, Handle handle, const QString &, const QVariant &);
+ bool removeValue(QValueSpacePublisher *creator, Handle handle, const QString &);
+ bool removeSubTree(QValueSpacePublisher *creator, Handle handle);
+ void addWatch(QValueSpacePublisher *creator, Handle handle);
+ void removeWatches(QValueSpacePublisher *creator, Handle parent);
+ void sync();
+
+ void nodeChanged(unsigned short);
+
+ static SharedMemoryLayer * instance();
+
+private slots:
+ void disconnected();
+ void readyRead();
+ void closeConnections();
+
+ void doClientSync();
+
+protected:
+ virtual void timerEvent(QTimerEvent *);
+
+private:
+#ifdef QVALUESPACE_UPDATE_STATS
+ void updateStats();
+#else
+ inline void updateStats() {}
+#endif
+ QList<NodeWatch> watchers(const QByteArray &);
+ void doClientEmit();
+ void doClientTransmit();
+ void doServerTransmit();
+ bool doRemove(const QByteArray &path);
+ bool doWriteItem(const QByteArray &path, const QVariant &val);
+ bool setItem(NodeOwner owner, const QByteArray &path, const QVariant &val);
+ bool doSetItem(NodeOwner owner, const QByteArray &path, const QVariant &val);
+ bool remItems(NodeOwner owner, const QByteArray &path);
+ bool doRemItems(NodeOwner owner, const QByteArray &path);
+ bool setWatch(NodeWatch watch, const QByteArray &path);
+ bool doSetWatch(NodeWatch watch, const QByteArray &path);
+ bool remWatch(NodeWatch watch, const QByteArray &path);
+ bool doRemWatch(NodeWatch watch, const QByteArray &path);
+ void doNotify(const QByteArray &path, const QPacketProtocol *protocol, bool interested);
+ void doClientNotify(QValueSpacePublisher *publisher, const QByteArray &path, bool interested);
+ void doNotifyObject(unsigned long own, unsigned long protocol);
+
+ QString socket() const;
+
+ static QVariant fromDatum(const NodeDatum * data);
+
+ struct ReadHandle
+ {
+ ReadHandle(const QByteArray &_path)
+ : refCount(1), path(_path), currentPath(0),
+ currentNode(INVALID_HANDLE), version(0), creationId(0),
+ forceChange(false)
+ {
+ }
+
+ unsigned int refCount;
+
+ /* Path this read handle is *intended* to point to */
+ QByteArray path;
+ /* Offset into path indicating where we currently point to. 0xFFFFFFFF
+ means we point to the full path (saves path.size() call) */
+ unsigned int currentPath;
+ /* Node we are currently pointing to. */
+ unsigned short currentNode;
+ /* Last version of current node */
+ unsigned int version;
+ /* Last creation Id of current node */
+ unsigned int creationId;
+
+ /* Indicates that version has been updated in a value() call, but
+ a change should be emitted anyway. */
+ bool forceChange;
+ };
+ ReadHandle * rh(QAbstractValueSpaceLayer::Handle handle) const
+ {
+ Q_ASSERT(handle && INVALID_HANDLE != handle);
+ return reinterpret_cast<ReadHandle *>(handle);
+ }
+ bool refreshHandle(ReadHandle *);
+ void clearHandle(ReadHandle *);
+
+ QLocalServer *sserver;
+
+ Type type;
+ FixedMemoryTree * layer;
+ QSystemReadWriteLock * lock;
+ QMutex localLock;
+ QMap<QString, ReadHandle *> handles;
+
+ void triggerTodo();
+ bool triggeredTodo;
+ QPacket todo;
+ QSet<QPacketProtocol *> connections;
+
+ unsigned int newPackId() {
+ lastSentId = nextPackId++;
+ return lastSentId;
+ }
+ unsigned int nextPackId;
+
+ unsigned int lastSentId;
+ unsigned int lastRecvId;
+
+ bool valid;
+ friend class ALServerImpl;
+
+ unsigned int forceChangeCount;
+
+ uchar *clientIndex;
+ void incNode(unsigned short);
+ void decNode(unsigned short);
+ QHash<unsigned short, unsigned int> m_nodeInterest;
+
+
+ unsigned int changedNodesCount;
+ unsigned short changedNodes[VERSION_TABLE_ENTRIES];
+
+ // Stats memory
+ unsigned long *m_statPoolSize;
+ unsigned long *m_statMaxSystemBytes;
+ unsigned long *m_statSystemBytes;
+ unsigned long *m_statInuseBytes;
+ unsigned long *m_statKeepCost;
+
+ QSharedMemory* shm;
+ QSharedMemory* subShm;
+};
+
+QVALUESPACE_AUTO_INSTALL_LAYER(SharedMemoryLayer);
+
+///////////////////////////////////////////////////////////////////////////////
+// define SharedMemoryLayer
+///////////////////////////////////////////////////////////////////////////////
+#define SHMLAYER_SIZE 1000000
+#define SHMLAYER_ADD 0
+#define SHMLAYER_REM 1
+#define SHMLAYER_DONE 2
+#define SHMLAYER_ADDWATCH 5
+#define SHMLAYER_REMWATCH 6
+#define SHMLAYER_SYNC 7
+#define SHMLAYER_SUBINDEX 8
+#define SHMLAYER_NOTIFY 9
+
+struct SharedMemoryLayerClient : public QPacketProtocol
+{
+ SharedMemoryLayerClient(QIODevice *dev, QObject *parent = 0)
+ : QPacketProtocol(dev, parent), index(0) {}
+
+ uchar *index;
+};
+
+SharedMemoryLayer::SharedMemoryLayer()
+: type(Client), layer(0), lock(0), localLock(QMutex::Recursive), triggeredTodo(false),
+ nextPackId(1), lastSentId(0), lastRecvId(0), valid(false),
+ forceChangeCount(0), clientIndex(0),
+ changedNodesCount(0),
+ m_statPoolSize(0), m_statMaxSystemBytes(0), m_statSystemBytes(0),
+ m_statInuseBytes(0), m_statKeepCost(0), shm(0), subShm(0)
+{
+ sserver = new ALServerImpl( this );
+
+ connect(qApp, SIGNAL(destroyed()), this, SLOT(closeConnections()));
+}
+
+SharedMemoryLayer::~SharedMemoryLayer()
+{
+ if(Server == type) {
+ delete lock;
+ lock = 0;
+ }
+}
+
+QString SharedMemoryLayer::name()
+{
+ return QLatin1String("Shared Memory Layer");
+}
+
+static void ShmLayerNodeChanged(unsigned short, void *);
+bool SharedMemoryLayer::startup(Type type)
+{
+ QMutexLocker locker(&localLock);
+
+ valid = false;
+
+ Q_ASSERT(!layer);
+ if(Server == type) {
+ if(!sserver->listen(socket())) {
+ qWarning() << "SharedMemoryLayer: Unable to create server socket. Only "
+ "local connections will be allowed." << sserver->errorString();
+ }
+ } else {
+ QLocalSocket * sock = new QLocalSocket;
+ sock->connectToServer(socket());
+ if(!sock->waitForConnected(1000)) {
+ qWarning("SharedMemoryLayer: Unable to connect to server, "
+ "shared memory layer will be disabled.");
+ delete sock;
+ return false;
+ }
+
+ QPacketProtocol * protocol;
+ protocol = new QPacketProtocol(sock, this);
+ sock->setParent(protocol);
+
+ QObject::connect(sock, SIGNAL(stateChanged(QLocalSocket::LocalSocketState)),
+ protocol, SIGNAL(invalidPacket()));
+ QObject::connect(protocol, SIGNAL(invalidPacket()),
+ this, SLOT(disconnected()));
+ QObject::connect(protocol, SIGNAL(readyRead()),
+ this, SLOT(readyRead()));
+
+ connections.insert(protocol);
+ }
+
+ if(Server == type) {
+ shm = new QSharedMemory(socket(), this);
+ bool created = shm->create(SHMLAYER_SIZE);
+ if (!created) {
+ //qDebug() << "Reattaching to existing memory";
+ shm->attach();
+ }
+ lock = new QSystemReadWriteLock(socket() + "_lock", QSystemReadWriteLock::Create);
+ } else {
+ shm = new QSharedMemory(socket(), this);
+ shm->attach(QSharedMemory::ReadOnly);
+ qsrand(QTime(0,0,0).secsTo(QTime::currentTime())+QCoreApplication::applicationPid());
+ subShm = new QSharedMemory(socket()+QString::number(qrand()), this);
+ if (!subShm->create((VERSION_TABLE_ENTRIES + 7) / 8, QSharedMemory::ReadWrite)) {
+ qWarning() << "SharedMemoryLayer client cannot create clientIndex:"
+ << subShm->errorString() << subShm->key();
+ }
+
+ lock = new QSystemReadWriteLock(socket() + "_lock", QSystemReadWriteLock::Open);
+ }
+
+ if (shm->error() != QSharedMemory::NoError ||
+ ((!subShm || subShm->error()!= QSharedMemory::NoError) && Server != type)) {
+ qFatal("SharedMemoryLayer: Unable to create or access shared resources. (%s - %s)",
+ shm->errorString().toLatin1().constData(),
+ subShm->errorString().toLatin1().constData());
+ return false;
+ }
+
+ if (subShm)
+ clientIndex = (uchar *)subShm->data();
+ if(Client == type) {
+ QPacket mem;
+ mem << (unsigned int)0
+ << (quint8)SHMLAYER_SUBINDEX << subShm->key()
+ << (quint8)SHMLAYER_DONE;
+ (*connections.begin())->send(mem);
+ }
+
+ layer = new FixedMemoryTree((char*)shm->data(),
+ SHMLAYER_SIZE,
+ (Server == type));
+ if(Server == type)
+ layer->setNodeChangeFunction(&ShmLayerNodeChanged, (void *)this);
+
+ this->type = type;
+
+ valid = (lock != 0) && (layer != 0);
+ if (!valid)
+ qWarning("SharedMemoryLayer::startup failed");
+ return valid;
+}
+
+void ALServerImpl::incomingConnection(quintptr socketDescriptor)
+{
+ QLocalSocket * sock = new QLocalSocket;
+ sock->setSocketDescriptor(socketDescriptor);
+
+ QPacketProtocol * protocol;
+ protocol = new SharedMemoryLayerClient(sock, this);
+ sock->setParent(protocol);
+
+ SharedMemoryLayer *al = static_cast<SharedMemoryLayer*>( QObject::parent() );
+
+ QObject::connect(sock, SIGNAL(stateChanged(QLocalSocket::LocalSocketState)),
+ protocol, SIGNAL(invalidPacket()));
+ QObject::connect(protocol, SIGNAL(invalidPacket()),
+ al, SLOT(disconnected()));
+ QObject::connect(protocol, SIGNAL(readyRead()),
+ al, SLOT(readyRead()));
+
+ qDebug() << "New client" << protocol << "connected";
+
+ al->connections.insert(protocol);
+}
+
+void SharedMemoryLayer::timerEvent(QTimerEvent *)
+{
+ QMutexLocker locker(&localLock);
+
+ if(Client == type)
+ doClientTransmit();
+ else
+ doServerTransmit();
+}
+
+void SharedMemoryLayer::doServerTransmit()
+{
+ QMutexLocker locker(&localLock);
+
+ Q_ASSERT(Server == type);
+
+ triggeredTodo = false;
+
+ QPacket others;
+ others << (quint8)SHMLAYER_SYNC << (unsigned int)0;
+
+ for(QSet<QPacketProtocol *>::ConstIterator iter = connections.begin();
+ iter != connections.end();
+ ++iter) {
+
+ SharedMemoryLayerClient *client =
+ (SharedMemoryLayerClient *)(*iter);
+ if(!client->index) {
+ (*iter)->send(others);
+ } else {
+ bool found = false;
+ for(unsigned int ii = 0;!found && ii < changedNodesCount; ++ii) {
+ if(client->index[changedNodes[ii] >> 3] & (1 << (changedNodes[ii] & 0x7)))
+ found = true;
+ }
+ if(found) {
+ //qDebug() << "GGGGGGGGGGGGGGGG";
+ (*iter)->send(others);
+ }
+ }
+
+ }
+
+ changedNodesCount = 0;
+ doClientEmit();
+}
+
+void SharedMemoryLayer::doClientTransmit()
+{
+ QMutexLocker locker(&localLock);
+
+ Q_ASSERT(Client == type);
+
+ triggeredTodo = false;
+
+ if(!todo.isEmpty()) {
+ todo << (quint8)SHMLAYER_DONE;
+ (*connections.begin())->send(todo);
+ todo.clear();
+ }
+}
+
+typedef QHash<QByteArray, QMap<const QPacketProtocol *, uint> > PathsOfInterest;
+Q_GLOBAL_STATIC(PathsOfInterest, pathsOfInterest);
+
+void SharedMemoryLayer::disconnected()
+{
+ Q_ASSERT(sender());
+
+ QMutexLocker locker(&localLock);
+
+ QPacketProtocol * protocol = (QPacketProtocol *)sender();
+ protocol->disconnect();
+ protocol->deleteLater();
+
+ if(Client == type) {
+ qFatal("SharedMemoryLayer: Shared memory layer server unexpectedly terminated.");
+ } else {
+ qDebug() << "Removing" << protocol << "from space";
+
+ NodeOwner owner;
+ owner.data1 = (unsigned long)protocol;
+ owner.data2 = 0xFFFFFFFF;
+
+ connections.remove(protocol);
+ if(layer->remove("/", owner)) {
+ QPacket others;
+ others << (quint8)SHMLAYER_SYNC << (unsigned int)0;
+ for(QSet<QPacketProtocol *>::ConstIterator iter = connections.begin();
+ iter != connections.end();
+ ++iter)
+ (*iter)->send(others);
+ doClientEmit();
+ }
+
+ QList<QByteArray> paths = pathsOfInterest()->keys();
+
+ for (int i = 0; i < paths.count(); ++i) {
+ const QByteArray interestPath = paths.at(i);
+
+ if (!(*pathsOfInterest())[interestPath].remove(protocol))
+ continue;
+
+ if (!(*pathsOfInterest())[interestPath].isEmpty())
+ continue;
+
+ pathsOfInterest()->remove(interestPath);
+
+ QList<NodeWatch> owners = watchers(interestPath);
+
+ QSet<unsigned long> written;
+ QSet<QValueSpacePublisher *> notified;
+
+ for (int ii = 0; ii < owners.count(); ++ii) {
+ const NodeWatch &watch = owners.at(ii);
+
+ if (watch.data1 != (unsigned long)protocol)
+ continue;
+
+ if (watch.data1 == 0) {
+ QValueSpacePublisher *publisher =
+ reinterpret_cast<QValueSpacePublisher *>(watch.data2);
+ if (!notified.contains(publisher)) {
+ doClientNotify(publisher, interestPath, false);
+ notified.insert(publisher);
+ }
+ } else {
+ if (!written.contains(watch.data1)) {
+ ((QPacketProtocol *)watch.data1)->send() << quint8(SHMLAYER_NOTIFY)
+ << watch.data2 << interestPath
+ << false;
+ written.insert(watch.data1);
+ }
+ }
+ }
+
+ }
+ }
+}
+
+static void ShmLayerNodeChanged(unsigned short node, void *ctxt)
+{
+ SharedMemoryLayer *layer = (SharedMemoryLayer *)ctxt;
+ layer->nodeChanged(node);
+}
+
+void SharedMemoryLayer::nodeChanged(unsigned short node)
+{
+ QMutexLocker locker(&localLock);
+
+ if(changedNodesCount != VERSION_TABLE_ENTRIES)
+ changedNodes[changedNodesCount++] = node;
+}
+
+void SharedMemoryLayer::readyRead()
+{
+ QMutexLocker locker(&localLock);
+
+ Q_ASSERT(sender());
+ QPacketProtocol * protocol = (QPacketProtocol *)sender();
+
+ QPacket pack = protocol->read();
+ if (Client == type) {
+ //qDebug() << "Client" << protocol << "woken";
+ if (pack.isEmpty())
+ return;
+
+ quint8 op = 0;
+ pack >> op;
+ switch(op) {
+ case SHMLAYER_SYNC: {
+ unsigned int recvId = 0;
+ pack >> recvId;
+ if(0 != recvId)
+ lastRecvId = recvId;
+ doClientEmit();
+ break;
+ }
+ case SHMLAYER_NOTIFY: {
+ unsigned long owner;
+ QByteArray path;
+ bool interested;
+ pack >> owner >> path >> interested;
+ doClientNotify(reinterpret_cast<QValueSpacePublisher *>(owner), path, interested);
+ break;
+ }
+ default:
+ qFatal("SharedMemoryLayer: Invalid message type %d "
+ "received from shared memory layer server.", op);
+ break;
+ }
+ } else {
+ unsigned int packId = 0;
+ pack >> packId;
+
+ bool changed = false;
+ bool done = false;
+
+ changedNodesCount = 0;
+ while(!done && !pack.isEmpty()) {
+ quint8 op;
+ pack >> op;
+
+ switch(op) {
+ case SHMLAYER_ADD:
+ {
+ unsigned long own;
+ QByteArray path;
+ QVariant value;
+ pack >> own >> path >> value;
+ NodeOwner owner;
+ owner.data1 = (unsigned long)protocol;
+ owner.data2 = own;
+ changed |= doSetItem(owner, path, value);
+ }
+ break;
+
+ case SHMLAYER_REM:
+ {
+ unsigned long own;
+ QByteArray path;
+ pack >> own >> path;
+ NodeOwner owner;
+ owner.data1 = (unsigned long)protocol;
+ owner.data2 = own;
+ changed |= doRemItems(owner, path);
+ }
+ break;
+
+ case SHMLAYER_ADDWATCH:
+ {
+ unsigned long own;
+ QByteArray path;
+ pack >> own >> path;
+ NodeWatch owner;
+ owner.data1 = (unsigned long)protocol;
+ owner.data2 = own;
+ changed |= doSetWatch(owner, path);
+ }
+ break;
+
+ case SHMLAYER_REMWATCH:
+ {
+ unsigned long own;
+ QByteArray path;
+ pack >> own >> path;
+ NodeWatch owner;
+ owner.data1 = (unsigned long)protocol;
+ owner.data2 = own;
+ changed |= doRemWatch(owner, path);
+ }
+ break;
+
+ case SHMLAYER_DONE:
+ done = true;
+ break;
+
+ case SHMLAYER_SUBINDEX:
+ {
+ QString shmkey;
+ pack >> shmkey;
+
+ QSharedMemory* mem = new QSharedMemory(shmkey, protocol);
+ if (!mem->attach(QSharedMemory::ReadOnly)) {
+ qWarning() << "SharedMemoryLayer: Unable to shmat with id:"
+ << shmkey << "error:" << mem->errorString();
+ delete mem;
+ } else {
+ static_cast<SharedMemoryLayerClient *>(protocol)->index =
+ reinterpret_cast<uchar *>(mem->data());
+ }
+ }
+ break;
+
+ case SHMLAYER_NOTIFY:
+ {
+ QByteArray path;
+ bool interested;
+ pack >> path >> interested;
+ doNotify(path, protocol, interested);
+ }
+ break;
+ default:
+ qWarning("SharedMemoryLayer: Invalid client request %x received.", op);
+ disconnected();
+ return;
+ }
+ }
+
+ // Send change notification
+ QPacket causal;
+ causal << (quint8)SHMLAYER_SYNC << packId;
+ protocol->send(causal);
+
+ if(changed)
+ doServerTransmit();
+ }
+}
+
+void SharedMemoryLayer::closeConnections()
+{
+ QMutexLocker locker(&localLock);
+
+ QMutableSetIterator<QPacketProtocol *> i(connections);
+ while (i.hasNext()) {
+ delete i.next();
+ i.remove();
+ }
+}
+
+void SharedMemoryLayer::doClientEmit()
+{
+ QMutexLocker locker(&localLock);
+
+ QMap<QString, ReadHandle *> cpy = handles;
+ for(QMap<QString, ReadHandle *>::ConstIterator iter = cpy.begin();
+ iter != cpy.end();
+ ++iter)
+ ++(*iter)->refCount;
+
+ for(QMap<QString, ReadHandle *>::ConstIterator iter = cpy.begin();
+ iter != cpy.end();
+ ++iter) {
+ if(refreshHandle(*iter)) {
+ if((*iter)->forceChange) {
+ Q_ASSERT(forceChangeCount);
+ --forceChangeCount;
+ (*iter)->forceChange = false;
+ }
+
+ emit handleChanged((Handle)*iter);
+ } else {
+
+ if((*iter)->forceChange) {
+ Q_ASSERT(forceChangeCount);
+ --forceChangeCount;
+ (*iter)->forceChange = false;
+ emit handleChanged((Handle)*iter);
+ }
+ }
+ }
+
+ while(forceChangeCount) {
+ for(QMap<QString, ReadHandle *>::ConstIterator iter = cpy.begin();
+ forceChangeCount && iter != cpy.end();
+ ++iter) {
+
+ if((*iter)->forceChange) {
+ Q_ASSERT(forceChangeCount);
+ --forceChangeCount;
+ (*iter)->forceChange = false;
+ emit handleChanged((Handle)*iter);
+ }
+
+ }
+ }
+
+ for(QMap<QString, ReadHandle *>::ConstIterator iter = cpy.begin();
+ iter != cpy.end();
+ ++iter)
+ removeHandle((Handle)*iter);
+}
+
+QUuid SharedMemoryLayer::id()
+{
+ return QVALUESPACE_SHAREDMEMORY_LAYER;
+}
+
+unsigned int SharedMemoryLayer::order()
+{
+ return 0x10000000;
+}
+
+bool SharedMemoryLayer::value(Handle handle, QVariant *data)
+{
+ QMutexLocker locker(&localLock);
+
+ if (!valid)
+ return false;
+ Q_ASSERT(layer);
+ Q_ASSERT(data);
+
+ ReadHandle * rhandle = rh(handle);
+
+ lock->lockForRead();
+
+ if(0xFFFFFFFF != rhandle->currentPath)
+ if(refreshHandle(rhandle)) {
+ if(!rhandle->forceChange) {
+ ++forceChangeCount;
+ rhandle->forceChange = true;
+ }
+ }
+
+ Node * node = 0;
+ if(0xFFFFFFFF == rhandle->currentPath) {
+ node = layer->getNode(rhandle->currentNode);
+ if(!node || node->creationId != rhandle->creationId) {
+ refreshHandle(rhandle);
+ node = layer->getNode(rhandle->currentNode);
+ if(!rhandle->forceChange) {
+ ++forceChangeCount;
+ rhandle->forceChange = true;
+ }
+ }
+ }
+
+ bool rv = false;
+ if(0xFFFFFFFF == rhandle->currentPath) {
+ Q_ASSERT(node);
+ NodeDatum const * const datum = layer->data(rhandle->currentNode);
+ if(datum) {
+ *data = fromDatum(datum);
+ rv = true;
+ }
+ }
+
+ lock->unlock();
+
+ return rv;
+}
+
+bool SharedMemoryLayer::value(Handle handle, const QString &subPath,
+ QVariant *data)
+{
+ QMutexLocker locker(&localLock);
+
+ Q_ASSERT(layer);
+ Q_ASSERT(data);
+ Q_ASSERT(!subPath.isEmpty());
+ Q_ASSERT(*subPath.constData() == QLatin1Char('/'));
+
+ ReadHandle * rhandle = rh(handle);
+
+ lock->lockForRead();
+
+ if(0xFFFFFFFF != rhandle->currentPath)
+ if(refreshHandle(rhandle)) {
+ if(!rhandle->forceChange) {
+ ++forceChangeCount;
+ rhandle->forceChange = true;
+ }
+ }
+
+ bool rv = false;
+ if(0xFFFFFFFF == rhandle->currentPath) {
+ QByteArray abs_path = rhandle->path + subPath.toUtf8();
+ if (rhandle->path.length() == 1)
+ abs_path = subPath.toUtf8();
+ ReadHandle vhandle(abs_path);
+ clearHandle(&vhandle);
+ refreshHandle(&vhandle);
+ if(0xFFFFFFFF == vhandle.currentPath) {
+ // Valid!
+ NodeDatum const * const datum = layer->data(vhandle.currentNode);
+ if(datum) {
+ *data = fromDatum(datum);
+ rv = true;
+ }
+ }
+ }
+
+ lock->unlock();
+
+ return rv;
+}
+
+QSet<QString> SharedMemoryLayer::children(Handle handle)
+{
+ QMutexLocker locker(&localLock);
+
+ Q_ASSERT(layer);
+ ReadHandle * rhandle = rh(handle);
+
+ QSet<QString> rv;
+ lock->lockForRead();
+
+ if(0xFFFFFFFF != rhandle->currentPath)
+ if(refreshHandle(rhandle)) {
+ if(!rhandle->forceChange) {
+ ++forceChangeCount;
+ rhandle->forceChange = true;
+ }
+ }
+
+ Node * node = 0;
+ if(0xFFFFFFFF == rhandle->currentPath) {
+ node = layer->getNode(rhandle->currentNode);
+ if(!node || node->creationId != rhandle->creationId) {
+ if(refreshHandle(rhandle)) {
+ if(0xFFFFFFFF == rhandle->currentPath)
+ node = layer->getNode(rhandle->currentNode);
+ else
+ node = 0;
+ if(!rhandle->forceChange) {
+ ++forceChangeCount;
+ rhandle->forceChange = true;
+ }
+ }
+ }
+ }
+
+ if(0xFFFFFFFF == rhandle->currentPath) {
+ Q_ASSERT(node);
+ for(unsigned short ii = 0; ii < node->subNodes; ++ii) {
+ Node * n = layer->subNode(rhandle->currentNode, ii);
+ rv.insert(QString::fromUtf8(n->name, n->nameLen));
+ }
+ }
+
+ lock->unlock();
+ return rv;
+}
+
+QValueSpace::LayerOptions SharedMemoryLayer::layerOptions() const
+{
+ return QValueSpace::TransientLayer | QValueSpace::WritableLayer;
+}
+
+SharedMemoryLayer::Handle SharedMemoryLayer::item(Handle parent, const QString &key)
+{
+ QMutexLocker locker(&localLock);
+
+ Q_UNUSED(parent);
+ Q_ASSERT(layer);
+ Q_ASSERT(*key.constData() == QLatin1Char('/'));
+ Q_ASSERT(InvalidHandle == parent);
+ QMap<QString, ReadHandle *>::Iterator iter = handles.find(key);
+ if(iter != handles.end()) {
+ ++(*iter)->refCount;
+ return (QAbstractValueSpaceLayer::Handle)*iter;
+ } else {
+ ReadHandle * handle = new ReadHandle(key.toUtf8());
+ clearHandle(handle);
+ lock->lockForRead();
+ refreshHandle(handle);
+ lock->unlock();
+ handles.insert(key, handle);
+ return (QAbstractValueSpaceLayer::Handle)(handle);
+ }
+}
+
+/*!
+ Attempt to advance \a handle to point to the true path or update it if it
+ does. Returns true if the handle has either:
+
+ \list 1
+ \i Changed value
+ \i Moved from being a partial reference to a true reference
+ \i Moved from being a true reference to a partial reference
+ \endlist
+
+ That is, returns true if a handleChanged() signal is needed :)
+
+ The layer MUST be locked for reading.
+ */
+
+bool SharedMemoryLayer::refreshHandle(ReadHandle * handle)
+{
+ QMutexLocker locker(&localLock);
+
+ Q_ASSERT(handle);
+
+ ReadHandle old = *handle;
+ unsigned short oldNode = handle->currentNode;
+
+ // Refresh handle
+ if(0xFFFFFFFF == handle->currentPath) {
+ // True path!
+ Node * node = layer->getNode(handle->currentNode);
+ if(!node || node->creationId != handle->creationId) {
+ // Bad news!
+ clearHandle(handle);
+ oldNode = INVALID_HANDLE; // prevent double dec
+ } else {
+ // Super - still a true path
+ handle->version = layer->version(handle->currentNode);
+ }
+ }
+
+ if(0xFFFFFFFF != handle->currentPath &&
+ INVALID_HANDLE != handle->currentNode) {
+ // Partial
+ Node * node = layer->getNode(handle->currentNode);
+ if(!node || node->creationId != handle->creationId) {
+ // Bad news!
+ clearHandle(handle);
+ oldNode = INVALID_HANDLE; // prevent double dec
+ } else if(handle->version != layer->version(handle->currentNode)) {
+ // Something changed - try and advance!
+ const char * out = 0;
+ handle->currentNode = layer->findClosest(handle->currentNode,
+ handle->path.constData() + handle->currentPath,
+ &out);
+
+ Node * newNode = layer->getNode(handle->currentNode);
+ Q_ASSERT(newNode);
+ handle->version = layer->version(handle->currentNode);
+ handle->creationId = newNode->creationId;
+ if(out)
+ handle->currentPath = (out - handle->path.constData());
+ else
+ handle->currentPath = 0xFFFFFFFF;
+ }
+ }
+
+ if(INVALID_HANDLE == handle->currentNode) {
+ // Start from scratch
+ const char * out = 0;
+ handle->currentNode = layer->findClosest(handle->path.constData(),
+ &out);
+
+ Node * newNode = layer->getNode(handle->currentNode);
+ Q_ASSERT(newNode);
+ handle->version = layer->version(handle->currentNode);
+ handle->creationId = newNode->creationId;
+ if(out)
+ handle->currentPath = (out - handle->path.constData());
+ else
+ handle->currentPath = 0xFFFFFFFF;
+ }
+
+ if(handle->currentNode != oldNode) {
+ decNode(oldNode);
+ incNode(handle->currentNode);
+ }
+
+ bool rv = false;
+ if(0xFFFFFFFF == handle->currentPath) {
+ // Valid!
+ rv = (handle->currentNode != old.currentNode ||
+ handle->currentPath != old.currentPath ||
+ handle->creationId != old.creationId ||
+ handle->version != old.version);
+ } else {
+ // Returns true if old was valid
+ rv = (0xFFFFFFFF == old.currentPath);
+ }
+
+ return rv;
+}
+
+void SharedMemoryLayer::clearHandle(ReadHandle *handle)
+{
+ QMutexLocker locker(&localLock);
+
+ handle->currentPath = 0;
+ decNode(handle->currentNode);
+ handle->currentNode = INVALID_HANDLE;
+ handle->version = 0;
+ handle->creationId = 0;
+}
+
+void SharedMemoryLayer::triggerTodo()
+{
+ QMutexLocker locker(&localLock);
+
+ if (triggeredTodo || !valid)
+ return;
+
+ QCoreApplication::postEvent(this, new QTimerEvent(0), Qt::LowEventPriority);
+}
+
+void SharedMemoryLayer::setProperty(Handle, Properties)
+{
+ // Properties aren't used by shared memory layer (always emits changed)
+}
+
+void SharedMemoryLayer::removeHandle(Handle h)
+{
+ QMutexLocker locker(&localLock);
+
+ Q_ASSERT(layer);
+ Q_ASSERT(h && INVALID_HANDLE != h);
+
+ ReadHandle * rhandle = rh(h);
+ Q_ASSERT(rhandle->refCount);
+ --rhandle->refCount;
+ if(!rhandle->refCount) {
+ handles.remove(QString::fromUtf8(rhandle->path.constData(), rhandle->path.length()));
+ clearHandle(rhandle);
+ delete rhandle;
+ }
+}
+
+#ifdef QVALUESPACE_UPDATE_STATS
+
+void SharedMemoryLayer::updateStats()
+{
+ QMutexLocker locker(&localLock);
+
+ if(!m_statPoolSize) {
+ // Hasn't initialized stats!
+ Q_ASSERT(layer);
+ unsigned int v = 0;
+ NodeOwner owner = { 1, 1 };
+
+ layer->insert("/.ValueSpace/SharedMemoryLayer/Memory/PoolSize",
+ NodeDatum::UInt, (const char *)&v, sizeof(unsigned int),
+ owner);
+ layer->insert("/.ValueSpace/SharedMemoryLayer/Memory/MaxSystemBytes",
+ NodeDatum::UInt, (const char *)&v, sizeof(unsigned int),
+ owner);
+ layer->insert("/.ValueSpace/SharedMemoryLayer/Memory/SystemBytes",
+ NodeDatum::UInt, (const char *)&v, sizeof(unsigned int),
+ owner);
+ layer->insert("/.ValueSpace/SharedMemoryLayer/Memory/InuseBytes",
+ NodeDatum::UInt, (const char *)&v, sizeof(unsigned int),
+ owner);
+ layer->insert("/.ValueSpace/SharedMemoryLayer/Memory/KeepCost",
+ NodeDatum::UInt, (const char *)&v, sizeof(unsigned int),
+ owner);
+
+ const char * out;
+ unsigned short node;
+ unsigned short baseNode =
+ layer->findClosest("/.ValueSpace/SharedMemoryLayer/Memory", &out);
+
+ node = layer->findClosest(baseNode, "/PoolSize", &out);
+ m_statPoolSize = (unsigned long *)layer->data(node)->data;
+ node = layer->findClosest(baseNode, "/MaxSystemBytes", &out);
+ m_statMaxSystemBytes = (unsigned long *)layer->data(node)->data;
+ node = layer->findClosest(baseNode, "/SystemBytes", &out);
+ m_statSystemBytes = (unsigned long *)layer->data(node)->data;
+ node = layer->findClosest(baseNode, "/InuseBytes", &out);
+ m_statInuseBytes = (unsigned long *)layer->data(node)->data;
+ node = layer->findClosest(baseNode, "/KeepCost", &out);
+ m_statKeepCost = (unsigned long *)layer->data(node)->data;
+ }
+
+ QMallocPool::MemoryStats mstats = layer->mallocPool()->memoryStatistics();
+ *m_statPoolSize = mstats.poolSize;
+ *m_statMaxSystemBytes = mstats.maxSystemBytes;
+ *m_statSystemBytes = mstats.systemBytes;
+ *m_statInuseBytes = mstats.inuseBytes;
+ *m_statKeepCost = mstats.keepCost;
+}
+
+#endif
+
+QList<NodeWatch> SharedMemoryLayer::watchers(const QByteArray &path)
+{
+ QMutexLocker locker(&localLock);
+
+ Q_ASSERT(layer);
+
+ lock->lockForRead();
+
+ const char * matched = 0;
+ unsigned short node = layer->findClosest(path.constData(), &matched);
+ QList<NodeWatch> owners;
+ while(INVALID_HANDLE != node) {
+ Node * n = layer->getNode(node);
+ if(1 == n->watchCount) {
+ owners.append(*(NodeWatch *)n->watchBegin());
+ } else if(2 <= n->watchCount) {
+ NodeWatch *watches =
+ (NodeWatch *)layer->fromPtr(*(unsigned int*)n->watchBegin());
+ for(unsigned int ii = 0; ii < n->watchCount; ++ii) {
+ owners.append(watches[ii]);
+ }
+ }
+
+ node = n->parent;
+ }
+
+ lock->unlock();
+
+ return owners;
+}
+
+void SharedMemoryLayer::doNotify(const QByteArray &path, const QPacketProtocol *protocol,
+ bool interested)
+{
+ QMutexLocker locker(&localLock);
+
+ bool sendNotification = false;
+ if (interested) {
+ if ((++(*pathsOfInterest())[path][protocol]) == 1)
+ sendNotification = true;
+ } else {
+ if ((--(*pathsOfInterest())[path][protocol]) == 0) {
+ (*pathsOfInterest())[path].remove(protocol);
+
+ if ((*pathsOfInterest())[path].isEmpty())
+ pathsOfInterest()->remove(path);
+
+ sendNotification = true;
+ }
+ }
+
+ if (!sendNotification)
+ return;
+
+ QList<NodeWatch> owners = watchers(path);
+
+ QSet<unsigned long> written;
+ QSet<QValueSpacePublisher *> notified;
+
+ for (int ii = 0; ii < owners.count(); ++ii) {
+ const NodeWatch &watch = owners.at(ii);
+
+ if (watch.data1 == 0) {
+ QValueSpacePublisher *publisher =
+ reinterpret_cast<QValueSpacePublisher *>(watch.data2);
+ if (!notified.contains(publisher)) {
+ doClientNotify(publisher, path, interested);
+ notified.insert(publisher);
+ }
+ } else {
+ if (!written.contains(watch.data1)) {
+ ((QPacketProtocol *)watch.data1)->send() << quint8(SHMLAYER_NOTIFY)
+ << watch.data2 << path << interested;
+ written.insert(watch.data1);
+ }
+ }
+ }
+}
+
+void SharedMemoryLayer::doNotifyObject(unsigned long own, unsigned long protocol)
+{
+ QMutexLocker locker(&localLock);
+
+ QList<QByteArray> paths = pathsOfInterest()->keys();
+
+ for (int i = 0; i < paths.count(); ++i) {
+ const QByteArray &interestPath = paths.at(i);
+
+ QList<NodeWatch> owners = watchers(interestPath);
+
+ QSet<unsigned long> written;
+ QSet<QValueSpacePublisher *> notified;
+
+ for (int ii = 0; ii < owners.count(); ++ii) {
+ const NodeWatch &watch = owners.at(ii);
+
+ if (watch.data1 != protocol || watch.data2 != own)
+ continue;
+
+ if (watch.data1 == 0) {
+ QValueSpacePublisher *publisher =
+ reinterpret_cast<QValueSpacePublisher *>(watch.data2);
+ if (!notified.contains(publisher)) {
+ doClientNotify(publisher, interestPath, true);
+ notified.insert(publisher);
+ }
+ } else {
+ if (!written.contains(watch.data1)) {
+ ((QPacketProtocol *)watch.data1)->send() << quint8(SHMLAYER_NOTIFY)
+ << watch.data2 << interestPath
+ << true;
+ written.insert(watch.data1);
+ }
+ }
+ }
+ }
+}
+
+bool SharedMemoryLayer::setWatch(NodeWatch watch, const QByteArray &path)
+{
+ QMutexLocker locker(&localLock);
+
+ if(path.count() > MAX_PATH_SIZE || path.startsWith("/.ValueSpace") || !valid)
+ return false;
+ Q_ASSERT(layer);
+
+ if(Client == type) {
+ if(todo.isEmpty())
+ todo << newPackId();
+
+ todo << (quint8)SHMLAYER_ADDWATCH << watch.data2 << path;
+ triggerTodo();
+ return true;
+ } else {
+ bool changed = doSetWatch(watch, path);
+ if(changed) triggerTodo();
+ return changed;
+ }
+}
+
+bool SharedMemoryLayer::doSetWatch(NodeWatch watch, const QByteArray &path)
+{
+ QMutexLocker locker(&localLock);
+
+ Q_ASSERT(layer);
+
+ lock->lockForWrite();
+ bool rv = layer->addWatch(path.constData(), watch);
+ updateStats();
+ lock->unlock();
+
+ doNotifyObject(watch.data2, watch.data1);
+
+ return rv;
+}
+
+bool SharedMemoryLayer::remWatch(NodeWatch watch, const QByteArray &path)
+{
+ QMutexLocker locker(&localLock);
+
+ if(path.count() > MAX_PATH_SIZE || !valid)
+ return false;
+ Q_ASSERT(layer);
+
+ if(Client == type) {
+ if(todo.isEmpty())
+ todo << newPackId();
+
+ todo << (quint8)SHMLAYER_REMWATCH << watch.data2 << path;
+ triggerTodo();
+ return true;
+ } else {
+ bool changed = doRemWatch(watch, path);
+ if(changed) triggerTodo();
+ return changed;
+ }
+}
+
+bool SharedMemoryLayer::doRemWatch(NodeWatch watch, const QByteArray &path)
+{
+ QMutexLocker locker(&localLock);
+
+ Q_ASSERT(layer);
+
+ lock->lockForWrite();
+ bool rv = layer->remWatch(path.constData(), watch);
+ updateStats();
+ lock->unlock();
+
+ return rv;
+}
+
+bool SharedMemoryLayer::setItem(NodeOwner owner, const QByteArray &path,
+ const QVariant &val)
+{
+ QMutexLocker locker(&localLock);
+
+ if(path.count() > MAX_PATH_SIZE || path.startsWith("/.ValueSpace") || !valid)
+ return false;
+ Q_ASSERT(layer);
+ bool changed = false;
+
+ if(Client == type) {
+ if(todo.isEmpty())
+ todo << newPackId();
+
+ todo << (quint8)SHMLAYER_ADD << owner.data2 << path << val;
+ triggerTodo();
+ return true;
+ } else {
+ changed = doSetItem(owner, path, val);
+ if(changed)
+ triggerTodo();
+ }
+ return changed;
+}
+
+bool SharedMemoryLayer::doSetItem(NodeOwner owner, const QByteArray &path,
+ const QVariant &val)
+{
+ QMutexLocker locker(&localLock);
+
+ bool rv = false;
+
+ lock->lockForWrite();
+
+ switch(val.type()) {
+ case QVariant::Bool:
+ {
+ unsigned int v = val.toBool()?1:0;
+ rv = layer->insert(path.constData(), NodeDatum::Boolean, (const char *)&v,
+ sizeof(unsigned int), owner);
+ }
+ break;
+ case QVariant::Int:
+ {
+ int v = val.toInt();
+ rv = layer->insert(path.constData(), NodeDatum::Int, (const char *)&v,
+ sizeof(int), owner);
+ }
+ break;
+ case QVariant::UInt:
+ {
+ unsigned int v = val.toUInt();
+ rv = layer->insert(path.constData(), NodeDatum::UInt, (const char *)&v,
+ sizeof(unsigned int), owner);
+ }
+ break;
+ case QVariant::LongLong:
+ {
+ long long v = val.toLongLong();
+ rv = layer->insert(path.constData(), NodeDatum::LongLong, (const char *)&v,
+ sizeof(long long), owner);
+ }
+ break;
+ case QVariant::ULongLong:
+ {
+ unsigned long long v = val.toULongLong();
+ rv = layer->insert(path.constData(), NodeDatum::ULongLong, (const char *)&v,
+ sizeof(unsigned long long), owner);
+ }
+ break;
+ case QVariant::Double:
+ {
+ double v = val.toDouble();
+ rv = layer->insert(path.constData(), NodeDatum::Double, (const char *)&v,
+ sizeof(double), owner);
+ }
+ break;
+ case QVariant::Char:
+ {
+ QChar v = val.toChar();
+ rv = layer->insert(path.constData(), NodeDatum::Char, (const char *)&v,
+ sizeof(QChar), owner);
+ }
+ break;
+ case QVariant::String:
+ {
+ QString v = val.toString();
+ rv = layer->insert(path.constData(), NodeDatum::String,
+ (const char *)v.constData(), v.length() * sizeof(QChar), owner);
+ }
+ break;
+ case QVariant::ByteArray:
+ {
+ QByteArray v = val.toByteArray();
+ rv = layer->insert(path.constData(), NodeDatum::ByteArray,
+ v.constData(), v.length(), owner);
+ }
+ break;
+ default:
+ {
+ QByteArray v;
+ QDataStream ds(&v, QIODevice::WriteOnly);
+ ds << val;
+ rv = layer->insert(path.constData(), NodeDatum::SerializedType,
+ v.constData(), v.length(), owner);
+ }
+ break;
+ }
+
+ updateStats();
+ lock->unlock();
+
+ return rv;
+}
+
+bool SharedMemoryLayer::remItems(NodeOwner owner, const QByteArray &path)
+{
+ QMutexLocker locker(&localLock);
+
+ if (!valid)
+ return false;
+ bool rv = false;
+
+ if(Client == type) {
+ if(todo.isEmpty())
+ todo << newPackId();
+
+ todo << (quint8)SHMLAYER_REM << owner.data2 << path;
+ triggerTodo();
+ } else {
+ rv = doRemItems(owner, path);
+ if(rv) triggerTodo();
+ }
+
+ return rv;
+}
+
+bool SharedMemoryLayer::doRemItems(NodeOwner owner, const QByteArray &path)
+{
+ QMutexLocker locker(&localLock);
+
+ if (!valid)
+ return false;
+ bool rv = false;
+
+ if(Client == type) {
+ if(todo.isEmpty())
+ todo << newPackId();
+
+ todo << (quint8)SHMLAYER_REM << owner.data2 << path;
+ triggerTodo();
+
+ } else {
+ Q_ASSERT(layer);
+ lock->lockForWrite();
+ rv = layer->remove(path.constData(), owner);
+ updateStats();
+ lock->unlock();
+ }
+
+ return rv;
+}
+
+QString SharedMemoryLayer::socket() const
+{
+ QString socketPath(QLatin1String("qt/"));
+
+#ifdef Q_OS_UNIX
+ static bool pipePathExists = false;
+ if (!pipePathExists) {
+ if (QDir::temp().mkpath(socketPath))
+ pipePathExists = true;
+ }
+ socketPath = QDir::temp().absoluteFilePath(socketPath);
+#endif
+
+ QByteArray key = qgetenv("QT_VALUESPACE_SHMLAYER");
+ if (!key.isEmpty())
+ socketPath.append(QString::fromLocal8Bit(key.constData(), key.length()));
+ else
+ socketPath.append(QLatin1String("valuespace_shmlayer"));
+
+ return socketPath;
+}
+
+void SharedMemoryLayer::sync()
+{
+ if (type == Client) {
+ if (QThread::currentThread() != thread())
+ QMetaObject::invokeMethod(this, "doClientSync", Qt::BlockingQueuedConnection);
+ else
+ doClientSync();
+ } else {
+ doServerTransmit();
+ }
+}
+
+void SharedMemoryLayer::doClientSync()
+{
+ QMutexLocker locker(&localLock);
+
+ Q_ASSERT(1 == connections.count());
+
+ unsigned int waitId = lastSentId;
+
+ if (lastRecvId >= waitId)
+ return; // Done
+
+ // Send any outstanding messages
+ doClientTransmit();
+
+ // Get transmission socket
+ QLocalSocket *socket = static_cast<QLocalSocket *>((*connections.begin())->device());
+ socket->flush();
+
+ // Wait
+ while (QLocalSocket::UnconnectedState != socket->state() && waitId > lastRecvId)
+ socket->waitForReadyRead(-1);
+}
+
+QVariant SharedMemoryLayer::fromDatum(const NodeDatum * data)
+{
+ switch(data->type) {
+ case NodeDatum::Boolean:
+ Q_ASSERT(4 == data->len);
+ return QVariant(0 != (*(unsigned int *)data->data));
+ case NodeDatum::Int:
+ Q_ASSERT(4 == data->len);
+ return QVariant((*(int *)data->data));
+ case NodeDatum::UInt:
+ Q_ASSERT(4 == data->len);
+ return QVariant((*(unsigned int *)data->data));
+ case NodeDatum::LongLong:
+ Q_ASSERT(8 == data->len);
+ return QVariant((*(long long*)data->data));
+ case NodeDatum::ULongLong:
+ Q_ASSERT(8 == data->len);
+ return QVariant((*(unsigned long long*)data->data));
+ case NodeDatum::Double:
+ Q_ASSERT(8 == data->len);
+ return QVariant((*(double*)data->data));
+ case NodeDatum::Char:
+ Q_ASSERT(2 == data->len);
+ return QVariant(QChar((unsigned short)(0xFFFF & (*(unsigned int*)data->data))));
+ case NodeDatum::String:
+ return QVariant(QString((QChar *)data->data,
+ data->len / sizeof(QChar)));
+ case NodeDatum::ByteArray:
+ return QVariant(QByteArray(data->data, data->len));
+ case NodeDatum::SerializedType:
+ {
+ QByteArray ba(data->data, data->len);
+ QDataStream ds(ba);
+ QVariant rv;
+ ds >> rv;
+ return rv;
+ }
+ default:
+ qFatal("SharedMemoryLayer: Unknown datum type.");
+ return QVariant();
+ }
+}
+
+void SharedMemoryLayer::incNode(unsigned short node)
+{
+ QMutexLocker locker(&localLock);
+
+ if(type == Server || node == INVALID_HANDLE)
+ return;
+
+ QHash<unsigned short, unsigned int>::Iterator iter =
+ m_nodeInterest.find(node);
+ if(iter == m_nodeInterest.end()) {
+ clientIndex[node >> 3] |= (1 << (node & 0x7));
+ m_nodeInterest.insert(node, 1);
+ } else {
+ (*iter)++;
+ }
+}
+
+void SharedMemoryLayer::decNode(unsigned short node)
+{
+ QMutexLocker locker(&localLock);
+
+ if(type == Server || node == INVALID_HANDLE)
+ return;
+
+ QHash<unsigned short, unsigned int>::Iterator iter =
+ m_nodeInterest.find(node);
+ Q_ASSERT(iter != m_nodeInterest.end());
+ (*iter)--;
+ if(!*iter) {
+ clientIndex[node >> 3] &= ~(1 << (node & 0x7));
+ m_nodeInterest.erase(iter);
+ }
+}
+
+Q_GLOBAL_STATIC(SharedMemoryLayer, sharedMemoryLayer);
+SharedMemoryLayer * SharedMemoryLayer::instance()
+{
+ return sharedMemoryLayer();
+}
+
+typedef QSet<QValueSpacePublisher *> WatchObjects;
+Q_GLOBAL_STATIC(WatchObjects, watchObjects);
+
+void SharedMemoryLayer::doClientNotify(QValueSpacePublisher *publisher, const QByteArray &path,
+ bool interested)
+{
+ QMutexLocker locker(&localLock);
+
+ // Invalid publisher.
+ if (!watchObjects()->contains(publisher))
+ return;
+
+ QByteArray emitPath;
+
+ const QByteArray publisherPath = publisher->path().toUtf8();
+ if (path.startsWith(publisherPath)) {
+ // path is under publisherPath
+ emitPath = path.mid(publisherPath.length());
+ } else if (publisherPath.startsWith(path)) {
+ // path is a parent of publisherPath
+ } else {
+ // path is not for this publisher.
+ return;
+ }
+
+ emitInterestChanged(publisher, QString::fromUtf8(emitPath.constData()), interested);
+ }
+
+bool SharedMemoryLayer::supportsInterestNotification() const
+{
+ return true;
+}
+
+bool SharedMemoryLayer::notifyInterest(Handle handle, bool interested)
+{
+ QMutexLocker locker(&localLock);
+
+ if (!valid)
+ return false;
+ Q_ASSERT(layer);
+
+ ReadHandle *rhandle = rh(handle);
+ if (type == Client) {
+ if (todo.isEmpty())
+ todo << newPackId();
+
+ todo << quint8(SHMLAYER_NOTIFY) << rhandle->path << interested;
+
+ triggerTodo();
+ } else {
+ doNotify(rhandle->path, 0, interested);
+ }
+
+ return true;
+}
+
+bool SharedMemoryLayer::setValue(QValueSpacePublisher *creator, Handle handle, const QString &path,
+ const QVariant &data)
+{
+ QMutexLocker locker(&localLock);
+
+ ReadHandle *readHandle = reinterpret_cast<ReadHandle *>(handle);
+
+ if (!handles.values().contains(readHandle))
+ return false;
+
+ NodeOwner owner;
+ owner.data1 = 0;
+ owner.data2 = reinterpret_cast<unsigned long>(creator);
+
+ QByteArray fullPath(readHandle->path);
+ if (!fullPath.endsWith('/') && path != QLatin1String("/"))
+ fullPath.append('/');
+
+ fullPath.append(path.mid(1));
+
+ return setItem(owner, fullPath, data);
+}
+
+bool SharedMemoryLayer::removeValue(QValueSpacePublisher *creator,
+ Handle handle,
+ const QString &path)
+{
+ QMutexLocker locker(&localLock);
+
+ ReadHandle *readHandle = reinterpret_cast<ReadHandle *>(handle);
+
+ if (!handles.values().contains(readHandle))
+ return false;
+
+ NodeOwner owner;
+ owner.data1 = 0;
+ owner.data2 = reinterpret_cast<quintptr>(creator);
+
+ QByteArray fullPath(readHandle->path);
+ if (!fullPath.endsWith('/'))
+ fullPath.append('/');
+
+ int index = 0;
+ while (index < path.length() && path[index] == QLatin1Char('/'))
+ ++index;
+
+ fullPath.append(path.mid(index));
+
+ return remItems(owner, fullPath);
+}
+
+bool SharedMemoryLayer::removeSubTree(QValueSpacePublisher *creator, Handle handle)
+{
+ QMutexLocker locker(&localLock);
+
+ ReadHandle *readHandle = reinterpret_cast<ReadHandle *>(handle);
+
+ if (!handles.values().contains(readHandle))
+ return false;
+
+ NodeOwner owner;
+ owner.data1 = 0;
+ owner.data2 = reinterpret_cast<quintptr>(creator);
+
+ return remItems(owner, readHandle->path);
+}
+
+void SharedMemoryLayer::addWatch(QValueSpacePublisher *creator, Handle handle)
+{
+ QMutexLocker locker(&localLock);
+
+ watchObjects()->insert(creator);
+
+ ReadHandle *readHandle = reinterpret_cast<ReadHandle *>(handle);
+
+ if (!handles.values().contains(readHandle))
+ return;
+
+ NodeWatch owner;
+ owner.data1 = 0;
+ owner.data2 = reinterpret_cast<unsigned long>(creator);
+
+ setWatch(owner, readHandle->path);
+}
+
+void SharedMemoryLayer::removeWatches(QValueSpacePublisher *creator, Handle parent)
+{
+ QMutexLocker locker(&localLock);
+
+ watchObjects()->remove(creator);
+
+ ReadHandle *readHandle = reinterpret_cast<ReadHandle *>(parent);
+
+ if (!handles.values().contains(readHandle))
+ return;
+
+ NodeWatch owner;
+ owner.data1 = 0;
+ owner.data2 = reinterpret_cast<unsigned long>(creator);
+
+ remWatch(owner, readHandle->path);
+}
+
+#include "sharedmemorylayer.moc"
+QTM_END_NAMESPACE
+