

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  This file is part of CMSDK version 2.1.1.260726 (26 July 2026)
//_/_/  
//_/_/  developed by
//_/_/  
//_/_/  Communicative Machines Ltd. 
//_/_/    http://cmlabs.com/
//_/_/  Copyright (c) 2026
//_/_/  
//_/_/  in collaboration with 
//_/_/  
//_/_/  Icelandic Institute for Intelligent Machines ses
//_/_/    Menntavegur 1
//_/_/    101 Reykjavik
//_/_/    ICELAND
//_/_/    http://www.iiim.is/
//_/_/
//_/_/  and is released under the following
//_/_/  
//_/_/  EXTENDED BSD License
//_/_/
//_/_/  Redistribution and use in source and binary forms, with or without
//_/_/  modification, is permitted provided that the following conditions 
//_/_/  are met:
//_/_/
//_/_/  - Redistributions of source code must retain the above copyright 
//_/_/    and collaboration notice, this list of conditions and the following 
//_/_/    disclaimer.
//_/_/
//_/_/  - Redistributions in binary form must reproduce the above copyright 
//_/_/    notice, this list of conditions and the following disclaimer in the 
//_/_/    documentation and/or other materials provided with the distribution.
//_/_/
//_/_/  - Neither the name of its copyright holders nor the names of its 
//_/_/    contributors may be used to endorse or promote products derived from 
//_/_/    this software without specific prior written permission.
//_/_/
//_/_/  - CADIA Clause: The license granted in and to the software under this 
//_/_/    agreement is a limited-use license. The software may not be used in 
//_/_/    furtherance of: (i) intentionally causing bodily injury or severe 
//_/_/    emotional harm to any person; (ii) invading the personal privacy or 
//_/_/    violating the human rights of any person; or (iii) committing or 
//_/_/    preparing for any act of war. 
//_/_/
//_/_/  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
//_/_/  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
//_/_/  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
//_/_/  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
//_/_/  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
//_/_/  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
//_/_/  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
//_/_/  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
//_/_/  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
//_/_/  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
//_/_/  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
/*! @file ComponentMemory.cpp
    @brief Implementation of the shared component registry: record allocation/relocation, parameters, private data, stats and migration support.

    All writes happen under the segment's named cross-process mutex; record growth may
    relocate entries and rewrite the offset index, which is why only offsets are stored
    in shared memory.
    @ingroup Memory */

#include "ComponentMemory.h"
#include "UnitTestFramework.h"

namespace cmlabs {

ComponentMemory::ComponentMemory(MasterMemory* master) {
	mutex = NULL;
	this->master = master;
	header = NULL;
	memorySize = 0;
	port = 0;
	serial = 0;
}

ComponentMemory::~ComponentMemory() {
	if (mutex)
		mutex->enter(5000, __FUNCTION__);
	if (memorySize)
		utils::CloseSharedMemorySegment((char*) header, memorySize);
	header = NULL;
	if (mutex)
		mutex->leave();
	delete(mutex);
	mutex = NULL;
}

bool ComponentMemory::getMemoryUsage(uint64& alloc, uint64& usage) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	alloc = memorySize;
	usage = header->usage;

	mutex->leave();
	return true;
}

bool ComponentMemory::addLocalPerformanceStats(std::list<PerfStats> &perfStats) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	PerfStats stats;
	ComponentInfoStruct* info;
	for (uint32 n=0; n<=header->maxID; n++) {
		if (info = GETCOMPDATA(header, n)) {
			memset(&stats, 0, sizeof(PerfStats));
			stats.compID = n;
			stats.spaceID = info->procID;
			stats.nodeID = info->nodeID;
			stats.type = info->type;
			stats.status = info->status;
			stats.currentCPUTicks = info->stats.usageCPUTicks;
			stats.currentMemoryBytes = info->size;
			stats.totalInputBytes = info->stats.msgInBytes;
			stats.totalInputCount = info->stats.msgInCount;
			stats.totalOutputBytes = info->stats.msgOutBytes;
			stats.totalOutputCount = info->stats.msgOutCount;
			stats.runCount = info->stats.runCount;
			//stats.totalQueueBytes = 0;
			//stats.totalQueueCount = 0;
			stats.currentRunStartTime = info->stats.currentRunStartTime;
			stats.firstRunStartTime = info->stats.firstRunStartTime;
			stats.totalCycleCount = info->stats.cycleCount;
			stats.totalRunCount = info->stats.runCount;
			stats.migrationCount = info->stats.migrationCount;
			perfStats.push_back(stats);
		}
	}
	mutex->leave();
	return true;
}

std::string ComponentMemory::printFriendly() {
	std::vector<ComponentInfoStruct>* list = getAllComponents();
	if (!list)
		return "No components registered";
	else if (!list->size()) {
		delete(list);
		return "No components registered";
	}

	std::string str = utils::StringFormat("Components count %u intcount %u indexSize %u maxID %u memsize %u memuse %u\n",
		list->size(), header->count, header->indexSize, header->maxID, header->size, header->usage);
	std::vector<ComponentInfoStruct>::iterator i = list->begin(), e = list->end();
	while (i != e) {
		str += utils::StringFormat("Comp %u: '%s' node %u proc %u type %u, %u params %u data %u runs %u msgIn %u msgOut\n",
			(*i).id, (*i).name, (*i).nodeID, (*i).procID, (*i).type, (*i).paramCount, (*i).dataCount,
			(*i).stats.runCount, (*i).stats.msgInCount, (*i).stats.msgOutCount);
		i++;
	}

	delete(list);
	return str;
}


std::string ComponentMemory::toXML(bool stats) {
	std::vector<ComponentInfoStruct>* list = getAllComponents();
	if (!list)
		return "<components count=\"0\" />\n";
	else if (!list->size()) {
		delete(list);
		return "<components count=\"0\" />\n";
	}

	std::string str = utils::StringFormat("<components count=\"%u\" indexsize=\"%u\" maxid=\"%u\" memsize=\"%u\" memuse=\"%u\">\n",
		header->count, header->indexSize, header->maxID, header->size, header->usage);
	std::vector<ComponentInfoStruct>::iterator i = list->begin(), e = list->end();
	while (i != e) {
		str += (*i).toXML(stats);
		i++;
	}
	delete(list);
	str += "</components>\n";
	return str;
}


std::vector<ComponentInfoStruct>* ComponentMemory::getAllComponents() {
	std::vector<ComponentInfoStruct>* list = new std::vector<ComponentInfoStruct>;
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return list;
	CHECKCOMPONENTMEMORYSERIAL

	ComponentInfoStruct* info;
	for (uint32 n=0; n<=header->maxID; n++) {
		if (info = GETCOMPDATA(header, n))
			list->push_back(*info);
	}
	mutex->leave();
	return list;
}

std::vector<ComponentInfoStruct>* ComponentMemory::getAllLocalComponents() {
	std::vector<ComponentInfoStruct>* list = new std::vector<ComponentInfoStruct>;
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return list;
	CHECKCOMPONENTMEMORYSERIAL

	ComponentInfoStruct* info;
	for (uint32 n=0; n<=header->maxID; n++) {
		if ((info = GETCOMPDATA(header, n)) && (info->nodeID == master->getNodeID()))
			list->push_back(*info);
	}
	mutex->leave();
	return list;
}

std::vector<ComponentInfoStruct>* ComponentMemory::getAllLocalComponents(uint8 type) {
	std::vector<ComponentInfoStruct>* list = new std::vector<ComponentInfoStruct>;
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return list;
	CHECKCOMPONENTMEMORYSERIAL

	ComponentInfoStruct* info;
	for (uint32 n=0; n<=header->maxID; n++) {
		if ((info = GETCOMPDATA(header, n)) && (info->nodeID == master->getNodeID()) && (info->type == type))
			list->push_back(*info);
	}
	mutex->leave();
	return list;
}

std::vector<ComponentInfoStruct>* ComponentMemory::getAllModules() {
	std::vector<ComponentInfoStruct>* list = new std::vector<ComponentInfoStruct>;
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return list;
	CHECKCOMPONENTMEMORYSERIAL

	ComponentInfoStruct* info;
	for (uint32 n=0; n<=header->maxID; n++) {
		if ((info = GETCOMPDATA(header, n)) && (info->nodeID == master->getNodeID())
			&& ( (info->type == COMP_INTERNAL) || (info->type == COMP_EXTERNAL) ) )
			list->push_back(*info);
	}
	mutex->leave();
	return list;
}

std::vector<ComponentInfoStruct>* ComponentMemory::getAllCatalogs() {
	std::vector<ComponentInfoStruct>* list = new std::vector<ComponentInfoStruct>;
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return list;
	CHECKCOMPONENTMEMORYSERIAL

	ComponentInfoStruct* info;
	for (uint32 n=0; n<=header->maxID; n++) {
		if ((info = GETCOMPDATA(header, n)) && (info->nodeID == master->getNodeID())
			&& ( (info->type == COMP_WHITEBOARD) || (info->type == COMP_CATALOG) ) )
			list->push_back(*info);
	}
	mutex->leave();
	return list;
}

bool ComponentMemory::create(uint32 indexSize) {
	serial = master->incrementComponentShmemSerial();
	port = master->port;

	mutex = new utils::Mutex(utils::StringFormat("PsycloneComponentMemoryMutex_%u", port).c_str(), true);
	if (!mutex->enter(5000, __FUNCTION__))
		return false;
	
	// add initial 256b average per component for parameters and data.
	memorySize = sizeof(ComponentMemoryStruct) + indexSize * (sizeof(uint64) + sizeof(ComponentInfoStruct) + 256);

	//header = (ComponentMemoryStruct*) utils::OpenSharedMemorySegment(utils::StringFormat("PsycloneComponentMemory_%u_%u", port, serial).c_str(), memorySize);
	//if (header) {
	//	utils::CloseSharedMemorySegment((char*)header, memorySize);
	//	LogPrint(0,LOG_SYSTEM,2,"ComponentMemory removing stale shared memory (%u/%u)...", port, serial);
	//}

	header = (ComponentMemoryStruct*) utils::CreateSharedMemorySegment(utils::StringFormat("PsycloneComponentMemory_%u_%u", port, serial).c_str(), memorySize, true);
	if (!header) {
		mutex->leave();
		return false;
	}
	memset(header, 0, (size_t)memorySize);
	header->size = memorySize;
	header->cid = COMPONENTMEMORYID;
	header->createdTime = GetTimeNow();
	header->indexSize = indexSize;
	header->usage = sizeof(ComponentMemoryStruct) + (indexSize * sizeof(uint64));
	// this->port = port;
	master->setComponentShmemSize(memorySize);
	mutex->leave();
	return true;
}

bool ComponentMemory::open() {
	serial = master->getComponentShmemSerial();
	uint64 size = master->getComponentShmemSize();
	this->port = master->port;

	bool createdMutex = false;
	if (!mutex) {
		mutex = new utils::Mutex(utils::StringFormat("PsycloneComponentMemoryMutex_%u", port).c_str());
		createdMutex = true;
		if (!mutex->enter(5000, __FUNCTION__))
			return false;
	}
	
	ComponentMemoryStruct* newHeader = (ComponentMemoryStruct*) utils::OpenSharedMemorySegment(utils::StringFormat("PsycloneComponentMemory_%u_%u", port, serial).c_str(), size);
	if (!newHeader) {
		if (createdMutex)
			mutex->leave();
		return false;
	}
	if (newHeader->cid != COMPONENTMEMORYID) {
		utils::CloseSharedMemorySegment((char*)newHeader, size);
		if (createdMutex)
			mutex->leave();
		return false;
	}

	memorySize = size;
	if (header) 
		utils::CloseSharedMemorySegment((char*)header, header->size);
	header = newHeader;
	if (createdMutex)
		mutex->leave();
	// else leave mutex locked as we are calling from within the object
	return true;
}




bool ComponentMemory::resize(uint64 newMemorySize) {

	if (newMemorySize > 100000000) {
		LogPrint(0,LOG_MEMORY,0,"Memory Error: Component Memory requested size %s denied...", utils::BytifySize((double)newMemorySize).c_str());
		return false;
	}
	else if (newMemorySize > 50000000) {
		LogPrint(0,LOG_MEMORY,1,"Memory Warning: Component Memory now %s...", utils::BytifySize((double)newMemorySize).c_str());
	}

	serial = master->incrementComponentShmemSerial();

	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;

	//ComponentMemoryStruct* newHeader = (ComponentMemoryStruct*) utils::OpenSharedMemorySegment(utils::StringFormat("PsycloneComponentMemory_%u_%u", port, serial).c_str(), newMemorySize);
	//if (newHeader) {
	//	utils::CloseSharedMemorySegment((char*)newHeader, newMemorySize);
	//	LogPrint(0,LOG_MEMORY,2,"ComponentMemory removing stale shared memory (%u/%u)...", port, serial);
	//}

	ComponentMemoryStruct* newHeader = (ComponentMemoryStruct*) utils::CreateSharedMemorySegment(utils::StringFormat("PsycloneComponentMemory_%u_%u", port, serial).c_str(), newMemorySize, true);
	if (!newHeader) {
		mutex->leave();
		return false;
	}
	memcpy(newHeader, header, (size_t)header->usage);
	newHeader->size = newMemorySize;

	utils::CloseSharedMemorySegment((char*)header, memorySize);
	header = newHeader;
	memorySize = newMemorySize;
	master->setComponentShmemSize(memorySize);
//	utils::Sleep(10);
	mutex->leave();
	return true;
}

bool ComponentMemory::resizeIndex(uint32 newIndexSize) {

	int32 difSize = (newIndexSize - header->indexSize) * sizeof(uint64);
	if (difSize <= 0)
		return true;
	if ((uint32)difSize > header->size - header->usage)
		if (!resize(memorySize * 2))
			return false;

	//LogPrint(0, 0, 0, "Component list before resizeIndex\n%s...", printFriendly().c_str());

	uint32 oldDataOffset = sizeof(ComponentMemoryStruct) + (header->indexSize*sizeof(uint64));
	uint32 newDataOffset = sizeof(ComponentMemoryStruct) + (newIndexSize*sizeof(uint64));
	memmove(((char*)header) + newDataOffset, ((char*)header) + oldDataOffset, (size_t)(header->usage - oldDataOffset));
	memset(((char*)header) + oldDataOffset, 0, newDataOffset - oldDataOffset);

	header->indexSize = newIndexSize;
	header->usage += newDataOffset - oldDataOffset;

	//LogPrint(0, 0, 0, "Component list after resizeIndex\n%s...", printFriendly().c_str());
	return true;
}

bool ComponentMemory::growComponent(uint32 id, uint64 addSize) {
	ComponentInfoStruct* info = GETCOMPDATA(header,id);
	if (!info)
		return false;
	return resizeComponent(id, info->size + addSize);
}

bool ComponentMemory::resizeComponent(uint32 id, uint64 newSize) {
	ComponentInfoStruct* info = NULL;

	uint64 difSize;
	uint64* index = GETCOMPOFFSETP(header,id);
	if (!*index) {
		// we are inserting a new component
		difSize = newSize;
	}
	else {
		info = GETCOMPDATA(header,id);
		if (info->id != id)
			return false;
		if (info->size > newSize)
			return true;
		difSize = newSize - info->size;
	}
	if (header->size - header->usage < difSize) {
		if (!resize(memorySize * 2))
			return false;
		return resizeComponent(id, newSize);
	}

	uint32 nextID = 0;
	uint32 n;
	uint64* i = index + 1;
	for (n=id+1; n<=header->maxID; n++, i++) {
		if (*i) {
			nextID = n;
			break;
		}
	}

	// shift qID > id up by difsize
	if (nextID) {
		// Push every subsequent component up by difSize
		uint64 oldOffset = *i;
		char* data = (char*)GETCOMPDATA(header,nextID);
		if (!data) {
			LogPrint(0, LOG_MEMORY, 0, "Couldn't shift component %u memory when inserting component %u...", nextID, id);
			return false;
		}
		// Bytes from nextID's block to the end of used data. Offsets are relative
		// to (header + indexSize*8) while usage is header-relative, so convert the
		// offset before subtracting (the old code over-copied by indexSize*8 bytes,
		// which could write past the segment end when nearly full).
		uint64 nextStart = (header->indexSize * sizeof(uint64)) + oldOffset;
		uint64 sizeMove = header->usage - nextStart;
		memmove(data+difSize, data, (size_t)sizeMove);
		// Adjust indexes
		for (n=nextID; n<=header->maxID; n++, i++) {
			if (*i) {
				// printf("Moving index %u offset %u '%s' by %u\n", n, *i, (GETCOMPDATA(header, n))->name, difSize);
				*i += difSize;
			}
		}

		if (!info) {
			GETCOMPOFFSET(header, id) = oldOffset;
			info = (ComponentInfoStruct*) data;
			memset(info, 0, sizeof(ComponentInfoStruct));
			info->id = id;
			info->createdTime = info->lastUpdateTime = GetTimeNow();
		}

	}
	else if (!info) {
		GETCOMPOFFSET(header,id) = header->usage - (header->indexSize * sizeof(uint64));
		info = GETCOMPDATA(header, id);
		memset(info, 0, sizeof(ComponentInfoStruct));
		info->id = id;
		info->createdTime = info->lastUpdateTime = GetTimeNow();
	}

	info->size = newSize;
	header->usage += difSize;
	return true;
}









// ************************************************************************************************

bool ComponentMemory::confirmComponentID(uint32 id) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL
	if (id >= header->indexSize) {
		mutex->leave();
		return false;
	}
	ComponentInfoStruct* info = GETCOMPDATA(header, id);
	if (!info || (info->syncStatus < 1)) {
		mutex->leave();
		return false;
	}
	if (!info->type)
		info->syncStatus = 2;
	else
		info->syncStatus = 3;
	mutex->leave();
	return true;
}

bool ComponentMemory::cancelComponentID(uint32 id) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL
	if (id >= header->indexSize) {
		mutex->leave();
		return false;
	}
	ComponentInfoStruct* info = GETCOMPDATA(header, id);
	if (!info || (info->syncStatus != 1)) {
		mutex->leave();
		return false;
	}
	info->syncStatus = 0;
	info->type = 0;
	info->nodeID = 0;
	info->procID = 0;
	info->createdTime = 0;
	mutex->leave();
	return true;
}

bool ComponentMemory::writeComponentNamesToMsg(DataMessage* msg) {
	std::vector<ComponentInfoStruct>* list = getAllComponents();
	if (!list)
		return true;
	else if (!list->size()) {
		delete(list);
		return true;
	}

	std::vector<ComponentInfoStruct>::iterator i = list->begin(), e = list->end();
	while (i != e) {
		msg->setString((*i).id, "component", (*i).name);
		i++;
	}
	delete(list);
	return true;
}

bool ComponentMemory::writeComponentsToMsg(DataMessage* msg) {
	std::vector<ComponentInfoStruct>* list = getAllComponents();
	if (!list)
		return true;
	else if (!list->size()) {
		delete(list);
		return true;
	}

	uint32 n = 0;
	std::vector<ComponentInfoStruct>::iterator i = list->begin(), e = list->end();
	while (i != e) {
		if (!(*i).writeToMsg(n, msg)) {
			delete(list);
			return false;
		}
		i++;
		n++;
	}
	delete(list);
	return true;
}

bool ComponentMemory::syncComponents(DataMessage* msg) {
	int64 n = 0;
	uint16 id, nodeID, procID;
	uint8 type, policy, selfTrigger;
	const char* name;
	uint64 time, size;
	uint32 exID;
	while (
		(id = (uint16)msg->getInt(n, "ID")) &&
		(type = (uint8)msg->getInt(n, "Type")) &&
		(size = (uint64)msg->getInt(n, "Size")) &&
		(nodeID = (uint16)msg->getInt(n, "NodeID")) &&
		(procID = (uint16)msg->getInt(n, "ProcID")) &&
		(name = msg->getString(n, "Name")) &&
		(time = msg->getTime(n, "CreatedTime"))) {
			policy = (uint8)msg->getInt(n, "Policy");
			selfTrigger = (uint8)msg->getInt(n, "SelfTrigger");
			if (!createComponent(id, type, policy, selfTrigger, name, size, nodeID, procID, time, exID))
				return false;
			confirmComponentID(id);
			n++;
	}
	return true;
}

// Static
bool ComponentMemory::createComponent(uint32 id, uint8 type, uint8 policy, uint8 selfTrigger, const char* name, uint64 size, uint16 nodeID, uint16 procID, uint64 time, uint32& existingID) {

	existingID = 0;
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (getComponentID(name, existingID) && (id != existingID)) {
		mutex->leave();
		return false;
	}

	if (id >= header->indexSize)
		resizeIndex(header->indexSize + id + 1024);

	ComponentInfoStruct* info = GETCOMPDATA(header, id);

	if (info) {
		if (master->getNodeID() == nodeID)
			LogPrint(0, LOG_MEMORY, 1, "Updating local component %u (proc %u)...", id, procID);
		else
			LogPrint(0, LOG_MEMORY, 1, "Updating remote component %u (node %u proc %u)...", id, nodeID, procID);
		if ((stricmp(name, info->name) != 0) ||
			((nodeID != info->nodeID) && (info->nodeID)) ||
			((procID != info->procID) && (info->procID)) ) {
				mutex->leave();
				return false;
		}
		info->type = type;
		info->policy = policy;
		info->selfTrigger = selfTrigger;
		info->nodeID = nodeID;
		info->procID = procID;
		if (size > info->size)
			if (!resizeComponent(id, size + sizeof(ComponentInfoStruct))) {
				mutex->leave();
				return false;
			}
	}
	else {
		if (master->getNodeID() == nodeID)
			LogPrint(0, LOG_MEMORY, 2, "Creating new local component %u (proc %u)...", id, procID);
		else
			LogPrint(0, LOG_MEMORY, 2, "Registering remote component %u (node %u proc %u)...", id, nodeID, procID);
		if (!resizeComponent(id, size + sizeof(ComponentInfoStruct))) {
			mutex->leave();
			return false;
		}
		info = GETCOMPDATA(header, id);
		info->type = type;
		info->policy = policy;
		info->selfTrigger = selfTrigger;
		info->nodeID = nodeID;
		info->procID = procID;
		if (time)
			info->createdTime = time;
		else
			info->createdTime = GetTimeNow();
		header->count++;
		utils::strcpyavail(info->name, name, MAXKEYNAMELEN-1, false);
	}

	if (id > header->maxID)
		header->maxID = id;

	if (info->syncStatus < 1)
		info->syncStatus = 1;

	// LogPrint(0, 0, 0, "New component list\n%s...", printFriendly().c_str());

	// Do not unlock the data, ComponentData destructor will do this
	mutex->leave();
	return true;
}

// static
bool ComponentMemory::addComponentStats(uint32 cid, uint8 status, uint64 usageCPUTicks, DataMessage* inputMsg,
		DataMessage* outputMsg, uint32 runCount, uint32 cycleCount) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	uint32 size = 0;
	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return false;
	}

	info->status = status;
	info->stats.time = GetTimeNow();
	if (status == COMPSTATUS_STARTING) {
		info->stats.currentRunStartTime = info->stats.time;
		if (!info->stats.firstRunStartTime)
			info->stats.firstRunStartTime = info->stats.time;
	}
	info->stats.cycleCount += cycleCount;
	info->stats.runCount += runCount;
	info->stats.usageCPUTicks += usageCPUTicks;
	DataMessage* draft;
	if (inputMsg) {
		info->stats.msgInCount++;
		info->stats.msgInBytes += inputMsg->getSize();
		// Circular ring: write the newest draft to the head slot and advance the
		// head, instead of memmove-shifting the whole 10-deep ring (9*DRAFTMSGSIZE
		// = 36KB) down by one slot on every message. Readers (toXML, PsyProbe)
		// iterate all slots, so slot order doesn't matter to them.
		char* inSlot = info->stats.recentInMsg + ((info->stats.recentInPos % 10) * DRAFTMSGSIZE);
		if (inputMsg->data->size < DRAFTMSGSIZE)
			memcpy(inSlot, inputMsg->data, inputMsg->data->size);
		else {
			draft = new DataMessage(*inputMsg, DRAFTMSGSIZE);
			memcpy(inSlot, draft->data, draft->data->size);
			delete(draft);
		}
		info->stats.recentInPos = (info->stats.recentInPos + 1) % 10;
	}
	if (outputMsg) {
		info->stats.msgOutCount++;
		info->stats.msgOutBytes += outputMsg->getSize();
		char* outSlot = info->stats.recentOutMsg + ((info->stats.recentOutPos % 10) * DRAFTMSGSIZE);
		if (outputMsg->data->size < DRAFTMSGSIZE)
			memcpy(outSlot, outputMsg->data, outputMsg->data->size);
		else {
			draft = new DataMessage(*outputMsg, DRAFTMSGSIZE);
			memcpy(outSlot, draft->data, draft->data->size);
			delete(draft);
		}
		info->stats.recentOutPos = (info->stats.recentOutPos + 1) % 10;
	}

	mutex->leave();
	return true;
}


// Static
bool ComponentMemory::destroyComponent(uint32 cid) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	uint64* index = GETCOMPOFFSETP(header, cid);
	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return false;
	}
	uint64 size = info->size;

	// Check if any data comes after this component; if so, shift it down to
	// reclaim this component's block and fix up the affected offsets.
	if (cid < header->maxID) {
		uint32 nextID = 0;
		// Iterate the index slots for ids AFTER cid (start at cid+1, not cid).
		uint64* i = GETCOMPOFFSETP(header, cid + 1);
		for (uint32 n=cid+1; n<=header->maxID; n++, i++) {
			if (*i) {
				if (!nextID) {
					nextID = n;
					// Bytes from the next component's block to the end of used data.
					// Offsets are relative to (header + indexSize*8) while usage is
					// header-relative, so convert the offset before subtracting.
					uint64 nextStart = (header->indexSize * sizeof(uint64)) + *i;
					memmove(info, ((char*)info)+size, (size_t)(header->usage - nextStart));
				}
				*i -= size;
			}
		}
	}

	// Free this component's own slot (index still points at cid's slot).
	*index = 0;
	header->usage -= size;
	header->count--;
	
	mutex->leave();
	return true;
}

bool ComponentMemory::updateComponentLocation(uint32 cid, uint16 nodeID, uint16 procID) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return false;
	}

	info->nodeID = nodeID;
	info->procID = procID;
	mutex->leave();
	return true;
}

char* ComponentMemory::getComponentData(uint32 cid, uint64& size) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return NULL;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return NULL;
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return NULL;
	}

	size = info->size;
	char* data = new char[(size_t)size];
	memcpy(data, info, (size_t)size);

	mutex->leave();
	return data;
}

bool ComponentMemory::setComponentData(uint32 cid, const char* data, uint64 size, bool wasMigrated) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize)
		resizeIndex(header->indexSize + 256);

	if (!resizeComponent(cid, size)) {
		mutex->leave();
		return false;
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return false;
	}

	uint64 oldsize = info->size;

	memcpy(info, data, (size_t)size);
	info->size = oldsize;

	if (cid > header->maxID)
		header->maxID = cid;

	if (wasMigrated)
		info->stats.migrationCount++;

	// Do not unlock the data, ComponentData destructor will do this
	mutex->leave();
	return true;
}


std::list<uint16>* ComponentMemory::findProcessComponents(uint16 procID) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return NULL;
	CHECKCOMPONENTMEMORYSERIAL

	ComponentInfoStruct* info;
	uint16 localNodeID = master->getNodeID();
	std::list<uint16>* comps = new std::list<uint16>;
	for (uint32 n=0; n<=header->maxID; n++) {
		if (info = GETCOMPDATA(header, n)) {
			if ((info->nodeID == localNodeID) && (info->procID == procID))
				comps->push_back(info->id);
		}
	}
	mutex->leave();
	return comps;
}

bool ComponentMemory::canComponentMigrate(uint32 cid) {
	return (getComponentPolicy(cid) & COMPONENT_CAN_MIGRATE);
}

uint8 ComponentMemory::getComponentPolicy(uint32 cid) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return 0;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return 0;
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return 0;
	}

	uint8 policy = info->policy;
	mutex->leave();
	return policy;
}

bool ComponentMemory::updateComponentInformation(uint32 cid, uint8 type, uint8 policy, uint8 selfTrigger, uint16 nodeID, uint16 procID) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return false;
	}

	info->policy = policy;
	info->selfTrigger = selfTrigger;
	info->nodeID = nodeID;
	info->procID = procID;
	info->type = type;
	if (info->syncStatus == 2)
		info->syncStatus = 3;
	mutex->leave();
	return true;
}

bool ComponentMemory::setComponentPolicy(uint32 cid, uint8 policy) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return false;
	}

	info->policy = policy;
	mutex->leave();
	return true;
}

uint8 ComponentMemory::getComponentSelfTrigger(uint32 cid) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return 0;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return 0;
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return 0;
	}

	uint8 selfTrigger = info->selfTrigger;
	mutex->leave();
	return selfTrigger;
}

bool ComponentMemory::setComponentSelfTrigger(uint32 cid, uint8 selfTrigger) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return false;
	}

	info->selfTrigger = selfTrigger;
	mutex->leave();
	return true;
}


bool ComponentMemory::getComponentLocation(uint32 cid, uint16& nodeID, uint16& procID) {
	nodeID = 0;
	procID = 0;
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return false;
	}

	nodeID = info->nodeID;
	procID = info->procID;
	mutex->leave();
	return true;
}

// static
uint16 ComponentMemory::getComponentNodeID(uint32 cid) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return 0;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return 0;
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return 0;
	}

	uint16 nodeID = info->nodeID;
	mutex->leave();
	return nodeID;
}

// static
uint16 ComponentMemory::getComponentProcessID(uint32 cid) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return 0;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return 0;
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return 0;
	}
	
	uint16 procID = info->procID;
	mutex->leave();
	return procID;
}


// static
bool ComponentMemory::isComponentLocal(uint32 cid) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return 0;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return false;
	}
	
	if (master->getNodeID() == info->nodeID) {
		mutex->leave();
		return true;
	}
	else {
		mutex->leave();
		return false;
	}
}

// static
bool ComponentMemory::isComponentLocal(const char* name) {
	uint32 cid = 0;
	if (getComponentID(name, cid) && cid)
		return isComponentLocal(cid);
	else
		return false;
}

std::string ComponentMemory::getComponentNameString(uint32 cid) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return "";
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return "";
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return "";
	}
	
	std::string str = info->name;
	mutex->leave();
	return str;
}

// static
bool ComponentMemory::getComponentName(uint32 cid, char* name, uint32 maxNameLen) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return false;
	}
	
	utils::strcpyavail(name, info->name, maxNameLen, true);
	mutex->leave();
	return true;
}

uint8 ComponentMemory::getComponentType(uint32 cid) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return 0;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return 0;
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return 0;
	}

	uint8 type = info->type;
	mutex->leave();
	return type;
}

// static
ComponentStats ComponentMemory::getComponentStats(uint32 cid) {
	ComponentStats stats;
	memset(&stats, 0, sizeof(ComponentStats));

	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return stats;
	if (serial != master->getComponentShmemSerial()) {if (!open()) {mutex->leave();return stats;}}

	if (cid >= header->indexSize) {
		mutex->leave();
		return stats;
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return stats;
	}
	
	memcpy(&stats, &info->stats, sizeof(ComponentStats));
	mutex->leave();
	return stats;
}

AveragePerfStats ComponentMemory::getComponentPerfStats(uint32 cid) {
	AveragePerfStats perfStats;
	memset(&perfStats, 0, sizeof(AveragePerfStats));

	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return perfStats;
	if (serial != master->getComponentShmemSerial()) {if (!open()) {mutex->leave();return perfStats;}}

	if (cid >= header->indexSize) {
		mutex->leave();
		return perfStats;
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return perfStats;
	}
	
	memcpy(&perfStats, &info->perfStats, sizeof(AveragePerfStats));
	mutex->leave();
	return perfStats;
}

bool ComponentMemory::setComponentPerfStats(uint32 cid, AveragePerfStats &perfStruct) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return false;
	}
	
	memcpy(&info->perfStats, &perfStruct, sizeof(AveragePerfStats));
	mutex->leave();
	return true;
}

// static
bool ComponentMemory::setComponentStats(uint32 cid, ComponentStats& stats){
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return false;
	}
	
	memcpy(&info->stats, &stats, sizeof(ComponentStats));
	mutex->leave();
	return true;
}

// static
bool ComponentMemory::getComponentID(const char* name, uint32 &cid) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__) || !name || !strlen(name))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	ComponentInfoStruct* info;
	for (uint32 n=0; n<=header->maxID; n++) {
		if ((info = GETCOMPDATA(header, n)) && (info->syncStatus > 1)) {
			if (stricmp(info->name, name) == 0) {
				cid = n;
				mutex->leave();
				return true;
			}
		}
	}
	mutex->leave();
	return false;
}

// static
uint8 ComponentMemory::lookupComponentID(const char* name, uint32 &cid) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__) || !name || !strlen(name))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	uint8 syncStatus;
	ComponentInfoStruct* info;
	for (uint32 n=0; n<=header->maxID; n++) {
		if ((info = GETCOMPDATA(header, n)) && (info->syncStatus > 0)) {
			if (stricmp(info->name, name) == 0) {
				cid = n;
				syncStatus = info->syncStatus;
				mutex->leave();
				return syncStatus;
			}
		}
	}
	mutex->leave();
	return 0;
}



char* ComponentMemory::makeRoomForPrivateData(uint32 cid, uint64 newSize) {
	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info)
		return NULL;
	if (info->size - COMPINFOUSAGE(info) < newSize) {
		if (!growComponent(cid, newSize))
			return NULL;
		if (!(info = GETCOMPDATA(header, cid)))
			return NULL;
	}
	return ((char*)info) + sizeof(ComponentInfoStruct) + info->paramSize + info->dataSize;
}

char* ComponentMemory::makeRoomForParameter(uint32 cid, uint64 newSize) {
	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info)
		return NULL;
	if (info->size - COMPINFOUSAGE(info) < newSize) {
		if (!growComponent(cid, newSize))
			return NULL;
		if (!(info = GETCOMPDATA(header, cid)))
			return NULL;
	}

	// move private data
	char* paramSrc = (char*)info + sizeof(ComponentInfoStruct);
	char* dataSrc = paramSrc + info->paramSize;
	// space available after private data, so we need to move the private data down
	// to make a gap btw this and the parameter data
	memcpy(dataSrc+newSize, dataSrc, (size_t)info->dataSize);
	// fill the gap with 0s
	memset(dataSrc, 0, (size_t)newSize);
	// return pointer to the new gap
	return dataSrc;
}

ParamHeader* ComponentMemory::getParameter(uint32 cid, const char* name) {
	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info)
		return NULL;

	char* src = (char*)info + sizeof(ComponentInfoStruct);
	char* srcEnd = src + info->paramSize;
	
	uint32 offset = 0;
	while ( src < srcEnd ) {
		if (stricmp(name, src + sizeof(ParamHeader)) == 0)
			return (ParamHeader*)src;
		offset += *(uint32*)src;
		src += *(uint32*)src;
	}
	return NULL;
}

PrivateHeader* ComponentMemory::getPrivateData(uint32 cid, const char* name) {
	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info)
		return NULL;

	char* src = (char*)info + sizeof(ComponentInfoStruct) + info->paramSize;
	char* srcEnd = src + info->dataSize;
	
	while ( src < srcEnd ) {
		if (stricmp(name, src + sizeof(PrivateHeader)) == 0)
			return (PrivateHeader*)src;
		src += *(uint32*)src;
	}
	return NULL;
}


// Private Data
bool ComponentMemory::setPrivateData(uint32 cid, const char* name, const char* data, uint64 size, const char* mimetype) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	std::string actualMimetype;

	uint32 nameLen = (uint32)strlen(name);
	uint32 mimeLen = 0;
	if (mimetype) {
		if (stricmp(mimetype, "xml") == 0)
			actualMimetype = "text/xml";
		else if (stricmp(mimetype, "json") == 0)
			actualMimetype = "application/json";
		else if (stricmp(mimetype, "binary") == 0)
			actualMimetype = "application/octet-stream";
		else if (stricmp(mimetype, "html") == 0)
			actualMimetype = "text/html";
		else if (stricmp(mimetype, "bitmap") == 0)
			actualMimetype = "image/bmp";
		else if (stricmp(mimetype, "text") == 0)
			actualMimetype = "text/plain";
		else
			actualMimetype = mimetype;
		mimeLen = (uint32)actualMimetype.length();
	}
	uint64 totalSize = sizeof(PrivateHeader) + nameLen + 1 + mimeLen + 1 + size;

	PrivateHeader* priv = getPrivateData(cid, name);

	if (priv) {
		if (priv->size == totalSize) {
			if (mimeLen)
				memcpy(((char*)priv) + sizeof(PrivateHeader) + nameLen + 1, actualMimetype.c_str(), (size_t)mimeLen + 1);
			else
				memset(((char*)priv) + sizeof(PrivateHeader) + nameLen + 1, 0, 1); // Just the 0
			memcpy(((char*)priv) + sizeof(PrivateHeader) + nameLen + 1 + mimeLen + 1, data, (size_t)size);

			//printf("\n$$$$$$$$$$$$$$$$$$$$ ResetData: '%s' [%s]: '%s'\n\n",
			//	((char*)priv + sizeof(PrivateHeader)),
			//	((char*)priv) + sizeof(PrivateHeader) + nameLen + 1,
			//	((char*)priv) + sizeof(PrivateHeader) + nameLen + 1 + mimeLen + 1);

			mutex->leave();
			return true;
		}
		else {
			deletePrivateData(cid, name);
		}
	}

	char* newData = makeRoomForPrivateData(cid, totalSize);
	if (newData == NULL) {
		mutex->leave();
		return false;
	}

	// Copy in new data
	*(uint64*) newData = totalSize;
	memcpy(newData + sizeof(PrivateHeader), name, nameLen+1); // incl. 0 at the end of the string
	if (mimeLen)
		memcpy(newData + sizeof(PrivateHeader) + nameLen + 1, actualMimetype.c_str(), mimeLen + 1); // incl. 0 at the end of the string
	else
		memset(newData + sizeof(PrivateHeader) + nameLen + 1, 0, 1); // Just the 0
	memcpy(newData + sizeof(PrivateHeader) + nameLen+1 + mimeLen + 1, data, (size_t)size);

	//printf("\n������������������� SetData: '%s' [%s]: '%s'\n\n",
	//	newData + sizeof(PrivateHeader),
	//	newData + sizeof(PrivateHeader) + nameLen + 1,
	//	newData + sizeof(PrivateHeader) + nameLen + 1 + mimeLen + 1);

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	info->dataCount++;
	info->dataSize += totalSize;
	mutex->leave();
	return true;
}

uint64 ComponentMemory::getPrivateDataSize(uint32 cid, const char* name) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return 0;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return 0;
	}

	PrivateHeader* priv = getPrivateData(cid, name);
	if (!priv) {
		mutex->leave();
		return 0;
	}

	uint32 nameLen = (uint32)strlen(((char*)priv + sizeof(PrivateHeader)));
	uint32 mimeLen = (uint32)strlen(((char*)priv + sizeof(PrivateHeader) + nameLen+1));

	uint64 size = priv->size - sizeof(PrivateHeader) - nameLen - mimeLen - 2;
	mutex->leave();
	return size;
}

char* ComponentMemory::getPrivateDataCopy(uint32 cid, const char* name, uint64 &size) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return NULL;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return NULL;
	}

	PrivateHeader* priv = getPrivateData(cid, name);
	if (!priv) {
		mutex->leave();
		return NULL;
	}

	uint32 nameLen = (uint32)strlen(((char*)priv + sizeof(PrivateHeader)));
	uint32 mimeLen = (uint32)strlen(((char*)priv + sizeof(PrivateHeader) + nameLen + 1));

	uint64 offset = sizeof(PrivateHeader) + nameLen + mimeLen + 2;
	size = priv->size - offset;
	if (!size) {
		mutex->leave();
		return NULL;
	}
	char* data = new char[(size_t)size];
	memcpy((void*)data, ((char*)priv) + offset, (size_t)size);
	mutex->leave();
	return data;
}

bool ComponentMemory::getPrivateData(uint32 cid, const char* name, char* data, uint64 maxSize) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	PrivateHeader* priv = getPrivateData(cid, name);
	if (!priv) {
		mutex->leave();
		return false;
	}

	uint32 nameLen = (uint32)strlen(((char*)priv + sizeof(PrivateHeader)));
	uint32 mimeLen = (uint32)strlen(((char*)priv + sizeof(PrivateHeader) + nameLen + 1));

	uint64 offset = sizeof(PrivateHeader) + nameLen + mimeLen + 2;
	uint64 size = priv->size - offset;
	if (size > maxSize) {
		mutex->leave();
		return false;
	}
	memcpy((void*)data, ((char*)priv) + offset, (size_t)size);
	mutex->leave();
	return true;
}

std::string ComponentMemory::getPrivateDataMimetype(uint32 cid, const char* name) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return "";
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return "";
	}

	PrivateHeader* priv = getPrivateData(cid, name);
	if (!priv) {
		mutex->leave();
		return "";
	}

	uint32 nameLen = (uint32)strlen(((char*)priv + sizeof(PrivateHeader)));
	std::string type = ((char*)priv + sizeof(PrivateHeader) + nameLen + 1);

	mutex->leave();
	return type;
}

std::map<std::string, std::string> ComponentMemory::getPrivateDataKeysAndTypes(uint32 cid) {
	std::map<std::string, std::string> map;

	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return map;
	CHECKCOMPONENTMEMORYSERIALRETURN(map)

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return map;
	}

	char* src = (char*)info + sizeof(ComponentInfoStruct) + info->paramSize;
	char* srcEnd = src + info->dataSize;

	uint32 nameLen;
	uint32 mimeLen;
	PrivateHeader* priv;

	while (src < srcEnd) {
		priv = (PrivateHeader*)src;
		nameLen = (uint32)strlen(((char*)priv + sizeof(PrivateHeader)));
		mimeLen = (uint32)strlen(((char*)priv + sizeof(PrivateHeader) + nameLen + 1));

		if (nameLen) {
			if (mimeLen)
				map[((char*)priv + sizeof(PrivateHeader))] = ((char*)priv + sizeof(PrivateHeader) + nameLen + 1);
			else
				map[((char*)priv + sizeof(PrivateHeader))] = "";
		}

		src += *(uint32*)src;
	}

	mutex->leave();
	return map;
}

std::list<std::string> ComponentMemory::getPrivateDataKeys(uint32 cid) {
	std::list<std::string> list;

	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return list;
	CHECKCOMPONENTMEMORYSERIALRETURN(list)

		ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	if (!info) {
		mutex->leave();
		return list;
	}

	char* src = (char*)info + sizeof(ComponentInfoStruct) + info->paramSize;
	char* srcEnd = src + info->dataSize;

	uint32 nameLen;
	PrivateHeader* priv;

	while (src < srcEnd) {
		priv = (PrivateHeader*)src;
		nameLen = (uint32)strlen(((char*)priv + sizeof(PrivateHeader)));

		if (nameLen)
			list.push_back(((char*)priv + sizeof(PrivateHeader)));

		src += *(uint32*)src;
	}

	mutex->leave();
	return list;
}





bool ComponentMemory::deletePrivateData(uint32 cid, const char* name) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	PrivateHeader* priv = getPrivateData(cid, name);
	if (!priv) {
		mutex->leave();
		return false;
	}

	uint64 delSize = priv->size;
	uint64 moveSize = (uint32)(((char*)info)+COMPINFOUSAGE(info)-((char*)priv)-priv->size);
	memmove(((char*)priv), ((char*)priv)+priv->size, (size_t)moveSize);
	info->dataCount--;
	info->dataSize -= delSize;
	mutex->leave();
	return true;
}










// Parameters
bool ComponentMemory::createParameter(uint32 cid, const char* name, const char* val, const char* defaultValue) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ParamHeader* param = getParameter(cid, name);
	if (param) {
		mutex->leave();
		return false;
	}

	// Calculate required size
	uint32 nameLen = (uint32)strlen(name);
	uint32 newSize = nameLen + 1 + sizeof(ParamHeader) + (uint32)strlen(val) + 1;
	if (defaultValue != NULL)
		newSize += (uint32)strlen(defaultValue) + 1;
	else
		newSize += (uint32)strlen(val) + 1;

	char* newData = makeRoomForParameter(cid, newSize);
	if (newData == NULL) {
		mutex->leave();
		return false;
	}

	// Copy in new data
	param = (ParamHeader*) newData;
	param->size = newSize;
	param->type = PARAM_STRING;
	memcpy(newData + sizeof(ParamHeader), name, nameLen+1); // incl. 0 at the end of the string
	newData += sizeof(ParamHeader) + nameLen + 1;
	// copy in main value
	utils::strcpyavail(newData, val, newSize, true);
	newData += strlen(val) + 1;
	// copy in default value
	if (defaultValue != NULL)
		utils::strcpyavail(newData, defaultValue, newSize, true);
	else
		utils::strcpyavail(newData, val, newSize, true);

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	info->paramCount++;
	info->paramSize += newSize;
	mutex->leave();
	return true;
}

bool ComponentMemory::createParameter(uint32 cid, const char* name, const char* val, uint32 count, uint32 defaultIndex) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ParamHeader* param = getParameter(cid, name);
	if (param) {
		mutex->leave();
		return false;
	}

	// Calculate required size
	uint32 n, len;
	uint32 nameLen = (uint32)strlen(name);
	uint32 newSize = nameLen + 1 + sizeof(ParamHeader) + sizeof(ParamCollectionHeader);
	uint32 dataSize = 0;
	const char* pVal = val;
	for (n=0; n<count; n++) {
		len = (uint32)strlen(pVal) + 1;
		dataSize += len;
		pVal += len;
	}
	newSize += dataSize;

	char* newData = makeRoomForParameter(cid, newSize);
	if (newData == NULL) {
		mutex->leave();
		return false;
	}

	// Copy in new data
	param = (ParamHeader*) newData;
	param->size = newSize;
	param->type = PARAM_STRING_COLL;
	memcpy(newData + sizeof(ParamHeader), name, nameLen+1); // incl. 0 at the end of the string

	ParamCollectionHeader* dHeader = (ParamCollectionHeader*) (newData + sizeof(ParamHeader) + nameLen + 1);
	dHeader->index = defaultIndex;
	dHeader->defaultIndex = defaultIndex;
	dHeader->count = count;

	// copy in data values
	memcpy((char*)dHeader + sizeof(ParamCollectionHeader), val, dataSize);

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	info->paramCount++;
	info->paramSize += newSize;
	mutex->leave();
	return true;
}

bool ComponentMemory::createParameter(uint32 cid, const char* name, std::vector<std::string> values, const char* defaultValue) {
	uint32 n, dlen, len = 0, dataSize = 0, defaultIndex = 0;
	uint32 count = (uint32)values.size();
	for (n=0; n<count; n++)
		dataSize += (uint32)values[n].size() + 1;
	char* val = new char[dataSize];
	for (n=0; n<count; n++) {
		dlen = (uint32)values[n].size()+1;
		memcpy(val+len, values[n].c_str(), dlen);
		len += dlen;
		if (values[n] == defaultValue)
			defaultIndex = n;
	}
	bool res = createParameter(cid, name, val, count, defaultIndex);
	delete [] val;
	return res;
}

bool ComponentMemory::createParameter(uint32 cid, const char* name, std::vector<std::string> values, int64 defaultValue) {
	uint32 n, defaultIndex = 0;
	uint32 count = (uint32)values.size();
	int64* val = new int64[count];
	for (n=0; n<count; n++) {
		val[n] = utils::Ascii2Int64(values[n].c_str());
		if (val[n] == defaultValue)
			defaultIndex = n;
	}
	bool res = createParameter(cid, name, val, count, defaultIndex);
	delete [] val;
	return res;
}

bool ComponentMemory::createParameter(uint32 cid, const char* name, std::vector<int64> values, int64 defaultValue) {
	uint32 n, defaultIndex = 0;
	uint32 count = (uint32)values.size();
	int64* val = new int64[count];
	for (n = 0; n<count; n++) {
		val[n] = values[n];
		if (val[n] == defaultValue)
			defaultIndex = n;
	}
	bool res = createParameter(cid, name, val, count, defaultIndex);
	delete[] val;
	return res;
}

bool ComponentMemory::createParameter(uint32 cid, const char* name, std::vector<std::string> values, float64 defaultValue) {
	uint32 n, defaultIndex = 0;
	uint32 count = (uint32)values.size();
	float64* val = new float64[count];
	for (n=0; n<count; n++) {
		val[n] = utils::Ascii2Float64(values[n].c_str());
		if (val[n] == defaultValue)
			defaultIndex = n;
	}
	bool res = createParameter(cid, name, val, count, defaultIndex);
	delete [] val;
	return res;
}

bool ComponentMemory::createParameter(uint32 cid, const char* name, std::vector<float64> values, float64 defaultValue) {
	uint32 n, defaultIndex = 0;
	uint32 count = (uint32)values.size();
	float64* val = new float64[count];
	for (n = 0; n<count; n++) {
		val[n] = values[n];
		if (val[n] == defaultValue)
			defaultIndex = n;
	}
	bool res = createParameter(cid, name, val, count, defaultIndex);
	delete[] val;
	return res;
}

bool ComponentMemory::createParameter(uint32 cid, const char* name, int64* val, uint32 count, uint32 defaultIndex) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ParamHeader* param = getParameter(cid, name);
	if (param) {
		mutex->leave();
		return false;
	}

	// Calculate required size
	uint32 nameLen = (uint32)strlen(name);
	uint32 newSize = nameLen + 1 + sizeof(ParamHeader) + sizeof(ParamCollectionHeader) + (count * sizeof(int64));

	char* newData = makeRoomForParameter(cid, newSize);
	if (newData == NULL) {
		mutex->leave();
		return false;
	}

	// Copy in new data
	ParamHeader* pHeader = (ParamHeader*) newData;
	pHeader->size = newSize;
	pHeader->type = PARAM_INTEGER_COLL;
	memcpy(newData + sizeof(ParamHeader), name, nameLen+1); // incl. 0 at the end of the string

	ParamCollectionHeader* dHeader = (ParamCollectionHeader*) (newData + sizeof(ParamHeader) + nameLen + 1);
	dHeader->index = defaultIndex;
	dHeader->defaultIndex = defaultIndex;
	dHeader->count = count;

	// copy in data values
	memcpy((char*)dHeader + sizeof(ParamCollectionHeader), val, count * sizeof(int64));

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	info->paramCount++;
	info->paramSize += newSize;
	mutex->leave();
	return true;
}

bool ComponentMemory::createParameter(uint32 cid, const char* name, float64* val, uint32 count, uint32 defaultIndex) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ParamHeader* param = getParameter(cid, name);
	if (param) {
		mutex->leave();
		return false;
	}

	// Calculate required size
	uint32 nameLen = (uint32)strlen(name);
	uint32 newSize = nameLen + 1 + sizeof(ParamHeader) + sizeof(ParamCollectionHeader) + (count * sizeof(float64));

	char* newData = makeRoomForParameter(cid, newSize);
	if (newData == NULL) {
		mutex->leave();
		return false;
	}

	// Copy in new data
	ParamHeader* pHeader = (ParamHeader*) newData;
	pHeader->size = newSize;
	pHeader->type = PARAM_FLOAT_COLL;
	memcpy(newData + sizeof(ParamHeader), name, nameLen+1); // incl. 0 at the end of the string

	ParamCollectionHeader* dHeader = (ParamCollectionHeader*) (newData + sizeof(ParamHeader) + nameLen + 1);
	dHeader->index = defaultIndex;
	dHeader->defaultIndex = defaultIndex;
	dHeader->count = count;

	// copy in data values
	memcpy((char*)dHeader + sizeof(ParamCollectionHeader), val, count * sizeof(float64));

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	info->paramCount++;
	info->paramSize += newSize;
	mutex->leave();
	return true;
}

bool ComponentMemory::createParameter(uint32 cid, const char* name, int64 val, int64 min, int64 max, int64 interval) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ParamHeader* param = getParameter(cid, name);
	if (param) {
		mutex->leave();
		return false;
	}

	// Calculate required size
	uint32 nameLen = (uint32)strlen(name);
	uint32 newSize = nameLen + 1 + sizeof(ParamHeader) + sizeof(ParamIntegerHeader);

	char* newData = makeRoomForParameter(cid, newSize);
	if (newData == NULL) {
		mutex->leave();
		return false;
	}

	// Copy in new data
	ParamHeader* pHeader = (ParamHeader*) newData;
	pHeader->size = newSize;
	pHeader->type = PARAM_INTEGER;
	memcpy(newData + sizeof(ParamHeader), name, nameLen+1); // incl. 0 at the end of the string

	ParamIntegerHeader* dHeader = (ParamIntegerHeader*) (newData + sizeof(ParamHeader) + nameLen + 1);

	// copy in values
	dHeader->val = val;
	dHeader->defaultVal = val;
	dHeader->minVal = min;
	dHeader->maxVal = max;
	dHeader->interval = interval;

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	info->paramCount++;
	info->paramSize += newSize;
	mutex->leave();
	return true;
}

bool ComponentMemory::createParameter(uint32 cid, const char* name, float64 val, float64 min, float64 max, float64 interval) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ParamHeader* param = getParameter(cid, name);
	if (param) {
		mutex->leave();
		return false;
	}

	// Calculate required size
	uint32 nameLen = (uint32)strlen(name);
	uint32 newSize = nameLen + 1 + sizeof(ParamHeader) + sizeof(ParamFloatHeader);

	char* newData = makeRoomForParameter(cid, newSize);
	if (newData == NULL) {
		mutex->leave();
		return false;
	}

	// Copy in new data
	ParamHeader* pHeader = (ParamHeader*) newData;
	pHeader->size = newSize;
	pHeader->type = PARAM_FLOAT;
	memcpy(newData + sizeof(ParamHeader), name, nameLen+1); // incl. 0 at the end of the string

	ParamFloatHeader* dHeader = (ParamFloatHeader*) (newData + sizeof(ParamHeader) + nameLen + 1);

	// copy in values
	dHeader->val = val;
	dHeader->defaultVal = val;
	dHeader->minVal = min;
	dHeader->maxVal = max;
	dHeader->interval = interval;

	ComponentInfoStruct* info = GETCOMPDATA(header, cid);
	info->paramCount++;
	info->paramSize += newSize;
	mutex->leave();
	return true;
}

bool ComponentMemory::hasParameter(uint32 cid, const char* name) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ParamHeader* param = getParameter(cid, name);
	if (!param) {
		mutex->leave();
		return false;
	}
	else {
		mutex->leave();
		return true;
	}
}

bool ComponentMemory::deleteParameter(uint32 cid, const char* name) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ParamHeader* param = getParameter(cid, name);
	if (!param) {
		mutex->leave();
		return false;
	}
	ComponentInfoStruct* info = GETCOMPDATA(header, cid);

	uint32 delSize = param->size;
	uint32 moveSize = (uint32)(((char*)info) + sizeof(ComponentInfoStruct) + COMPINFOUSAGE(info) - ((char*)param) - param->size);
	memmove(((char*)param), ((char*)param)+param->size, moveSize);
	info->paramCount--;
	info->paramSize -= delSize;
	mutex->leave();
	return true;
}

uint8 ComponentMemory::getParameterDataType(uint32 cid, const char* name) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return 0;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ParamHeader* param = getParameter(cid, name);
	if (!param) {
		mutex->leave();
		return 0;
	}

	uint8 type = param->type;
	mutex->leave();
	return type;
}

uint32 ComponentMemory::getParameterValueSize(uint32 cid, const char* name) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return 0;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ParamHeader* param = getParameter(cid, name);
	if (!param) {
		mutex->leave();
		return 0;
	}

	uint32 size;
	char* pSrc = ((char*)param) + sizeof(ParamHeader) + strlen(((char*)param) + sizeof(ParamHeader)) + 1;
	if ( (param->type == PARAM_INTEGER) || (param->type == PARAM_INTEGER_COLL) )
		size = sizeof(int64);
	else if ( (param->type == PARAM_FLOAT) || (param->type == PARAM_FLOAT_COLL) )
		size = sizeof(float64);
	else if (param->type == PARAM_STRING) {
		size = ((uint32)strlen(pSrc) + 1);
	}
	else {
		ParamCollectionHeader* cHeader = (ParamCollectionHeader*) pSrc;
		for (uint32 n=0; n<cHeader->index; n++)
			pSrc += strlen(pSrc) + 1;
		size = (uint32)strlen(pSrc) + 1;
	}

	mutex->leave();
	return size;
}

std::string ComponentMemory::getParameterString(uint32 cid, const char* name, uint32 maxSize) {
	char* val = new char[maxSize];
	if (!getParameter(cid, name, val, maxSize))
		return "";
	std::string valString = val;
	delete[] val;
	return valString;
}

std::string ComponentMemory::getParameterString(uint32 cid, const char* name) {
	std::string result;
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return result;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return result;
	}

	ParamHeader* pHeader = getParameter(cid, name);
	if (!pHeader) {
		mutex->leave();
		return result;
	}

	char* pSrc = ((char*)pHeader) + sizeof(ParamHeader) + strlen(((char*)pHeader) + sizeof(ParamHeader)) + 1;

	if (pHeader->type == PARAM_STRING) {
		result = pSrc;
		mutex->leave();
		return result;
	}
	else if (pHeader->type == PARAM_STRING_COLL) {
		ParamCollectionHeader* cHeader = (ParamCollectionHeader*)pSrc;
		pSrc += sizeof(ParamCollectionHeader);
		for (uint32 n = 0; n<cHeader->index; n++)
			pSrc += strlen(pSrc) + 1;
		result = pSrc;
		mutex->leave();
		return result;
	}
	else {
		mutex->leave();
		return result;
	}
}

int64 ComponentMemory::getParameterInt(uint32 cid, const char* name) {
	int64 val = 0;
	if (!getParameter(cid, name, val))
		return 0;
	return val;
}

float64 ComponentMemory::getParameterFloat(uint32 cid, const char* name) {
	float64 val = 0;
	if (!getParameter(cid, name, val))
		return 0;
	return val;
}

bool ComponentMemory::getParameterAsBool(uint32 cid, const char* name) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ParamHeader* pHeader = getParameter(cid, name);
	if (!pHeader) {
		mutex->leave();
		return 0;
	}

	ParamCollectionHeader* cHeader;
	int64 ival;
	float64 fval;
	char* pSrc = ((char*)pHeader) + sizeof(ParamHeader) + strlen(((char*)pHeader) + sizeof(ParamHeader)) + 1;

	if (pHeader->type == PARAM_STRING) {
		if (stricmp(pSrc, "Yes") == 0) {
			mutex->leave();
			return true;
		}
		else {
			mutex->leave();
			return false;
		}
	}
	else if (pHeader->type == PARAM_STRING_COLL) {
		cHeader = (ParamCollectionHeader*)pSrc;
		pSrc += sizeof(ParamCollectionHeader);
		for (uint32 n = 0; n<cHeader->index; n++)
			pSrc += strlen(pSrc) + 1;
		if (stricmp(pSrc, "yes") == 0) {
			mutex->leave();
			return true;
		}
		else {
			mutex->leave();
			return false;
		}
	}
	else if (pHeader->type == PARAM_INTEGER) {
		ival = *(int64*)(pSrc);
		mutex->leave();
		return (ival != 0);
	}
	else if (pHeader->type == PARAM_INTEGER_COLL) {
		cHeader = (ParamCollectionHeader*)pSrc;
		pSrc += sizeof(ParamCollectionHeader) + (cHeader->index * sizeof(int64));
		ival = *(int64*)pSrc;
		mutex->leave();
		return (ival != 0);
	}
	else if (pHeader->type == PARAM_FLOAT) {
		fval = *(float64*)(pSrc);
		mutex->leave();
		return (fval != 0);
	}
	else if (pHeader->type == PARAM_FLOAT_COLL) {
		cHeader = (ParamCollectionHeader*)pSrc;
		pSrc += sizeof(ParamCollectionHeader) + (cHeader->index * sizeof(float64));
		fval = *(float64*)pSrc;
		mutex->leave();
		return (fval != 0);
	}
	else {
		mutex->leave();
		return false;
	}
}


bool ComponentMemory::getParameter(uint32 cid, const char* name, char* val, uint32 maxSize) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ParamHeader* pHeader = getParameter(cid, name);
	if (!pHeader) {
		mutex->leave();
		return 0;
	}

	char* pSrc = ((char*)pHeader) + sizeof(ParamHeader) + strlen(((char*)pHeader) + sizeof(ParamHeader)) + 1;

	if (pHeader->type == PARAM_STRING) {
		if (maxSize < strlen(pSrc) + 1) {
			mutex->leave();
			return false;
		}
		utils::strcpyavail(val, pSrc, maxSize, true);
		mutex->leave();
		return true;
	}
	else if (pHeader->type == PARAM_STRING_COLL) {
		ParamCollectionHeader* cHeader = (ParamCollectionHeader*) pSrc;
		pSrc += sizeof(ParamCollectionHeader);
		for (uint32 n=0; n<cHeader->index; n++)
			pSrc += strlen(pSrc) + 1;
		if (maxSize < strlen(pSrc) + 1) {
			mutex->leave();
			return false;
		}
		utils::strcpyavail(val, pSrc, maxSize, true);
		mutex->leave();
		return true;
	}
	else {
		mutex->leave();
		return false;
	}
}

bool ComponentMemory::getParameter(uint32 cid, const char* name, int64& val) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ParamHeader* pHeader = getParameter(cid, name);
	if (!pHeader) {
		mutex->leave();
		return 0;
	}

	char* pSrc = ((char*)pHeader) + sizeof(ParamHeader) + strlen(((char*)pHeader) + sizeof(ParamHeader)) + 1;

	if (pHeader->type == PARAM_INTEGER) {
		val = *(int64*)(pSrc);
		mutex->leave();
		return true;
	}
	else if  (pHeader->type == PARAM_INTEGER_COLL) {
		ParamCollectionHeader* cHeader = (ParamCollectionHeader*) pSrc;
		pSrc += sizeof(ParamCollectionHeader) + (cHeader->index*sizeof(int64));
		val = *(int64*) pSrc;
		mutex->leave();
		return true;
	}
	else {
		mutex->leave();
		return false;
	}
}

bool ComponentMemory::getParameter(uint32 cid, const char* name, float64& val) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ParamHeader* pHeader = getParameter(cid, name);
	if (!pHeader) {
		mutex->leave();
		return 0;
	}

	char* pSrc = ((char*)pHeader) + sizeof(ParamHeader) + strlen(((char*)pHeader) + sizeof(ParamHeader)) + 1;

	if (pHeader->type == PARAM_FLOAT) {
		val = *(float64*)(pSrc);
		mutex->leave();
		return true;
	}
	else if  (pHeader->type == PARAM_FLOAT_COLL) {
		ParamCollectionHeader* cHeader = (ParamCollectionHeader*) pSrc;
		pSrc += sizeof(ParamCollectionHeader) + (cHeader->index*sizeof(float64));
		val = *(float64*) pSrc;
		mutex->leave();
		return true;
	}
	else {
		mutex->leave();
		return false;
	}
}


bool ComponentMemory::setParameter(uint32 cid, const char* name, const char* val) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ParamHeader* pHeader = getParameter(cid, name);
	if (!pHeader) {
		mutex->leave();
		return 0;
	}

	uint32 oldSize;
	char* pSrc = ((char*)pHeader) + sizeof(ParamHeader) + strlen(((char*)pHeader) + sizeof(ParamHeader)) + 1;
	if (pHeader->type == PARAM_STRING) {
		oldSize = (uint32)strlen(pSrc);
		if (strlen(val) == oldSize) {
			utils::strcpyavail(pSrc, val, oldSize, true);
			mutex->leave();
			return true;
		}
		// we need to recreate the parameter with the new size
		uint32 defaultSize = (uint32)strlen(pSrc + oldSize + 1) + 1;
		char* defaultValue = new char[defaultSize];
		utils::strcpyavail(defaultValue, pSrc + oldSize + 1, defaultSize, true);
		// delete the old parameter
		if (!deleteParameter(cid, name)) {
			delete [] defaultValue;
			mutex->leave();
			return false;
		}
		// create the new one
		if (!createParameter(cid, name, val, defaultValue)) {
			delete [] defaultValue;
			mutex->leave();
			return false;
		}
		// clean up
		delete [] defaultValue;
		mutex->leave();
		return true;
	}
	else if (pHeader->type == PARAM_STRING_COLL) {
		// See if the requested value is in the list
		ParamCollectionHeader* cHeader = (ParamCollectionHeader*) pSrc;
		uint32 index = 0;
		pSrc += sizeof(ParamCollectionHeader);
		while (index < cHeader->count) {
			if (stricmp(val, pSrc) == 0) {
				// if so, set the index to the correct entry
				cHeader->index = index;
				mutex->leave();
				return true;
			}
			index++;
			pSrc += strlen(pSrc) + 1;
		}
		mutex->leave();
		return false;
	}
	else {
		mutex->leave();
		return false;
	}
}

bool ComponentMemory::setParameter(uint32 cid, const char* name, int64 val) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ParamHeader* pHeader = getParameter(cid, name);
	if (!pHeader) {
		mutex->leave();
		return 0;
	}

	char* pSrc = ((char*)pHeader) + sizeof(ParamHeader) + strlen(((char*)pHeader) + sizeof(ParamHeader)) + 1;

	if (pHeader->type == PARAM_INTEGER) {
		((ParamIntegerHeader*) pSrc)->val = val;
		mutex->leave();
		return true;
	}
	else if (*(uint8*)pSrc == PARAM_INTEGER_COLL) {
		// See if the requested value is in the list
		ParamCollectionHeader* cHeader = (ParamCollectionHeader*) pSrc;
		uint32 index = 0;
		pSrc += sizeof(ParamCollectionHeader);
		while (index < cHeader->count) {
			if (*(int64*)pSrc == val) {
				// if so, set the index to the correct entry
				cHeader->index = index;
				mutex->leave();
				return true;
			}
			index++;
			pSrc += sizeof(int64);
		}
		mutex->leave();
		return false;
	}
	else {
		mutex->leave();
		return false;
	}
}

bool ComponentMemory::setParameter(uint32 cid, const char* name, float64 val) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ParamHeader* pHeader = getParameter(cid, name);
	if (!pHeader) {
		mutex->leave();
		return 0;
	}

	char* pSrc = ((char*)pHeader) + sizeof(ParamHeader) + strlen(((char*)pHeader) + sizeof(ParamHeader)) + 1;

	if (pHeader->type == PARAM_FLOAT) {
		((ParamFloatHeader*) pSrc)->val = val;
		mutex->leave();
		return true;
	}
	else if (*(uint8*)pSrc == PARAM_FLOAT_COLL) {
		// See if the requested value is in the list
		ParamCollectionHeader* cHeader = (ParamCollectionHeader*) pSrc;
		uint32 index = 0;
		pSrc += sizeof(ParamCollectionHeader);
		while (index < cHeader->count) {
			if (*(float64*)pSrc == val) {
				// if so, set the index to the correct entry
				cHeader->index = index;
				mutex->leave();
				return true;
			}
			index++;
			pSrc += sizeof(float64);
		}
		mutex->leave();
		return false;
	}
	else {
		mutex->leave();
		return false;
	}
}


bool ComponentMemory::resetParameter(uint32 cid, const char* name) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ParamHeader* pHeader = getParameter(cid, name);
	if (!pHeader) {
		mutex->leave();
		return 0;
	}

	char* pSrc = ((char*)pHeader) + sizeof(ParamHeader) + strlen(((char*)pHeader) + sizeof(ParamHeader)) + 1;

	if (pHeader->type == PARAM_INTEGER) {
		((ParamIntegerHeader*)pSrc)->val = ((ParamIntegerHeader*)pSrc)->defaultVal;
		mutex->leave();
		return true;
	}
	else if (*(uint8*)pSrc == PARAM_INTEGER_COLL) {
		// Set index to default index
		((ParamCollectionHeader*)pSrc)->index = ((ParamCollectionHeader*)pSrc)->defaultIndex;
		mutex->leave();
		return true;
	}
	else if (*(uint8*)pSrc == PARAM_FLOAT) {
		((ParamFloatHeader*)pSrc)->val = ((ParamFloatHeader*)pSrc)->defaultVal;
		mutex->leave();
		return true;
	}
	else if (*(uint8*)pSrc == PARAM_FLOAT_COLL) {
		// Set index to default index
		((ParamCollectionHeader*)pSrc)->index = ((ParamCollectionHeader*)pSrc)->defaultIndex;
		mutex->leave();
		return true;
	}
	else if (*(uint8*)pSrc == PARAM_STRING) {
		if (setParameter(cid, name, pSrc + strlen(pSrc) + 1)) {
			mutex->leave();
			return true;
		}
		else {
			mutex->leave();
			return false;
		}
	}
	else if (*(uint8*)pSrc == PARAM_STRING_COLL) {
		// Set index to default index
		((ParamCollectionHeader*)pSrc)->index = ((ParamCollectionHeader*)pSrc)->defaultIndex;
		mutex->leave();
		return true;
	}
	else {
		mutex->leave();
		return false;
	}
}

bool ComponentMemory::tweakParameter(uint32 cid, const char* name, int32 tweak) {
	if (!mutex || !mutex->enter(5000, __FUNCTION__))
		return false;
	CHECKCOMPONENTMEMORYSERIAL

	if (cid >= header->indexSize) {
		mutex->leave();
		return false;
	}

	ParamHeader* pHeader = getParameter(cid, name);
	if (!pHeader) {
		mutex->leave();
		return false;
	}

	char* pSrc = ((char*)pHeader) + sizeof(ParamHeader) + strlen(((char*)pHeader) + sizeof(ParamHeader)) + 1;

	if ( (pHeader->type == PARAM_INTEGER_COLL) || (pHeader->type == PARAM_FLOAT_COLL) || (pHeader->type == PARAM_STRING_COLL) ) {
		ParamCollectionHeader* cHeader = (ParamCollectionHeader*)pSrc;
		if ( (cHeader->index + tweak >= 0) && (cHeader->index + tweak < cHeader->count) ) {
			cHeader->index += tweak;
			mutex->leave();
			return true;
		}
		else {
			mutex->leave();
			return false;
		}
	}
	else if (pHeader->type == PARAM_INTEGER) {
		ParamIntegerHeader* iHeader = (ParamIntegerHeader*)pSrc;
		if (iHeader->interval == 0) {
			mutex->leave();
			return false;
		}
		int64 testInt = iHeader->val + (tweak * iHeader->interval);
		if ( (iHeader->minVal == iHeader->maxVal) || ((testInt >= iHeader->minVal) && (testInt <= iHeader->maxVal)) ) {
			iHeader->val = testInt;
			mutex->leave();
			return true;
		}
	}
	else if (pHeader->type == PARAM_FLOAT) {
		ParamFloatHeader* fHeader = (ParamFloatHeader*)pSrc;
		if (fHeader->interval == 0) {
			mutex->leave();
			return false;
		}
		float64 testFloat = fHeader->val + (tweak * fHeader->interval);
		if ( (fHeader->minVal == fHeader->maxVal) || ( (testFloat >= fHeader->minVal) && (testFloat <= fHeader->maxVal)) ) {
			fHeader->val = testFloat;
			mutex->leave();
			return true;
		}
	}
	mutex->leave();
	return false;
}



















bool ComponentMemory::UnitTest() {

	// First create and initialise the MemoryManager
	unittest::progress(5, "memory manager init");
	MemoryManager* manager = new MemoryManager();
	//uint32 maxPageCount = 100;
	if (!manager->create(0)) {
		unittest::fail("MemoryManager init() failed");
		delete(manager);
		return false;
	}

	// Component creation and lookup
	unittest::progress(15, "component create and lookup");
	uint32 cid = 1, existingID;
	char* name = new char[1024];
	utils::strcpyavail(name, "TestComponent", 1024, true);
	if (!manager->componentMemory->createComponent(cid, COMP_INTERNAL, 0, SELFTRIGGER_WARN, name, 1024, 0, 0, 0, existingID)) {
		unittest::fail("Could not create component data");
		delete(manager);
		return false;
	}
	manager->componentMemory->confirmComponentID(cid);

	char* str = new char[1024];
	if (!manager->componentMemory->getComponentName(cid, str, 1024)) {
		unittest::fail("Could not get component name");
		delete(manager);
		return false;
	}
	if (strcmp(name, str) != 0) {
		unittest::fail("Component name mismatch ('%s' != '%s')", name, str);
		delete(manager);
		return false;
	}
	unittest::detail("Created component '%s' with id %u", str, cid);

	uint32 cid2;
	if (!manager->componentMemory->getComponentID(name, cid2)) {
		unittest::fail("Could not get component id");
		delete(manager);
		return false;
	}
	if (cid != cid2) {
		unittest::fail("Component id mismatch ('%u' != '%u')", cid, cid2);
		delete(manager);
		return false;
	}

	// Private data set/get/resize/delete
	unittest::progress(30, "private data");
	if (!manager->componentMemory->setPrivateData(cid, "Private0", "Test", 5)) {
		unittest::fail("Component set private data failed");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->setPrivateData(cid, "Private1", "Test", 5)) {
		unittest::fail("Component set private data failed");
		delete(manager);
		return false;
	}

	if (manager->componentMemory->getPrivateDataSize(cid, "Private1") != 5) {
		unittest::fail("Component get private data size failed");
		delete(manager);
		return false;
	}

	char* test = new char[1024];
	if (!manager->componentMemory->getPrivateData(cid, "Private1", test, 1024)) {
		unittest::fail("Component get private data failed");
		delete(manager);
		return false;
	}

	if (strcmp("Test", test) != 0) {
		unittest::fail("Component get private data comparison failed");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->setPrivateData(cid, "Private1", "Test2", 6)) {
		unittest::fail("Component set private data 2 failed");
		delete(manager);
		return false;
	}

	if (manager->componentMemory->getPrivateDataSize(cid, "Private1") != 6) {
		unittest::fail("Component get private data size 2 failed");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->getPrivateData(cid, "Private1", test, 1024)) {
		unittest::fail("Component get private data 2 failed");
		delete(manager);
		return false;
	}

	if (strcmp("Test2", test) != 0) {
		unittest::fail("Component get private data comparison 2 failed");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->deletePrivateData(cid, "Private1")) {
		unittest::fail("Component delete private data failed");
		delete(manager);
		return false;
	}


	// Scalar parameters: create/get/delete
	unittest::progress(45, "scalar parameters");
	if (!manager->componentMemory->createParameter(cid, "Param1", "Test")) {
		unittest::fail("Component create string param failed");
		delete(manager);
		return false;
	}

	if (manager->componentMemory->createParameter(cid, "Param1", (int64)5)) {
		unittest::fail("Component create duplicate name failed to fail");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->createParameter(cid, "Param2", (int64)5)) {
		unittest::fail("Component create integer param failed");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->createParameter(cid, "Param3", 5.5)) {
		unittest::fail("Component create float param failed");
		delete(manager);
		return false;
	}

	// Check the value of each
	if (manager->componentMemory->getParameterValueSize(cid, "Param1") != 5) {
		unittest::fail("Component get string param size failed");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->getParameter(cid, "Param1", test, 1024)) {
		unittest::fail("Component get string param failed");
		delete(manager);
		return false;
	}

	if (strcmp("Test", test) != 0) {
		unittest::fail("Component get string parameter comparison 2 failed");
		delete(manager);
		return false;
	}

	int64 ival;
	if (!manager->componentMemory->getParameter(cid, "Param2", ival)) {
		unittest::fail("Component get integer param failed");
		delete(manager);
		return false;
	}

	float64 fval;
	if (!manager->componentMemory->getParameter(cid, "Param3", fval)) {
		unittest::fail("Component get float param failed");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->deleteParameter(cid, "Param2")) {
		unittest::fail("Component delete float param failed");
		delete(manager);
		return false;
	}

	if (manager->componentMemory->getParameter(cid, "Param2", ival)) {
		unittest::fail("Component get integer param failed");
		delete(manager);
		return false;
	}

	if (manager->componentMemory->getParameterValueSize(cid, "Param1") != 5) {
		unittest::fail("Component get string param size failed");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->getParameter(cid, "Param3", fval)) {
		unittest::fail("Component get float param failed");
		delete(manager);
		return false;
	}

	if (manager->componentMemory->getParameter(cid, "Param4", fval)) {
		unittest::fail("Component get non-existing param failed to fail");
		delete(manager);
		return false;
	}

	// Create max min, tweak and check
	unittest::progress(60, "ranged and collection parameters");
	if (!manager->componentMemory->createParameter(cid, "Param4", (int64)5, 0, 10, 1)) {
		unittest::fail("Component create integer param 2 failed");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->tweakParameter(cid, "Param4", 2)) {
		unittest::fail("Component tweak integer param failed");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->getParameter(cid, "Param4", ival)) {
		unittest::fail("Component get integer param 2 failed");
		delete(manager);
		return false;
	}

	if (ival != 7) {
		unittest::fail("Component tweak integer param result failed");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->tweakParameter(cid, "Param4", -4)) {
		unittest::fail("Component tweak integer param 2 failed");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->getParameter(cid, "Param4", ival)) {
		unittest::fail("Component get integer param 3 failed");
		delete(manager);
		return false;
	}

	if (ival != 3) {
		unittest::fail("Component tweak integer param result 2 failed");
		delete(manager);
		return false;
	}

	if (manager->componentMemory->tweakParameter(cid, "Param4", 8)) {
		unittest::fail("Component tweak integer param 2 failed to fail");
		delete(manager);
		return false;
	}

	// Create collection, tweak and check
	int64 i64[] = {1,2,3,4,5};
	if (!manager->componentMemory->createParameter(cid, "Param5", i64, 5, 2)) {
		unittest::fail("Component create integer coll param failed");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->getParameter(cid, "Param5", ival)) {
		unittest::fail("Component get integer param coll failed");
		delete(manager);
		return false;
	}

	if (ival != 3) {
		unittest::fail("Component tweak integer coll param result failed");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->tweakParameter(cid, "Param5", 2)) {
		unittest::fail("Component tweak integer coll param failed");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->getParameter(cid, "Param5", ival)) {
		unittest::fail("Component get integer param coll 2 failed");
		delete(manager);
		return false;
	}

	if (ival != 5) {
		unittest::fail("Component tweak integer coll param result 2 failed");
		delete(manager);
		return false;
	}

	if (manager->componentMemory->tweakParameter(cid, "Param5", -5)) {
		unittest::fail("Component tweak integer coll param failed to fail");
		delete(manager);
		return false;
	}


	utils::strcpyavail(test, "Test1", 1024, true);
	utils::strcpyavail(test+6, "Test2", 1024, true);
	utils::strcpyavail(test+12, "Test3", 1024, true);
	utils::strcpyavail(test+18, "Test4", 1024, true);
	utils::strcpyavail(test+24, "Test5", 1024, true);

	if (!manager->componentMemory->createParameter(cid, "Param6", test, 5, 2)) {
		unittest::fail("Component create string coll param failed");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->getParameter(cid, "Param6", test, 1024)) {
		unittest::fail("Component get string param coll failed");
		delete(manager);
		return false;
	}

	if (strcmp(test, "Test3") != 0) {
		unittest::fail("Component tweak string coll param result failed");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->tweakParameter(cid, "Param6", 2)) {
		unittest::fail("Component tweak string coll param failed");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->getParameter(cid, "Param6", test, 1024)) {
		unittest::fail("Component get string param coll 2 failed");
		delete(manager);
		return false;
	}

	if (strcmp(test, "Test5") != 0) {
		unittest::fail("Component tweak string coll param result 2 failed");
		delete(manager);
		return false;
	}

	if (manager->componentMemory->tweakParameter(cid, "Param6", -5)) {
		unittest::fail("Component tweak string coll param failed to fail");
		delete(manager);
		return false;
	}



	if (!manager->componentMemory->deleteParameter(cid, "Param3")) {
		unittest::fail("Component delete float param failed");
		delete(manager);
		return false;
	}

	if (!manager->componentMemory->destroyComponent(cid)) {
		unittest::fail("Could not destroy component");
		delete(manager);
		return false;
	}


	// ---- Location, flags and bounds accessors ----
	unittest::progress(66, "location/flags/bounds accessors");
	{
		uint32 lexisting;
		if (!manager->componentMemory->createComponent(7, COMP_INTERNAL, COMPONENT_CAN_MIGRATE, SELFTRIGGER_ALLOW, "LocComp", 256, 3, 4, 0, lexisting)) {
			unittest::fail("Loc: create failed"); delete(manager); return false;
		}
		manager->componentMemory->confirmComponentID(7);
		uint16 nID = 99, pID = 99;
		if (!manager->componentMemory->getComponentLocation(7, nID, pID) || nID != 3 || pID != 4) {
			unittest::fail("Loc: getComponentLocation got node=%u proc=%u (want 3/4)", nID, pID); delete(manager); return false;
		}
		if (manager->componentMemory->getComponentNodeID(7) != 3 || manager->componentMemory->getComponentProcessID(7) != 4) {
			unittest::fail("Loc: node/proc id accessors mismatch"); delete(manager); return false;
		}
		if (manager->componentMemory->getComponentSelfTrigger(7) != SELFTRIGGER_ALLOW) {
			unittest::fail("Loc: selfTrigger mismatch"); delete(manager); return false;
		}
		if (manager->componentMemory->getComponentType(7) != COMP_INTERNAL) {
			unittest::fail("Loc: type mismatch"); delete(manager); return false;
		}
		if (!manager->componentMemory->updateComponentLocation(7, 5, 6)) {
			unittest::fail("Loc: updateComponentLocation failed"); delete(manager); return false;
		}
		if (manager->componentMemory->getComponentProcessID(7) != 6 || manager->componentMemory->getComponentNodeID(7) != 5) {
			unittest::fail("Loc: location not updated"); delete(manager); return false;
		}
		// Out-of-range / unknown ids must fail gracefully (no crash / no false hit)
		uint16 bn, bp;
		if (manager->componentMemory->getComponentLocation(999999, bn, bp)) {
			unittest::fail("Loc: out-of-range id returned success"); delete(manager); return false;
		}
		if (manager->componentMemory->getComponentSelfTrigger(0) != 0) {
			unittest::fail("Loc: id 0 returned a self-trigger"); delete(manager); return false;
		}
		if (manager->componentMemory->getComponentName(999999, str, 1024)) {
			unittest::fail("Loc: out-of-range getComponentName returned success"); delete(manager); return false;
		}
		manager->componentMemory->destroyComponent(7);
	}


	// ---- Multi-component arena integrity: grow + middle-destroy shifts ----
	unittest::progress(70, "multi-component arena integrity");
	{
		const int NCOMP = 6;
		const uint32 baseID = 10;
		char cname[64], buf[4096], expect[64];

		// Create several components, each with distinct private data of varying size
		for (int k = 0; k < NCOMP; k++) {
			snprintf(cname, sizeof(cname), "Comp%d", k);
			if (!manager->componentMemory->createComponent(baseID + k, COMP_INTERNAL, 0, SELFTRIGGER_ALLOW, cname, 512, 0, 0, 0, existingID)) {
				unittest::fail("Multi: create %s failed", cname); delete(manager); return false;
			}
			manager->componentMemory->confirmComponentID(baseID + k);
			snprintf(buf, sizeof(buf), "DATA-%d", k);
			if (!manager->componentMemory->setPrivateData(baseID + k, "P", buf, 64 + k * 24)) {
				unittest::fail("Multi: setPrivateData %s failed", cname); delete(manager); return false;
			}
		}
		// All names + private data intact
		for (int k = 0; k < NCOMP; k++) {
			snprintf(cname, sizeof(cname), "Comp%d", k);
			snprintf(expect, sizeof(expect), "DATA-%d", k);
			if (!manager->componentMemory->getComponentName(baseID + k, str, 1024) || strcmp(str, cname) != 0) {
				unittest::fail("Multi: name check Comp%d got '%s'", k, str); delete(manager); return false;
			}
			if (!manager->componentMemory->getPrivateData(baseID + k, "P", buf, sizeof(buf)) || strncmp(buf, expect, strlen(expect)) != 0) {
				unittest::fail("Multi: data check Comp%d got '%s' want '%s'", k, buf, expect); delete(manager); return false;
			}
		}

		// Grow an EARLY component's private data -> forces resizeComponent to shift later blocks up
		memset(buf, 'Z', sizeof(buf));
		snprintf(buf, sizeof(buf), "BIGDATA-1");
		if (!manager->componentMemory->setPrivateData(baseID + 1, "P", buf, 2000)) {
			unittest::fail("Multi: grow Comp1 private data failed"); delete(manager); return false;
		}
		// Every component must still be intact after the shift
		for (int k = 0; k < NCOMP; k++) {
			snprintf(cname, sizeof(cname), "Comp%d", k);
			if (!manager->componentMemory->getComponentName(baseID + k, str, 1024) || strcmp(str, cname) != 0) {
				unittest::fail("Multi: post-grow name check Comp%d got '%s'", k, str); delete(manager); return false;
			}
			if (k == 1) snprintf(expect, sizeof(expect), "BIGDATA-1");
			else        snprintf(expect, sizeof(expect), "DATA-%d", k);
			if (!manager->componentMemory->getPrivateData(baseID + k, "P", buf, sizeof(buf)) || strncmp(buf, expect, strlen(expect)) != 0) {
				unittest::fail("Multi: post-grow data check Comp%d got '%s' want '%s'", k, buf, expect); delete(manager); return false;
			}
		}

		// Destroy a MIDDLE component (id 12, with higher-id components after it)
		if (!manager->componentMemory->destroyComponent(baseID + 2)) {
			unittest::fail("Multi: destroy middle component failed"); delete(manager); return false;
		}
		if (manager->componentMemory->getComponentName(baseID + 2, str, 1024)) {
			unittest::fail("Multi: destroyed Comp2 still present"); delete(manager); return false;
		}
		// All remaining components - especially those AFTER the destroyed one - must be intact
		for (int k = 0; k < NCOMP; k++) {
			if (k == 2) continue;
			snprintf(cname, sizeof(cname), "Comp%d", k);
			if (!manager->componentMemory->getComponentName(baseID + k, str, 1024) || strcmp(str, cname) != 0) {
				unittest::fail("Multi: post-destroy name check Comp%d got '%s'", k, str); delete(manager); return false;
			}
			if (k == 1) snprintf(expect, sizeof(expect), "BIGDATA-1");
			else        snprintf(expect, sizeof(expect), "DATA-%d", k);
			if (!manager->componentMemory->getPrivateData(baseID + k, "P", buf, sizeof(buf)) || strncmp(buf, expect, strlen(expect)) != 0) {
				unittest::fail("Multi: post-destroy data check Comp%d got '%s' want '%s'", k, buf, expect); delete(manager); return false;
			}
		}
		// Cleanup remaining
		for (int k = 0; k < NCOMP; k++)
			if (k != 2) manager->componentMemory->destroyComponent(baseID + k);
	}


	// ---- addComponentStats: counters + recent-message ring retention ----
	unittest::progress(73, "component stats ring");
	{
		uint32 sexisting;
		if (!manager->componentMemory->createComponent(20, COMP_INTERNAL, 0, SELFTRIGGER_ALLOW, "StatComp", 256, 0, 0, 0, sexisting)) {
			unittest::fail("Stats: create failed"); delete(manager); return false;
		}
		manager->componentMemory->confirmComponentID(20);

		DataMessage inMsg, outMsg;
		inMsg.setString("K", "in");
		outMsg.setString("K", "out");
		uint32 inBytes = inMsg.getSize(), outBytes = outMsg.getSize();

		manager->componentMemory->addComponentStats(20, COMPSTATUS_RUNNING, 100, &inMsg, &outMsg, 1, 2);
		ComponentStats s = manager->componentMemory->getComponentStats(20);
		if (s.msgInCount != 1 || s.msgOutCount != 1 || s.runCount != 1 || s.cycleCount != 2 ||
			s.usageCPUTicks != 100 || s.msgInBytes != inBytes || s.msgOutBytes != outBytes) {
			unittest::fail("Stats: counters wrong (in=%llu out=%llu run=%llu cyc=%llu cpu=%llu)",
				s.msgInCount, s.msgOutCount, s.runCount, s.cycleCount, s.usageCPUTicks);
			delete(manager); return false;
		}

		// Push 12 more input messages with a unique sequence id each (ring depth is 10)
		for (int k = 1; k <= 12; k++) {
			DataMessage m;
			m.setInt("Seq", (int64)k);
			manager->componentMemory->addComponentStats(20, COMPSTATUS_RUNNING, 0, &m, NULL, 0, 0);
		}
		s = manager->componentMemory->getComponentStats(20);
		if (s.msgInCount != 13) {
			unittest::fail("Stats: msgInCount %llu (want 13)", s.msgInCount); delete(manager); return false;
		}
		// The 10 most recent input messages must be retained: Seq 3..12 present, 1/2 evicted, newest present.
		bool seen[13]; for (int k = 0; k < 13; k++) seen[k] = false;
		int present = 0;
		for (int n = 0; n < 10; n++) {
			char* slot = s.recentInMsg + (n * DRAFTMSGSIZE);
			if (!((DataMessageHeader*)slot)->time)
				continue;
			present++;
			DataMessage m(slot, true);
			bool ok = false;
			int64 seq = m.getInt("Seq", ok);
			if (ok && seq >= 1 && seq <= 12)
				seen[seq] = true;
		}
		if (present != 10) {
			unittest::fail("Stats: ring holds %d messages (want 10)", present); delete(manager); return false;
		}
		if (seen[1] || seen[2]) {
			unittest::fail("Stats: ring did not evict oldest (Seq 1/2 still present)"); delete(manager); return false;
		}
		for (int k = 3; k <= 12; k++) {
			if (!seen[k]) {
				unittest::fail("Stats: ring missing recent message Seq %d", k); delete(manager); return false;
			}
		}
		manager->componentMemory->destroyComponent(20);
	}


	// ---- Name lookup across multiple components ----
	unittest::progress(74, "name lookup");
	{
		uint32 nexisting, gotID;
		manager->componentMemory->createComponent(30, COMP_INTERNAL, 0, SELFTRIGGER_ALLOW, "Alpha", 128, 0, 0, 0, nexisting);
		manager->componentMemory->confirmComponentID(30);
		manager->componentMemory->createComponent(31, COMP_INTERNAL, 0, SELFTRIGGER_ALLOW, "Beta", 128, 0, 0, 0, nexisting);
		manager->componentMemory->confirmComponentID(31);
		if (!manager->componentMemory->getComponentID("Beta", gotID) || gotID != 31) {
			unittest::fail("Name: lookup 'Beta' got %u (want 31)", gotID); delete(manager); return false;
		}
		if (!manager->componentMemory->getComponentID("Alpha", gotID) || gotID != 30) {
			unittest::fail("Name: lookup 'Alpha' got %u (want 30)", gotID); delete(manager); return false;
		}
		if (manager->componentMemory->getComponentID("DoesNotExist", gotID)) {
			unittest::fail("Name: lookup of missing name returned success"); delete(manager); return false;
		}
		manager->componentMemory->destroyComponent(30);
		manager->componentMemory->destroyComponent(31);
	}


	// Type levels
	unittest::progress(75, "type levels");
	uint16 tidt, existingID16;
	if (!manager->dataMapsMemory->createNewTypeLevel(1, "Type1", 0, existingID16)) {
		unittest::fail("Could not create new type");
		delete(manager);
		return false;
	}
	manager->dataMapsMemory->confirmTypeLevelID(1);

	if (!manager->dataMapsMemory->createNewTypeLevel(2, "Type2", 0, existingID16)) {
		unittest::fail("Could not create new type 2");
		delete(manager);
		return false;
	}
	manager->dataMapsMemory->confirmTypeLevelID(2);

	if (!manager->dataMapsMemory->getTypeLevelID("Type2", tidt)) {
		unittest::fail("Could not get type id");
		delete(manager);
		return false;
	}

	if (2 != tidt) {
		unittest::fail("Type id mismatch");
		delete(manager);
		return false;
	}

	if (!manager->dataMapsMemory->getTypeLevelName(2, test, 1024)) {
		unittest::fail("Could not get type name");
		delete(manager);
		return false;
	}

	if (strcmp(test, "Type2")) {
		unittest::fail("Type name mismatch");
		delete(manager);
		return false;
	}



	// Tags
	unittest::progress(88, "tags");
	uint32 idt;
	if (!manager->dataMapsMemory->createNewTag(1, "Tag1", 0, existingID)) {
		unittest::fail("Could not create new tag");
		delete(manager);
		return false;
	}
	manager->dataMapsMemory->confirmTagID(1);

	if (!manager->dataMapsMemory->createNewTag(2, "Tag2", 0, existingID)) {
		unittest::fail("Could not create new tag 2");
		delete(manager);
		return false;
	}
	manager->dataMapsMemory->confirmTagID(2);

	if (!manager->dataMapsMemory->getTagID("Tag2", idt)) {
		unittest::fail("Could not get tag id");
		delete(manager);
		return false;
	}

	if (2 != idt) {
		unittest::fail("Tag id mismatch");
		delete(manager);
		return false;
	}

	if (!manager->dataMapsMemory->getTagName(2, test, 1024)) {
		unittest::fail("Could not get tag name");
		delete(manager);
		return false;
	}

	if (strcmp(test, "Tag2")) {
		unittest::fail("Tag name mismatch");
		delete(manager);
		return false;
	}

	delete [] str;
	delete [] test;
	delete [] name;
	delete(manager);
	unittest::progress(100, "done");
	return true;
}

void Register_ComponentMemory_Tests() {
	UnitTestRunner::instance().registerTest("componentmemory", ComponentMemory::UnitTest,
		"Component memory: components, private data, parameters, type levels, tags", "memory");
}



} // namespace cmlabs













