<commit_before>/***********************************************************************************************************************
**
** Copyright (c) 2011, 2014 ETH Zurich
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
** following conditions are met:
**
**    * Redistributions of source code must retain the above copyright 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 the ETH Zurich nor the names of its contributors may be used to endorse or promote products
**      derived from this software without specific prior written permission.
**
**
** 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 HOLDER 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.
**
***********************************************************************************************************************/

#include "DebugConnector.h"

#include "Reply.h"
#include "Command.h"

#include "messages/AllMessages.h"

namespace OODebug {

DebugConnector::DebugConnector()
{
	QObject::connect(&tcpSocket_, static_cast<void (QTcpSocket::*)(QAbstractSocket::SocketError)>(&QTcpSocket::error),
						  this, &DebugConnector::handleSocketError);
}

DebugConnector::~DebugConnector()
{
	tcpSocket_.abort();
}

void DebugConnector::connect(QString mainClassName, QString vmHostName, int vmHostPort)
{
	mainClassName_ = mainClassName;
	if (tcpSocket_.isOpen())
		tcpSocket_.abort();
	QObject::disconnect(&tcpSocket_, &QTcpSocket::readyRead, this,
							  static_cast<void (DebugConnector::*)()>(&DebugConnector::read));
	Command::resetIds();
	// The connection setup is handled here:
	// First when we are connected we have to send the handshake. For receiving the hanshake reply,
	// we use a dedicated function which disconnects itself and connects the actual read function
	// to the readyRead slot. In this way we don't have the handshake handling in the normal read function.
	QObject::connect(&tcpSocket_, &QTcpSocket::readyRead, this, &DebugConnector::readHandshake);
	tcpSocket_.connectToHost(vmHostName, vmHostPort);
	tcpSocket_.waitForConnected();
	sendHandshake();
}

void DebugConnector::handleSocketError(QAbstractSocket::SocketError socketError)
{
	qDebug() << "ERROR: " << socketError;
	qDebug() << socketError;
}

void DebugConnector::read()
{
	read(-1);
}

int DebugConnector::read(qint32 requestId)
{
	QByteArray read = tcpSocket_.readAll();
	// If we still have a part of a packet add it here.
	if (!incompleteData_.isEmpty())
	{
		read.prepend(incompleteData_);
		incompleteData_ = QByteArray();
	}
	// If we haven't read enough retry later
	if (read.size() < int(sizeof(qint32)))
	{
		incompleteData_ = read;
		return -1;
	}
	// check if the packet is complete
	QDataStream inStream(read);
	qint32 packetLen;
	inStream >> packetLen;
	if (packetLen > read.length())
	{
		incompleteData_ = read;
		return -1;
	}
	// We have read the whole data for this message so handle it.
	qint32 id;
	inStream >> id;

	// It might be that we received more than one packet, if so we have to recursively call this function.
	if (packetLen < read.length())
	{
		incompleteData_ = read;
		incompleteData_.remove(0, packetLen);
		// trigger reads such that we handle the additional data
		if (-1 == id) this->read();
	}
	read.remove(packetLen + 1, read.length());

	if (-1 == requestId)
	{
		handleComposite(read);
		return 0;
	}
	auto nextInd = readyData_.length();
	readyData_ << read;
	if (requestId == id) return nextInd;
	return -1;
}

QByteArray DebugConnector::sendCommand(const Command& command)
{
	QByteArray raw;
	QDataStream stream(&raw, QIODevice::ReadWrite);
	stream << command;
	// Insert the length, it is always at position 0
	QByteArray len;
	qint32 dataLen = raw.length();
	QDataStream lenStream(&len, QIODevice::ReadWrite);
	lenStream << dataLen;
	raw.replace(0, len.length(), len);
	tcpSocket_.write(raw);
	tcpSocket_.waitForBytesWritten();

	int responseIndex = -1;
	while (-1 == responseIndex)
	{
		tcpSocket_.waitForReadyRead();
		responseIndex = read(command.id());
	}
	return readyData_.takeAt(responseIndex);
}

void DebugConnector::readHandshake()
{
	QByteArray read = tcpSocket_.readAll();
	Q_ASSERT(read.length() >= Protocol::handshake.length());
	if (read.startsWith(Protocol::handshake))
	{
		QObject::disconnect(&tcpSocket_, &QTcpSocket::readyRead, this, &DebugConnector::readHandshake);
		QObject::connect(&tcpSocket_, &QTcpSocket::readyRead, this,
							  static_cast<void (DebugConnector::*)()>(&DebugConnector::read));
		if (read.length() > Protocol::handshake.length())
		{
			// remove the handshake
			read.remove(0, Protocol::handshake.length());
			incompleteData_ = read;
			// trigger reads such that we handle the additional data
			this->read();
		}
		checkVersion();
		checkIdSizes();
		sendBreakAtStart();
	}
	else
	{
		qDebug() << "Handshake not received: " << read.toHex() << Protocol::handshake.toHex();
	}
}

void DebugConnector::sendHandshake()
{
	tcpSocket_.write(Protocol::handshake);
	tcpSocket_.waitForBytesWritten();
}

void DebugConnector::checkVersion()
{
	auto info = makeReply<VersionInfo>(sendCommand(VersionCommand()));
	qDebug() << "VM-INFO: " << info.jdwpMajor() << info.jdwpMinor();
}

void DebugConnector::checkIdSizes()
{
	// Our protocol is only designed for 64 bit machine make sure that this is the case:
	auto idSizes = makeReply<IDSizes>(sendCommand(IDSizeCommand()));
	Q_ASSERT(sizeof(qint64) == idSizes.fieldIDSize());
	Q_ASSERT(sizeof(qint64) == idSizes.methodIDSize());
	Q_ASSERT(sizeof(qint64) == idSizes.objectIDSize());
	Q_ASSERT(sizeof(qint64) == idSizes.referenceTypeIDSize());
	Q_ASSERT(sizeof(qint64) == idSizes.frameIDSize());
}

void DebugConnector::sendBreakAtStart()
{
	auto r = makeReply<Reply>(sendCommand(BreakClassLoad(mainClassName_)));
	Q_ASSERT(Protocol::Error::NONE == r.error());
	resume();
}

bool DebugConnector::resume()
{
	auto r = makeReply<Reply>(sendCommand(ResumeCommand()));
	return Protocol::Error::NONE == r.error();
}

qint64 DebugConnector::getClassId(const QString& signature)
{
	auto it = classIdMap_.find(signature);
	if (it == classIdMap_.end())
	{
		auto classesBySignature = makeReply<ClassesBySignature>(sendCommand(ClassesBySignatureCommand(signature)));
		Q_ASSERT(classesBySignature.classes().size() == 1);
		return classIdMap_[signature] = classesBySignature.classes()[0].typeID();
	}
	else
	{
		return it.value();
	}
}

qint64 DebugConnector::getMethodId(qint64 classId, const QString& signature)
{
	auto methods = makeReply<MethodsReply>(sendCommand(MethodsCommand(classId)));
	for (auto method : methods.methods())
	{
		// TODO: check for signature
		if (method.name() == signature) return method.methodID();
	}
	return -1;
}

int DebugConnector::sendBreakpoint(Location breakLocation)
{
	auto r = makeReply<EventSetReply>(sendCommand(BreakpointCommand(breakLocation)));
	return r.requestId();
}

void DebugConnector::handleComposite(QByteArray data)
{
	auto c = makeReply<CompositeCommand>(data);
	// If we receive a non composite event, we have an implementation error.
	Q_ASSERT(c.commandSet() == Protocol::CommandSet::Event);
	Q_ASSERT(c.command() == MessagePart::cast(Protocol::EventCommands::Composite));

	for (auto& event : c.events())
	{
		if (auto listener = eventListeners_[event.eventKind()])
			listener(event);
		else
			qDebug() << "EVENT" << static_cast<int>(event.eventKind());
	}
}

} /* namespace OODebug */
<commit_msg>Fix event handling in DebugConnector<commit_after>/***********************************************************************************************************************
**
** Copyright (c) 2011, 2014 ETH Zurich
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
** following conditions are met:
**
**    * Redistributions of source code must retain the above copyright 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 the ETH Zurich nor the names of its contributors may be used to endorse or promote products
**      derived from this software without specific prior written permission.
**
**
** 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 HOLDER 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.
**
***********************************************************************************************************************/

#include "DebugConnector.h"

#include "Reply.h"
#include "Command.h"

#include "messages/AllMessages.h"

namespace OODebug {

DebugConnector::DebugConnector()
{
	QObject::connect(&tcpSocket_, static_cast<void (QTcpSocket::*)(QAbstractSocket::SocketError)>(&QTcpSocket::error),
						  this, &DebugConnector::handleSocketError);
}

DebugConnector::~DebugConnector()
{
	tcpSocket_.abort();
}

void DebugConnector::connect(QString mainClassName, QString vmHostName, int vmHostPort)
{
	mainClassName_ = mainClassName;
	if (tcpSocket_.isOpen())
		tcpSocket_.abort();
	QObject::disconnect(&tcpSocket_, &QTcpSocket::readyRead, this,
							  static_cast<void (DebugConnector::*)()>(&DebugConnector::read));
	Command::resetIds();
	// The connection setup is handled here:
	// First when we are connected we have to send the handshake. For receiving the hanshake reply,
	// we use a dedicated function which disconnects itself and connects the actual read function
	// to the readyRead slot. In this way we don't have the handshake handling in the normal read function.
	QObject::connect(&tcpSocket_, &QTcpSocket::readyRead, this, &DebugConnector::readHandshake);
	tcpSocket_.connectToHost(vmHostName, vmHostPort);
	tcpSocket_.waitForConnected();
	sendHandshake();
}

void DebugConnector::handleSocketError(QAbstractSocket::SocketError socketError)
{
	qDebug() << "ERROR: " << socketError;
	qDebug() << socketError;
}

void DebugConnector::read()
{
	read(-1);
	auto it = readyData_.begin();
	while (it != readyData_.end())
	{
		auto data = *it;
		auto command = makeReply<Command>(data);
		if (command.commandSet() == Protocol::CommandSet::Event
			 && command.command() == MessagePart::cast(Protocol::EventCommands::Composite))
		{
			readyData_.erase(it);
			handleComposite(data);
			// The handling of one event might mess with our iterator so just emit the signal that we get called again
			// once the handling is done
			emit tcpSocket_.readyRead();
			break;
		}
		else
		{
			++it;
		}
	}
}

int DebugConnector::read(qint32 requestId)
{
	// First check if the data is already here
	if (requestId != -1)
	{
		for (int i = 0; i < readyData_.length(); ++i)
		{
			auto r = makeReply<Reply>(readyData_[i]);
			if (r.id() == requestId) return i;
		}
	}

	QByteArray read = tcpSocket_.readAll();
	// If we still have a part of a packet add it here.
	if (!incompleteData_.isEmpty())
	{
		read.prepend(incompleteData_);
		incompleteData_ = QByteArray();
	}
	// If we haven't read enough retry later
	if (read.size() < int(sizeof(qint32)))
	{
		incompleteData_ = read;
		return -1;
	}
	// check if the packet is complete
	QDataStream inStream(read);
	qint32 packetLen;
	inStream >> packetLen;
	if (packetLen > read.length())
	{
		incompleteData_ = read;
		return -1;
	}
	// We have read the whole data for this message so handle it.
	qint32 id;
	inStream >> id;

	// It might be that we received more than one packet, if so we have to recursively call this function.
	if (packetLen < read.length())
	{
		incompleteData_ = read;
		incompleteData_.remove(0, packetLen);
	}
	read.remove(packetLen + 1, read.length());

	auto nextInd = readyData_.length();
	readyData_ << read;
	if (requestId == id) return nextInd;
	return -1;
}

QByteArray DebugConnector::sendCommand(const Command& command)
{
	QByteArray raw;
	QDataStream stream(&raw, QIODevice::ReadWrite);
	stream << command;
	// Insert the length, it is always at position 0
	QByteArray len;
	qint32 dataLen = raw.length();
	QDataStream lenStream(&len, QIODevice::ReadWrite);
	lenStream << dataLen;
	raw.replace(0, len.length(), len);
	tcpSocket_.write(raw);
	tcpSocket_.waitForBytesWritten();

	int responseIndex = -1;
	while (-1 == responseIndex)
	{
		tcpSocket_.waitForReadyRead();
		responseIndex = read(command.id());
	}
	return readyData_.takeAt(responseIndex);
}

void DebugConnector::readHandshake()
{
	QByteArray read = tcpSocket_.readAll();
	Q_ASSERT(read.length() >= Protocol::handshake.length());
	if (read.startsWith(Protocol::handshake))
	{
		QObject::disconnect(&tcpSocket_, &QTcpSocket::readyRead, this, &DebugConnector::readHandshake);
		QObject::connect(&tcpSocket_, &QTcpSocket::readyRead, this,
							  static_cast<void (DebugConnector::*)()>(&DebugConnector::read));
		if (read.length() > Protocol::handshake.length())
		{
			// remove the handshake
			read.remove(0, Protocol::handshake.length());
			incompleteData_ = read;
			// trigger reads such that we handle the additional data
			this->read(-1);
		}
		checkVersion();
		checkIdSizes();
		sendBreakAtStart();
	}
	else
	{
		qDebug() << "Handshake not received: " << read.toHex() << Protocol::handshake.toHex();
	}
}

void DebugConnector::sendHandshake()
{
	tcpSocket_.write(Protocol::handshake);
	tcpSocket_.waitForBytesWritten();
}

void DebugConnector::checkVersion()
{
	auto info = makeReply<VersionInfo>(sendCommand(VersionCommand()));
	qDebug() << "VM-INFO: " << info.jdwpMajor() << info.jdwpMinor();
}

void DebugConnector::checkIdSizes()
{
	// Our protocol is only designed for 64 bit machine make sure that this is the case:
	auto idSizes = makeReply<IDSizes>(sendCommand(IDSizeCommand()));
	Q_ASSERT(sizeof(qint64) == idSizes.fieldIDSize());
	Q_ASSERT(sizeof(qint64) == idSizes.methodIDSize());
	Q_ASSERT(sizeof(qint64) == idSizes.objectIDSize());
	Q_ASSERT(sizeof(qint64) == idSizes.referenceTypeIDSize());
	Q_ASSERT(sizeof(qint64) == idSizes.frameIDSize());
}

void DebugConnector::sendBreakAtStart()
{
	auto r = makeReply<Reply>(sendCommand(BreakClassLoad(mainClassName_)));
	Q_ASSERT(Protocol::Error::NONE == r.error());
	resume();
}

bool DebugConnector::resume()
{
	auto r = makeReply<Reply>(sendCommand(ResumeCommand()));
	return Protocol::Error::NONE == r.error();
}

qint64 DebugConnector::getClassId(const QString& signature)
{
	auto it = classIdMap_.find(signature);
	if (it == classIdMap_.end())
	{
		auto classesBySignature = makeReply<ClassesBySignature>(sendCommand(ClassesBySignatureCommand(signature)));
		Q_ASSERT(classesBySignature.classes().size() == 1);
		return classIdMap_[signature] = classesBySignature.classes()[0].typeID();
	}
	else
	{
		return it.value();
	}
}

qint64 DebugConnector::getMethodId(qint64 classId, const QString& signature)
{
	auto methods = makeReply<MethodsReply>(sendCommand(MethodsCommand(classId)));
	for (auto method : methods.methods())
	{
		// TODO: check for signature
		if (method.name() == signature) return method.methodID();
	}
	return -1;
}

int DebugConnector::sendBreakpoint(Location breakLocation)
{
	auto r = makeReply<EventSetReply>(sendCommand(BreakpointCommand(breakLocation)));
	return r.requestId();
}

void DebugConnector::handleComposite(QByteArray data)
{
	auto c = makeReply<CompositeCommand>(data);
	// If we receive a non composite event, we have an implementation error.
	Q_ASSERT(c.commandSet() == Protocol::CommandSet::Event);
	Q_ASSERT(c.command() == MessagePart::cast(Protocol::EventCommands::Composite));

	for (auto& event : c.events())
	{
		if (auto listener = eventListeners_[event.eventKind()])
			listener(event);
		else
			qDebug() << "EVENT" << static_cast<int>(event.eventKind());
	}
}

} /* namespace OODebug */
<|endoftext|>


<commit_before>/***********************************************************************************************************************
**
** Copyright (c) 2011, 2014 ETH Zurich
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
** following conditions are met:
**
**    * Redistributions of source code must retain the above copyright 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 the ETH Zurich nor the names of its contributors may be used to endorse or promote products
**      derived from this software without specific prior written permission.
**
**
** 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 HOLDER 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.
**
***********************************************************************************************************************/

#include "DebugConnector.h"

#include "Reply.h"
#include "Command.h"

#include "messages/AllMessages.h"

namespace OODebug {

DebugConnector::DebugConnector()
{
	QObject::connect(&tcpSocket_, static_cast<void (QTcpSocket::*)(QAbstractSocket::SocketError)>(&QTcpSocket::error),
						  this, &DebugConnector::handleSocketError);
}

DebugConnector::~DebugConnector()
{
	tcpSocket_.abort();
}

void DebugConnector::connect(QString vmHostName, int vmHostPort)
{
	if (tcpSocket_.isOpen())
		tcpSocket_.abort();
	QObject::disconnect(&tcpSocket_, &QTcpSocket::readyRead, this, &DebugConnector::dispatchEvents);
	Command::resetIds();
	// The connection setup is handled here:
	// First when we are connected we have to send the handshake. For receiving the hanshake reply,
	// we use a dedicated function which disconnects itself and connects the actual read function
	// to the readyRead slot. In this way we don't have the handshake handling in the normal read function.
	QObject::connect(&tcpSocket_, &QTcpSocket::readyRead, this, &DebugConnector::readHandshake);
	tcpSocket_.connectToHost(vmHostName, vmHostPort);
	tcpSocket_.waitForConnected();
	sendHandshake();
}

void DebugConnector::handleSocketError(QAbstractSocket::SocketError socketError)
{
	// If the VM just went off we will receive a remote closed error this expected:
	if (socketError == QAbstractSocket::RemoteHostClosedError && !vmAlive_) return;
	qDebug() << "Socket ERROR: " << socketError;
}

void DebugConnector::dispatchEvents()
{
	readFromSocket();
	auto it = messagesReadyForProcessing_.begin();
	while (it != messagesReadyForProcessing_.end())
	{
		auto data = *it;
		auto command = makeReply<Command>(data);
		if (command.commandSet() == Protocol::CommandSet::Event
			 && command.command() == MessagePart::cast(Protocol::EventCommands::Composite))
		{
			messagesReadyForProcessing_.erase(it);
			handleComposite(data);
			// The handling of one event might mess with our iterator so just emit the signal that we get called again
			// once the handling is done
			emit tcpSocket_.readyRead();
			break;
		}
		else
		{
			++it;
		}
	}
}

void DebugConnector::readFromSocket()
{
	QByteArray dataRead = tcpSocket_.readAll();
	// If we still have a part of a packet add it here.
	if (!incompleteData_.isEmpty())
	{
		dataRead.prepend(incompleteData_);
		incompleteData_ = QByteArray();
	}
	// If we haven't read enough retry later.
	if (dataRead.size() < int(sizeof(qint32)))
	{
		incompleteData_ = dataRead;
		return;
	}
	// check if the packet is complete
	QDataStream inStream(dataRead);
	qint32 packetLen;
	inStream >> packetLen;
	if (packetLen > dataRead.length())
	{
		// We don't have a complete packet yet, try later.
		incompleteData_ = dataRead;
		return;
	}
	// It might be that we received more than one message, so we store the additional data.
	if (packetLen < dataRead.length())
	{
		incompleteData_ = dataRead;
		incompleteData_.remove(0, packetLen);
		dataRead.remove(packetLen + 1, dataRead.length());
	}
	messagesReadyForProcessing_ << dataRead;
}

QByteArray DebugConnector::sendCommand(const Command& command)
{
	QByteArray raw;
	QDataStream stream(&raw, QIODevice::ReadWrite);
	stream << command;
	// Insert the length, it is always at position 0
	QByteArray len;
	qint32 dataLen = raw.length();
	QDataStream lenStream(&len, QIODevice::ReadWrite);
	lenStream << dataLen;
	raw.replace(0, len.length(), len);
	tcpSocket_.write(raw);
	tcpSocket_.waitForBytesWritten();

	return waitForReply(command.id());
}

QByteArray DebugConnector::waitForReply(qint32 requestId)
{
	while (true)
	{
		// First check if the data is already here
		for (int i = 0; i < messagesReadyForProcessing_.length(); ++i)
		{
			auto r = makeReply<Reply>(messagesReadyForProcessing_[i]);
			if (r.id() == requestId) return messagesReadyForProcessing_.takeAt(i);
		}
		tcpSocket_.waitForReadyRead();
		readFromSocket();
	}
}

void DebugConnector::readHandshake()
{
	QByteArray dataRead = tcpSocket_.readAll();
	Q_ASSERT(dataRead.length() >= Protocol::handshake.length());
	if (dataRead.startsWith(Protocol::handshake))
	{
		QObject::disconnect(&tcpSocket_, &QTcpSocket::readyRead, this, &DebugConnector::readHandshake);
		QObject::connect(&tcpSocket_, &QTcpSocket::readyRead, this, &DebugConnector::dispatchEvents);
		if (dataRead.length() > Protocol::handshake.length())
		{
			// remove the handshake
			dataRead.remove(0, Protocol::handshake.length());
			incompleteData_ = dataRead;
			// trigger reads such that we handle the additional data
			readFromSocket();
		}
		checkVersion();
		checkIdSizes();
		dispatchEvents();
	}
	else
	{
		// We didn't receive the handshake, this should never happen, so we just stop here.
		// In the future we should handle this somehow nicer.
		Q_ASSERT(false);
	}
}

void DebugConnector::sendHandshake()
{
	tcpSocket_.write(Protocol::handshake);
	tcpSocket_.waitForBytesWritten();
}

void DebugConnector::checkVersion()
{
	auto info = makeReply<VersionInfo>(sendCommand(VersionCommand()));
	qDebug() << "VM-INFO: " << info.jdwpMajor() << info.jdwpMinor();
}

void DebugConnector::checkIdSizes()
{
	// Our protocol is only designed for 64 bit machine make sure that this is the case:
	auto idSizes = makeReply<IDSizes>(sendCommand(IDSizeCommand()));
	Q_ASSERT(sizeof(qint64) == idSizes.fieldIDSize());
	Q_ASSERT(sizeof(qint64) == idSizes.methodIDSize());
	Q_ASSERT(sizeof(qint64) == idSizes.objectIDSize());
	Q_ASSERT(sizeof(qint64) == idSizes.referenceTypeIDSize());
	Q_ASSERT(sizeof(qint64) == idSizes.frameIDSize());
}

bool DebugConnector::resume()
{
	if (vmAlive_)
	{
		auto r = makeReply<Reply>(sendCommand(ResumeCommand()));
		return Protocol::Error::NONE == r.error();
	}
	return false;
}

qint64 DebugConnector::getClassId(const QString& signature)
{
	auto classesBySignature = makeReply<ClassesBySignature>(sendCommand(ClassesBySignatureCommand(signature)));
	if (classesBySignature.classes().size() < 1)
		return NO_RESULT;
	// If we have more than one class id for one signature, we should check what the situation is and
	// adapt the following code to handle this situation correctly.
	Q_ASSERT(classesBySignature.classes().size() == 1);
	return classesBySignature.classes()[0].typeID();
}

qint64 DebugConnector::getMethodId(qint64 classId, const QString& signature)
{
	auto methods = makeReply<MethodsReply>(sendCommand(MethodsCommand(classId)));
	for (auto method : methods.methods())
	{
		// TODO: check for signature
		if (method.name() == signature) return method.methodID();
	}
	return NO_RESULT;
}

LineTable DebugConnector::getLineTable(qint64 classId, qint64 methodId)
{
	return makeReply<LineTable>(sendCommand(LineTableCommand(classId, methodId)));
}

Frames DebugConnector::getFrames(qint64 threadId, qint32 numFrames, qint32 startFrame)
{
	return makeReply<Frames>(sendCommand(FramesCommand(threadId, startFrame, numFrames)));
}

VariableTable DebugConnector::getVariableTable(qint64 classId, qint64 methodId)
{
	return makeReply<VariableTable>(sendCommand(VariableTableCommand(classId, methodId)));
}

Values DebugConnector::getValues(qint64 threadId, qint64 frameId, QList<StackVariable> variables)
{
	return makeReply<Values>(sendCommand(GetValuesCommand(threadId, frameId, variables)));
}

QString DebugConnector::getString(qint64 stringId)
{
	auto reply = makeReply<StringValue>(sendCommand(StringValueCommand(stringId)));
	return reply.stringValue();
}

bool DebugConnector::breakAtClassLoad(QString className)
{
	auto r = makeReply<Reply>(sendCommand(BreakClassLoad(className)));
	return Protocol::Error::NONE == r.error();
}

int DebugConnector::setBreakpoint(Location breakLocation)
{
	auto r = makeReply<EventSetReply>(sendCommand(BreakpointCommand(breakLocation)));
	return r.requestId();
}

bool DebugConnector::clearBreakpoint(qint32 requestId)
{
	auto r = makeReply<Reply>(sendCommand(EventClearCommand(Protocol::EventKind::BREAKPOINT, requestId)));
	return r.error() == Protocol::Error::NONE;
}

void DebugConnector::handleComposite(QByteArray data)
{
	auto c = makeReply<CompositeCommand>(data);
	// If we receive a non composite event, we have an implementation error.
	Q_ASSERT(c.commandSet() == Protocol::CommandSet::Event);
	Q_ASSERT(c.command() == MessagePart::cast(Protocol::EventCommands::Composite));

	for (auto& event : c.events())
	{
		// We always want to know the VM state
		if (Protocol::EventKind::VM_START == event.eventKind())
			vmAlive_ = true;
		else if (Protocol::EventKind::VM_DEATH == event.eventKind())
			vmAlive_ = false;
		// Dispatch event to listeners
		if (auto listener = eventListeners_[event.eventKind()])
			listener(event);
		else
			qDebug() << "EVENT" << static_cast<int>(event.eventKind());
	}
}

} /* namespace OODebug */
<commit_msg>Use truncate instead of remove This is more readable and fixes the (+1) error<commit_after>/***********************************************************************************************************************
**
** Copyright (c) 2011, 2014 ETH Zurich
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
** following conditions are met:
**
**    * Redistributions of source code must retain the above copyright 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 the ETH Zurich nor the names of its contributors may be used to endorse or promote products
**      derived from this software without specific prior written permission.
**
**
** 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 HOLDER 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.
**
***********************************************************************************************************************/

#include "DebugConnector.h"

#include "Reply.h"
#include "Command.h"

#include "messages/AllMessages.h"

namespace OODebug {

DebugConnector::DebugConnector()
{
	QObject::connect(&tcpSocket_, static_cast<void (QTcpSocket::*)(QAbstractSocket::SocketError)>(&QTcpSocket::error),
						  this, &DebugConnector::handleSocketError);
}

DebugConnector::~DebugConnector()
{
	tcpSocket_.abort();
}

void DebugConnector::connect(QString vmHostName, int vmHostPort)
{
	if (tcpSocket_.isOpen())
		tcpSocket_.abort();
	QObject::disconnect(&tcpSocket_, &QTcpSocket::readyRead, this, &DebugConnector::dispatchEvents);
	Command::resetIds();
	// The connection setup is handled here:
	// First when we are connected we have to send the handshake. For receiving the hanshake reply,
	// we use a dedicated function which disconnects itself and connects the actual read function
	// to the readyRead slot. In this way we don't have the handshake handling in the normal read function.
	QObject::connect(&tcpSocket_, &QTcpSocket::readyRead, this, &DebugConnector::readHandshake);
	tcpSocket_.connectToHost(vmHostName, vmHostPort);
	tcpSocket_.waitForConnected();
	sendHandshake();
}

void DebugConnector::handleSocketError(QAbstractSocket::SocketError socketError)
{
	// If the VM just went off we will receive a remote closed error this expected:
	if (socketError == QAbstractSocket::RemoteHostClosedError && !vmAlive_) return;
	qDebug() << "Socket ERROR: " << socketError;
}

void DebugConnector::dispatchEvents()
{
	readFromSocket();
	auto it = messagesReadyForProcessing_.begin();
	while (it != messagesReadyForProcessing_.end())
	{
		auto data = *it;
		auto command = makeReply<Command>(data);
		if (command.commandSet() == Protocol::CommandSet::Event
			 && command.command() == MessagePart::cast(Protocol::EventCommands::Composite))
		{
			messagesReadyForProcessing_.erase(it);
			handleComposite(data);
			// The handling of one event might mess with our iterator so just emit the signal that we get called again
			// once the handling is done
			emit tcpSocket_.readyRead();
			break;
		}
		else
		{
			++it;
		}
	}
}

void DebugConnector::readFromSocket()
{
	QByteArray dataRead = tcpSocket_.readAll();
	// If we still have a part of a packet add it here.
	if (!incompleteData_.isEmpty())
	{
		dataRead.prepend(incompleteData_);
		incompleteData_ = QByteArray();
	}
	// If we haven't read enough retry later.
	if (dataRead.size() < int(sizeof(qint32)))
	{
		incompleteData_ = dataRead;
		return;
	}
	// check if the packet is complete
	QDataStream inStream(dataRead);
	qint32 packetLen;
	inStream >> packetLen;
	if (packetLen > dataRead.length())
	{
		// We don't have a complete packet yet, try later.
		incompleteData_ = dataRead;
		return;
	}
	// It might be that we received more than one message, so we store the additional data.
	if (packetLen < dataRead.length())
	{
		incompleteData_ = dataRead;
		incompleteData_.remove(0, packetLen);
		dataRead.truncate(packetLen);
	}
	messagesReadyForProcessing_ << dataRead;
}

QByteArray DebugConnector::sendCommand(const Command& command)
{
	QByteArray raw;
	QDataStream stream(&raw, QIODevice::ReadWrite);
	stream << command;
	// Insert the length, it is always at position 0
	QByteArray len;
	qint32 dataLen = raw.length();
	QDataStream lenStream(&len, QIODevice::ReadWrite);
	lenStream << dataLen;
	raw.replace(0, len.length(), len);
	tcpSocket_.write(raw);
	tcpSocket_.waitForBytesWritten();

	return waitForReply(command.id());
}

QByteArray DebugConnector::waitForReply(qint32 requestId)
{
	while (true)
	{
		// First check if the data is already here
		for (int i = 0; i < messagesReadyForProcessing_.length(); ++i)
		{
			auto r = makeReply<Reply>(messagesReadyForProcessing_[i]);
			if (r.id() == requestId) return messagesReadyForProcessing_.takeAt(i);
		}
		tcpSocket_.waitForReadyRead();
		readFromSocket();
	}
}

void DebugConnector::readHandshake()
{
	QByteArray dataRead = tcpSocket_.readAll();
	Q_ASSERT(dataRead.length() >= Protocol::handshake.length());
	if (dataRead.startsWith(Protocol::handshake))
	{
		QObject::disconnect(&tcpSocket_, &QTcpSocket::readyRead, this, &DebugConnector::readHandshake);
		QObject::connect(&tcpSocket_, &QTcpSocket::readyRead, this, &DebugConnector::dispatchEvents);
		if (dataRead.length() > Protocol::handshake.length())
		{
			// remove the handshake
			dataRead.remove(0, Protocol::handshake.length());
			incompleteData_ = dataRead;
			// trigger reads such that we handle the additional data
			readFromSocket();
		}
		checkVersion();
		checkIdSizes();
		dispatchEvents();
	}
	else
	{
		// We didn't receive the handshake, this should never happen, so we just stop here.
		// In the future we should handle this somehow nicer.
		Q_ASSERT(false);
	}
}

void DebugConnector::sendHandshake()
{
	tcpSocket_.write(Protocol::handshake);
	tcpSocket_.waitForBytesWritten();
}

void DebugConnector::checkVersion()
{
	auto info = makeReply<VersionInfo>(sendCommand(VersionCommand()));
	qDebug() << "VM-INFO: " << info.jdwpMajor() << info.jdwpMinor();
}

void DebugConnector::checkIdSizes()
{
	// Our protocol is only designed for 64 bit machine make sure that this is the case:
	auto idSizes = makeReply<IDSizes>(sendCommand(IDSizeCommand()));
	Q_ASSERT(sizeof(qint64) == idSizes.fieldIDSize());
	Q_ASSERT(sizeof(qint64) == idSizes.methodIDSize());
	Q_ASSERT(sizeof(qint64) == idSizes.objectIDSize());
	Q_ASSERT(sizeof(qint64) == idSizes.referenceTypeIDSize());
	Q_ASSERT(sizeof(qint64) == idSizes.frameIDSize());
}

bool DebugConnector::resume()
{
	if (vmAlive_)
	{
		auto r = makeReply<Reply>(sendCommand(ResumeCommand()));
		return Protocol::Error::NONE == r.error();
	}
	return false;
}

qint64 DebugConnector::getClassId(const QString& signature)
{
	auto classesBySignature = makeReply<ClassesBySignature>(sendCommand(ClassesBySignatureCommand(signature)));
	if (classesBySignature.classes().size() < 1)
		return NO_RESULT;
	// If we have more than one class id for one signature, we should check what the situation is and
	// adapt the following code to handle this situation correctly.
	Q_ASSERT(classesBySignature.classes().size() == 1);
	return classesBySignature.classes()[0].typeID();
}

qint64 DebugConnector::getMethodId(qint64 classId, const QString& signature)
{
	auto methods = makeReply<MethodsReply>(sendCommand(MethodsCommand(classId)));
	for (auto method : methods.methods())
	{
		// TODO: check for signature
		if (method.name() == signature) return method.methodID();
	}
	return NO_RESULT;
}

LineTable DebugConnector::getLineTable(qint64 classId, qint64 methodId)
{
	return makeReply<LineTable>(sendCommand(LineTableCommand(classId, methodId)));
}

Frames DebugConnector::getFrames(qint64 threadId, qint32 numFrames, qint32 startFrame)
{
	return makeReply<Frames>(sendCommand(FramesCommand(threadId, startFrame, numFrames)));
}

VariableTable DebugConnector::getVariableTable(qint64 classId, qint64 methodId)
{
	return makeReply<VariableTable>(sendCommand(VariableTableCommand(classId, methodId)));
}

Values DebugConnector::getValues(qint64 threadId, qint64 frameId, QList<StackVariable> variables)
{
	return makeReply<Values>(sendCommand(GetValuesCommand(threadId, frameId, variables)));
}

QString DebugConnector::getString(qint64 stringId)
{
	auto reply = makeReply<StringValue>(sendCommand(StringValueCommand(stringId)));
	return reply.stringValue();
}

bool DebugConnector::breakAtClassLoad(QString className)
{
	auto r = makeReply<Reply>(sendCommand(BreakClassLoad(className)));
	return Protocol::Error::NONE == r.error();
}

int DebugConnector::setBreakpoint(Location breakLocation)
{
	auto r = makeReply<EventSetReply>(sendCommand(BreakpointCommand(breakLocation)));
	return r.requestId();
}

bool DebugConnector::clearBreakpoint(qint32 requestId)
{
	auto r = makeReply<Reply>(sendCommand(EventClearCommand(Protocol::EventKind::BREAKPOINT, requestId)));
	return r.error() == Protocol::Error::NONE;
}

void DebugConnector::handleComposite(QByteArray data)
{
	auto c = makeReply<CompositeCommand>(data);
	// If we receive a non composite event, we have an implementation error.
	Q_ASSERT(c.commandSet() == Protocol::CommandSet::Event);
	Q_ASSERT(c.command() == MessagePart::cast(Protocol::EventCommands::Composite));

	for (auto& event : c.events())
	{
		// We always want to know the VM state
		if (Protocol::EventKind::VM_START == event.eventKind())
			vmAlive_ = true;
		else if (Protocol::EventKind::VM_DEATH == event.eventKind())
			vmAlive_ = false;
		// Dispatch event to listeners
		if (auto listener = eventListeners_[event.eventKind()])
			listener(event);
		else
			qDebug() << "EVENT" << static_cast<int>(event.eventKind());
	}
}

} /* namespace OODebug */
<|endoftext|>


<commit_before>// testContext.cpp
// Author:  Ayman Habib
/*
* Copyright (c)  2005, Stanford University. All rights reserved.
* Use of the OpenSim software in source form is permitted provided that the following
* conditions are met:
* 	1. The software is used only for non-commercial research and education. It may not
*     be used in relation to any commercial activity.
* 	2. The software is not distributed or redistributed.  Software distribution is allowed
*     only through https://simtk.org/home/opensim.
* 	3. Use of the OpenSim software or derivatives must be acknowledged in all publications,
*      presentations, or documents describing work in which OpenSim or derivatives are used.
* 	4. Credits to developers may not be removed from executables
*     created from modifications of the source.
* 	5. Modifications of source code must retain the above copyright notice, this list of
*     conditions and the following disclaimer.
*
*  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;
*  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
*  OR BUSINESS INTERRUPTION) 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.
*/
#include "../OpenSimContext.h"
#include <OpenSim/Common/Function.h>
#include <OpenSim/Common/MarkerData.h>
#include <OpenSim/Simulation/SimbodyEngine/Body.h>
#include <OpenSim/Simulation/SimbodyEngine/Coordinate.h>
#include <OpenSim/Simulation/Model/Marker.h>
#include <OpenSim/Simulation/Model/Model.h>
#include <OpenSim/Simulation/Model/BodySet.h>
#include <OpenSim/Simulation/Model/MovingPathPoint.h>
#include <OpenSim/Simulation/Model/Muscle.h>
#include <OpenSim/Simulation/Model/PathPoint.h>
#include <OpenSim/Simulation/Wrap/PathWrap.h>
#include <OpenSim/Simulation/Model/ConditionalPathPoint.h>
#include <OpenSim/Simulation/SimbodyEngine/SimbodyEngine.h>
#include <OpenSim/Simulation/Wrap/WrapObject.h>
#include <OpenSim/Simulation/Model/Analysis.h>
#include <OpenSim/Simulation/Model/MarkerSet.h>
#include <OpenSim/Tools/AnalyzeTool.h>
#include <OpenSim/Tools/ModelScaler.h>
#include <OpenSim/Tools/Measurement.h>
#include <OpenSim/Tools/MarkerPlacer.h>
#include <OpenSim/Simulation/Model/ForceSet.h>
#include <OpenSim/Common/LoadOpenSimLibrary.h>
#include <OpenSim/Actuators/Thelen2003Muscle.h>

using namespace OpenSim;
using namespace SimTK;
using namespace std;

#define ASSERT_EQUAL(expected, found, tolerance) { \
double tol = std::max((tolerance), std::abs((expected)*(tolerance))); \
if ((found)<(expected)-(tol) || (found)>(expected)+(tol)) throw(exception());}

int main()
{
    int  status = 0;

	// To Retrace the steps taken by the GUI this test case follows the same call sequence:
	// new Model(file)
	// new OpenSimContext(model.initSystem(), model);
	// context.updateDisplayer(muscle)	// to display muscles
	// context.getCurrentPath(muscle)
	// context.getTransform(body)
	// context.transformPosition(body, loc, global)  // to display markers
	// context.getLocked(Coordinate)
	// context.getValue(cooridnate)
	LoadOpenSimLibrary("osimActuators");
	LoadOpenSimLibrary("osimSimulation");
	LoadOpenSimLibrary("osimJavaJNI");

	Model *model = new Model("wrist.osim");
	OpenSimContext* context = new OpenSimContext(&model->initSystem(), model);
	const ForceSet& fs = model->getForceSet();
	int n1 = fs.getNumGroups();
	const ObjectGroup* grp = fs.getGroup("wrist");
	assert(grp);
	const Array<Object*>& members = grp->getMembers();
	int sz = members.getSize();
	ASSERT_EQUAL(sz,5,0);
	assert(members.get(0)->getName()=="ECRB");
	delete model;
	delete context;
	model = new Model("arm26_20.osim");
	context = new OpenSimContext(&model->initSystem(), model);
	Array<std::string> stateNames = model->getStateVariableNames();
	OpenSim::Force* dForce=&(model->updForceSet().get("TRIlong"));
	Muscle* dTRIlong = dynamic_cast<Muscle*>(dForce);
	assert(dTRIlong);
    OpenSim::Thelen2003Muscle* thelenMsl = dynamic_cast<Thelen2003Muscle*>(dTRIlong);
    AbstractProperty& dProp = thelenMsl->updPropertyByName("ignore_tendon_compliance");
    PropertyHelper::setValueBool(true, dProp);
    cout << "Prop after is " << dProp.toString() << endl;
    context->recreateSystemAfterSystemExistsKeepStage();
    AbstractProperty& dProp2 = thelenMsl->updPropertyByName("ignore_tendon_compliance");
    cout << "Prop after create system" << dProp2.toString() << endl;
    bool after = PropertyHelper::getValueBool(dProp);
    assert(after);
	context->updateDisplayer(*dTRIlong);
	const OpenSim::Array<PathPoint*>& path = context->getCurrentPath(*dTRIlong);
	cout << "Muscle Path" << endl;
	cout << path.getSize() << endl;
	for(int i=0; i< path.getSize(); i++)
		cout << path.get(i)->getBodyName() << path.get(i)->getLocation() << endl;
	// Compare to known path
	const OpenSim::Body& dBody = model->getBodySet().get("r_ulna_radius_hand");
	Transform xform = context->getTransform(dBody);
	cout << xform << endl;
	double flat[16];
	context->getTransformAsDouble16(xform, flat);
	// Compare to known xform
	double markerPosition[] = {.005000000000, -0.290400000000, 0.030000000000};
	double markerPositionInGround[3];
	context->transformPosition(dBody, markerPosition, markerPositionInGround);  // to display markers
	cout << "Global frame position = " << markerPositionInGround[0] <<
		markerPositionInGround[1] << markerPositionInGround[2]<< endl;
	// Check xformed point against known position
	const Coordinate& dr_elbow_flex = model->getCoordinateSet().get("r_elbow_flex");
	bool isLocked = context->getLocked(dr_elbow_flex);
	assert(!isLocked);
	double startValue = context->getValue(dr_elbow_flex);
	cout << "Coordinate start value = " << startValue << endl;
	double length1 = context->getMuscleLength(*dTRIlong);
	cout << length1 << endl;
	ASSERT_EQUAL(.277609, length1, 1e-5);
	// Coordinate Slider
	context->setValue(dr_elbow_flex, 100*SimTK_PI/180.);
	// Get body transform, marker position and muscle path (tests wrapping as well)
	xform = context->getTransform(dBody);
	cout << "After setting coordinate to 100 deg." << endl;
	cout << xform << endl;
	// Compare to known xform
	context->updateDisplayer(*dTRIlong);
	const OpenSim::Array<PathPoint*>& newPath = context->getCurrentPath(*dTRIlong);
	// Compare to known path
	cout << "New Muscle Path" << endl;
	cout << path.getSize() << endl;
	for(int i=0; i< path.getSize(); i++)
		cout << path.get(i)->getBodyName() << path.get(i)->getLocation() << endl;
	double length2 = context->getMuscleLength(*dTRIlong);
	cout << length2 << endl;
	ASSERT_EQUAL(.315748, length2, 1e-5);
	// Analyze Tool for plotting?
	return status;
}

<commit_msg>Fix typo in message<commit_after>// testContext.cpp
// Author:  Ayman Habib
/*
* Copyright (c)  2005, Stanford University. All rights reserved.
* Use of the OpenSim software in source form is permitted provided that the following
* conditions are met:
* 	1. The software is used only for non-commercial research and education. It may not
*     be used in relation to any commercial activity.
* 	2. The software is not distributed or redistributed.  Software distribution is allowed
*     only through https://simtk.org/home/opensim.
* 	3. Use of the OpenSim software or derivatives must be acknowledged in all publications,
*      presentations, or documents describing work in which OpenSim or derivatives are used.
* 	4. Credits to developers may not be removed from executables
*     created from modifications of the source.
* 	5. Modifications of source code must retain the above copyright notice, this list of
*     conditions and the following disclaimer.
*
*  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;
*  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
*  OR BUSINESS INTERRUPTION) 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.
*/
#include "../OpenSimContext.h"
#include <OpenSim/Common/Function.h>
#include <OpenSim/Common/MarkerData.h>
#include <OpenSim/Simulation/SimbodyEngine/Body.h>
#include <OpenSim/Simulation/SimbodyEngine/Coordinate.h>
#include <OpenSim/Simulation/Model/Marker.h>
#include <OpenSim/Simulation/Model/Model.h>
#include <OpenSim/Simulation/Model/BodySet.h>
#include <OpenSim/Simulation/Model/MovingPathPoint.h>
#include <OpenSim/Simulation/Model/Muscle.h>
#include <OpenSim/Simulation/Model/PathPoint.h>
#include <OpenSim/Simulation/Wrap/PathWrap.h>
#include <OpenSim/Simulation/Model/ConditionalPathPoint.h>
#include <OpenSim/Simulation/SimbodyEngine/SimbodyEngine.h>
#include <OpenSim/Simulation/Wrap/WrapObject.h>
#include <OpenSim/Simulation/Model/Analysis.h>
#include <OpenSim/Simulation/Model/MarkerSet.h>
#include <OpenSim/Tools/AnalyzeTool.h>
#include <OpenSim/Tools/ModelScaler.h>
#include <OpenSim/Tools/Measurement.h>
#include <OpenSim/Tools/MarkerPlacer.h>
#include <OpenSim/Simulation/Model/ForceSet.h>
#include <OpenSim/Common/LoadOpenSimLibrary.h>
#include <OpenSim/Actuators/Thelen2003Muscle.h>

using namespace OpenSim;
using namespace SimTK;
using namespace std;

#define ASSERT_EQUAL(expected, found, tolerance) { \
double tol = std::max((tolerance), std::abs((expected)*(tolerance))); \
if ((found)<(expected)-(tol) || (found)>(expected)+(tol)) throw(exception());}

int main()
{
    int  status = 0;

	// To Retrace the steps taken by the GUI this test case follows the same call sequence:
	// new Model(file)
	// new OpenSimContext(model.initSystem(), model);
	// context.updateDisplayer(muscle)	// to display muscles
	// context.getCurrentPath(muscle)
	// context.getTransform(body)
	// context.transformPosition(body, loc, global)  // to display markers
	// context.getLocked(Coordinate)
	// context.getValue(cooridnate)
	LoadOpenSimLibrary("osimActuators");
	LoadOpenSimLibrary("osimSimulation");
	LoadOpenSimLibrary("osimJavaJNI");

	Model *model = new Model("wrist.osim");
	OpenSimContext* context = new OpenSimContext(&model->initSystem(), model);
	const ForceSet& fs = model->getForceSet();
	int n1 = fs.getNumGroups();
	const ObjectGroup* grp = fs.getGroup("wrist");
	assert(grp);
	const Array<Object*>& members = grp->getMembers();
	int sz = members.getSize();
	ASSERT_EQUAL(sz,5,0);
	assert(members.get(0)->getName()=="ECRB");
	delete model;
	delete context;
	model = new Model("arm26_20.osim");
	context = new OpenSimContext(&model->initSystem(), model);
	Array<std::string> stateNames = model->getStateVariableNames();
	OpenSim::Force* dForce=&(model->updForceSet().get("TRIlong"));
	Muscle* dTRIlong = dynamic_cast<Muscle*>(dForce);
	assert(dTRIlong);
    OpenSim::Thelen2003Muscle* thelenMsl = dynamic_cast<Thelen2003Muscle*>(dTRIlong);
    AbstractProperty& dProp = thelenMsl->updPropertyByName("ignore_tendon_compliance");
    PropertyHelper::setValueBool(true, dProp);
    cout << "Prop after is " << dProp.toString() << endl;
    context->recreateSystemAfterSystemExistsKeepStage();
    AbstractProperty& dProp2 = thelenMsl->updPropertyByName("ignore_tendon_compliance");
    cout << "Prop after create system is " << dProp2.toString() << endl;
    bool after = PropertyHelper::getValueBool(dProp);
    assert(after);
	context->updateDisplayer(*dTRIlong);
	const OpenSim::Array<PathPoint*>& path = context->getCurrentPath(*dTRIlong);
	cout << "Muscle Path" << endl;
	cout << path.getSize() << endl;
	for(int i=0; i< path.getSize(); i++)
		cout << path.get(i)->getBodyName() << path.get(i)->getLocation() << endl;
	// Compare to known path
	const OpenSim::Body& dBody = model->getBodySet().get("r_ulna_radius_hand");
	Transform xform = context->getTransform(dBody);
	cout << xform << endl;
	double flat[16];
	context->getTransformAsDouble16(xform, flat);
	// Compare to known xform
	double markerPosition[] = {.005000000000, -0.290400000000, 0.030000000000};
	double markerPositionInGround[3];
	context->transformPosition(dBody, markerPosition, markerPositionInGround);  // to display markers
	cout << "Global frame position = " << markerPositionInGround[0] <<
		markerPositionInGround[1] << markerPositionInGround[2]<< endl;
	// Check xformed point against known position
	const Coordinate& dr_elbow_flex = model->getCoordinateSet().get("r_elbow_flex");
	bool isLocked = context->getLocked(dr_elbow_flex);
	assert(!isLocked);
	double startValue = context->getValue(dr_elbow_flex);
	cout << "Coordinate start value = " << startValue << endl;
	double length1 = context->getMuscleLength(*dTRIlong);
	cout << length1 << endl;
	ASSERT_EQUAL(.277609, length1, 1e-5);
	// Coordinate Slider
	context->setValue(dr_elbow_flex, 100*SimTK_PI/180.);
	// Get body transform, marker position and muscle path (tests wrapping as well)
	xform = context->getTransform(dBody);
	cout << "After setting coordinate to 100 deg." << endl;
	cout << xform << endl;
	// Compare to known xform
	context->updateDisplayer(*dTRIlong);
	const OpenSim::Array<PathPoint*>& newPath = context->getCurrentPath(*dTRIlong);
	// Compare to known path
	cout << "New Muscle Path" << endl;
	cout << path.getSize() << endl;
	for(int i=0; i< path.getSize(); i++)
		cout << path.get(i)->getBodyName() << path.get(i)->getLocation() << endl;
	double length2 = context->getMuscleLength(*dTRIlong);
	cout << length2 << endl;
	ASSERT_EQUAL(.315748, length2, 1e-5);
	// Analyze Tool for plotting?
	return status;
}

<|endoftext|>


<commit_before>AliTender *AddTaskEMCALTender(const char *geoname="EMCAL_COMPLETEV1", const char* datatype="pp")
{
  // Parameters: geoname = "EMCAL_FIRSTYEARV1" or "EMCAL_COMPLETEV1" or ""

  // Get the pointer to the existing analysis manager via the static access method.
  //==============================================================================
  AliAnalysisManager *mgr = AliAnalysisManager::GetAnalysisManager();
  if (!mgr) {
    ::Error("AddTaskTrgContam", "No analysis manager to connect to.");
    return NULL;
  }

  // Create the task and configure it.
  //===========================================================================
  AliTender* ana = new  AliTender("AliTender");

  ana->SelectCollisionCandidates( AliVEvent::kEMC1 | AliVEvent::kMB | AliVEvent::kEMC7 | AliVEvent::kINT7);

  Bool_t ismc = (mgr->GetMCtruthEventHandler() != NULL);

  mgr->AddTask(ana);
  // Adding EMCAL supply
  AliEMCALTenderSupply *EMCALSupply=new AliEMCALTenderSupply("EMCALtender");
  EMCALSupply->SetDebugLevel(2);

  AliEMCALRecParam *params = new AliEMCALRecParam();
  params->SetClusteringThreshold(0.1); // 100 MeV
  params->SetMinECut(0.05); //50 MeV
  params->SetW0(4.5);
  params->SetTimeCut(1e6);//Open this cut for AODs
  params->SetTimeMin(-1);//Open this cut for AODs
  params->SetTimeMax(1e6);//Open this cut for AODs
  EMCALSupply->SetRecParam(params);

  EMCALSupply->SetEMCALGeometryName(geoname);

  EMCALSupply->SwitchOffCellFiducialRegion(); //Does NOT remove edge clusters
  if (!ismc) {
    if (1){//datatype == "pp") {
      //::Info("AddTaskEMCALTender", "USING pp data configuration...");
      //params->SetClusterizerFlag(AliEMCALRecParam::kClusterizerv2); //Std for pbpb
      EMCALSupply->SwitchOnRecalDistBadChannel();
      EMCALSupply->SwitchOnReCalibrateCluster();
      EMCALSupply->SwitchOnRecalculateClusPos();
      //EMCALSupply->SetNonLinearityFunction(AliEMCALTenderSupply::kBeamTestCorrected);
      //EMCALSupply->SwitchOnUpdateCell(); // will update cells and reclusterize
      //EMCALSupply->SwitchOnReclustering(); //SwitchOnReclustering if needed
    } else {
      //::Info("AddTaskEMCALTender", "USING PbPb data configuration...");
      //params->SetClusterizerFlag(AliEMCALRecParam::kClusterizerv2); //Std for pbpb
      EMCALSupply->SwitchOnCalibrateTime(); //
      EMCALSupply->SwitchOnUpdateCell(); // will update cells and reclusterize
      //EMCALSupply->SwitchOnReclustering(); //SwitchOnReclustering if needed
    }
  } else {
    ::Info("AddTaskEMCALTender", "USING MC configuration...");
  }
  EMCALSupply->SetMass(0.139);
  //EMCALSupply->SetStep(5);
  //EMCALSupply->SwitchOnCutEtaPhiSum();
  //EMCALSupply->SetRCut(0.0025);

  EMCALSupply->SwitchOnCutEtaPhiSeparate();
  EMCALSupply->SetEtaCut(0.025);
  EMCALSupply->SetPhiCut(0.05);

  ana->AddSupply(EMCALSupply);
  // Create ONLY the output containers for the data produced by the task.
  // Get and connect other common input/output containers via the manager as below
  //==============================================================================
  AliAnalysisDataContainer *coutput1 =
    mgr->CreateContainer("tender_event",
                         AliESDEvent::Class(),
                         AliAnalysisManager::kExchangeContainer,
                         "default_tender");
  mgr->ConnectInput  (ana, 0, mgr->GetCommonInputContainer());
  mgr->ConnectOutput (ana, 1, coutput1 );

  return ana;
}
<commit_msg>needs further cleanup<commit_after>// $Id$

AliTender *AddTaskEMCALTender(
  const char *geoname="EMCAL_COMPLETEV1",
  const char* datatype="pp")
{
  // Parameters: geoname = "EMCAL_FIRSTYEARV1" or "EMCAL_COMPLETEV1" or ""

  // Get the pointer to the existing analysis manager via the static access method.
  //==============================================================================
  AliAnalysisManager *mgr = AliAnalysisManager::GetAnalysisManager();
  if (!mgr) {
    ::Error("AddTaskTrgContam", "No analysis manager to connect to.");
    return NULL;
  }

  // Create the task and configure it.
  //===========================================================================
  AliTender* ana = new  AliTender("AliTender");

  ana->SelectCollisionCandidates( AliVEvent::kEMC1 | AliVEvent::kMB | AliVEvent::kEMC7 | AliVEvent::kINT7);

  Bool_t ismc = (mgr->GetMCtruthEventHandler() != NULL);

  mgr->AddTask(ana);
  // Adding EMCAL supply
  AliEMCALTenderSupply *EMCALSupply=new AliEMCALTenderSupply("EMCALtender");
  EMCALSupply->SetDebugLevel(2);

  AliEMCALRecParam *params = new AliEMCALRecParam();
  params->SetClusteringThreshold(0.1); // 100 MeV
  params->SetMinECut(0.05); //50 MeV
  params->SetW0(4.5);
  params->SetTimeCut(1e6);//Open this cut for AODs
  params->SetTimeMin(-1);//Open this cut for AODs
  params->SetTimeMax(1e6);//Open this cut for AODs
  EMCALSupply->SetRecParam(params);

  EMCALSupply->SetEMCALGeometryName(geoname);

  EMCALSupply->SwitchOffCellFiducialRegion(); //Does NOT remove edge clusters
  if (!ismc) {
    if (1){//datatype == "pp") {
      //::Info("AddTaskEMCALTender", "USING pp data configuration...");
      //params->SetClusterizerFlag(AliEMCALRecParam::kClusterizerv2); //Std for pbpb
      EMCALSupply->SwitchOnRecalDistBadChannel();
      EMCALSupply->SwitchOnReCalibrateCluster();
      EMCALSupply->SwitchOnRecalculateClusPos();
      //EMCALSupply->SetNonLinearityFunction(AliEMCALTenderSupply::kBeamTestCorrected);
      //EMCALSupply->SwitchOnUpdateCell(); // will update cells and reclusterize
      //EMCALSupply->SwitchOnReclustering(); //SwitchOnReclustering if needed
    } else {
      //::Info("AddTaskEMCALTender", "USING PbPb data configuration...");
      //params->SetClusterizerFlag(AliEMCALRecParam::kClusterizerv2); //Std for pbpb
      EMCALSupply->SwitchOnCalibrateTime(); //
      EMCALSupply->SwitchOnUpdateCell(); // will update cells and reclusterize
      //EMCALSupply->SwitchOnReclustering(); //SwitchOnReclustering if needed
    }
  } else {
    ::Info("AddTaskEMCALTender", "USING MC configuration...");
  }
  EMCALSupply->SetMass(0.139);
  //EMCALSupply->SetStep(5);
  //EMCALSupply->SwitchOnCutEtaPhiSum();
  //EMCALSupply->SetRCut(0.0025);

  EMCALSupply->SwitchOnCutEtaPhiSeparate();
  EMCALSupply->SetEtaCut(0.025);
  EMCALSupply->SetPhiCut(0.05);

  ana->AddSupply(EMCALSupply);
  // Create ONLY the output containers for the data produced by the task.
  // Get and connect other common input/output containers via the manager as below
  //==============================================================================
  AliAnalysisDataContainer *coutput1 =
    mgr->CreateContainer("tender_event",
                         AliESDEvent::Class(),
                         AliAnalysisManager::kExchangeContainer,
                         "default_tender");
  mgr->ConnectInput  (ana, 0, mgr->GetCommonInputContainer());
  mgr->ConnectOutput (ana, 1, coutput1 );

  return ana;
}
<|endoftext|>


<commit_before>/*------------------------------------------------------------------------------

    Copyright (c) 2004 Media Development Loan Fund

    This file is part of the Campcaster project.
    http://campcaster.campware.org/
    To report bugs, send an e-mail to bugs@campware.org

    Campcaster is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Campcaster is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Campcaster; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Author   : $Author$
    Version  : $Revision$
    Location : $URL$

------------------------------------------------------------------------------*/

/* ============================================================ include files */

#ifdef HAVE_CONFIG_H
#include "configure.h"
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#else
#error need unistd.h
#endif

#include <iostream>

#define DEBUG_PREFIX "Scheduler"
#include "LiveSupport/Core/Debug.h"

#include "LiveSupport/Core/TimeConversion.h"
#include "LiveSupport/Core/SessionId.h"

#include "PlaylistEvent.h"

using namespace boost;

using namespace LiveSupport::Core;
using namespace LiveSupport::Scheduler;

/* ===================================================  local data structures */

/* ================================================  local constants & macros */

/* ===============================================  local function prototypes */

/* =============================================================  module code */

/*------------------------------------------------------------------------------
 *  Constructor.
 *----------------------------------------------------------------------------*/
PlaylistEvent :: PlaylistEvent(
                        Ptr<SessionId>::Ref                 sessionId,
                        Ptr<AudioPlayerInterface>::Ref      audioPlayer,
                        Ptr<StorageClientInterface>::Ref    storage,
                        Ptr<PlayLogInterface>::Ref          playLog,
                        Ptr<ScheduleEntry>::Ref             scheduleEntry)
                                                                    throw ()
{
    this->sessionId     = sessionId;
    this->audioPlayer   = audioPlayer;
    this->storage       = storage;
    this->playLog       = playLog;
    this->scheduleEntry = scheduleEntry;

    // this init time is a wild guess, say 5 seconds should be enough
    initTime.reset(new posix_time::time_duration(0, 0, 10, 0));

    state = created;
}

/*------------------------------------------------------------------------------
 *  Initialize the event object.
 *----------------------------------------------------------------------------*/
void
PlaylistEvent :: initialize(void)                  throw (std::exception)
{
    //DEBUG_BLOCK

    if (state != created) {
        throw std::logic_error("PlaylistEvent in bad state");
    }

    state = initializing;
    // some ugliness because getPlaylistId() returns a const pointer
    Ptr<UniqueId>::Ref    playlistId(new UniqueId(scheduleEntry->getPlaylistId()
                                                               ->getId()));
    try {
        playlist = storage->acquirePlaylist(sessionId, playlistId);
    } catch (Core::XmlRpcException &e) {
        std::string     errorMessage = "storage server error: ";
        errorMessage += e.what();
        throw std::logic_error(errorMessage);
    }

    audioPlayer->preload(*playlist->getUri());
    state = initialized;
}

/*------------------------------------------------------------------------------
 *  De-initialize the event object.
 *----------------------------------------------------------------------------*/
void
PlaylistEvent :: deInitialize(void)                throw ()
{
    //DEBUG_BLOCK

    if (state != stopped) {
        // TODO: handle error?
        return;
    }

    try {
        storage->releasePlaylist(playlist);
    } catch (XmlRpcException &e) {
        std::cerr << e.what() << std::endl;
        // TODO: handle error?
    }
    playlist.reset();
    state = deInitialized;
}

/*------------------------------------------------------------------------------
 *  Initialize the event object.
 *----------------------------------------------------------------------------*/
void
PlaylistEvent :: start(void)                       throw ()
{
    //DEBUG_BLOCK

    if (state != initialized) {
        // TODO: handle error?
        return;
    }

    try {
        audioPlayer->open(*playlist->getUri());
        audioPlayer->start();

        playLog->addPlayLogEntry(playlist->getId(), TimeConversion::now());
    } catch (std::invalid_argument &e) {
        std::cerr << e.what() << std::endl;
        // TODO: handle error?
    } catch (std::runtime_error &e) {
        std::cerr << e.what() << std::endl;
        // TODO: handle error?
    }
    state = running;
}

/*------------------------------------------------------------------------------
 *  Initialize the event object.
 *----------------------------------------------------------------------------*/
void
PlaylistEvent :: stop(void)                        throw ()
{
    //DEBUG_BLOCK

    if (state != running) {
        // TODO: handle error?
        return;
    }

    try {
        audioPlayer->stop();
        audioPlayer->close();
    } catch (std::logic_error &e) {
        // TODO: handle error
        // NOTE: this may not be an error, because the user may have stopped
        // the playback manually (see Scheduler::StopCurrentlyPlayingMethod)
        std::cerr << "PlaylistEvent::stop error: " << std::endl;
        std::cerr << e.what() << std::endl;
    }
    state = stopped;
}

<commit_msg>Add error message.<commit_after>/*------------------------------------------------------------------------------

    Copyright (c) 2004 Media Development Loan Fund

    This file is part of the Campcaster project.
    http://campcaster.campware.org/
    To report bugs, send an e-mail to bugs@campware.org

    Campcaster is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Campcaster is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Campcaster; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Author   : $Author$
    Version  : $Revision$
    Location : $URL$

------------------------------------------------------------------------------*/

/* ============================================================ include files */

#ifdef HAVE_CONFIG_H
#include "configure.h"
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#else
#error need unistd.h
#endif

#include <iostream>

#define DEBUG_PREFIX "Scheduler"
#include "LiveSupport/Core/Debug.h"

#include "LiveSupport/Core/TimeConversion.h"
#include "LiveSupport/Core/SessionId.h"

#include "PlaylistEvent.h"

using namespace boost;

using namespace LiveSupport::Core;
using namespace LiveSupport::Scheduler;

/* ===================================================  local data structures */

/* ================================================  local constants & macros */

/* ===============================================  local function prototypes */

/* =============================================================  module code */

/*------------------------------------------------------------------------------
 *  Constructor.
 *----------------------------------------------------------------------------*/
PlaylistEvent :: PlaylistEvent(
                        Ptr<SessionId>::Ref                 sessionId,
                        Ptr<AudioPlayerInterface>::Ref      audioPlayer,
                        Ptr<StorageClientInterface>::Ref    storage,
                        Ptr<PlayLogInterface>::Ref          playLog,
                        Ptr<ScheduleEntry>::Ref             scheduleEntry)
                                                                    throw ()
{
    this->sessionId     = sessionId;
    this->audioPlayer   = audioPlayer;
    this->storage       = storage;
    this->playLog       = playLog;
    this->scheduleEntry = scheduleEntry;

    // this init time is a wild guess, say 5 seconds should be enough
    initTime.reset(new posix_time::time_duration(0, 0, 10, 0));

    state = created;
}

/*------------------------------------------------------------------------------
 *  Initialize the event object.
 *----------------------------------------------------------------------------*/
void
PlaylistEvent :: initialize(void)                  throw (std::exception)
{
    //DEBUG_BLOCK

    if (state != created) {
        throw std::logic_error("PlaylistEvent in bad state");
    }

    state = initializing;
    // some ugliness because getPlaylistId() returns a const pointer
    Ptr<UniqueId>::Ref    playlistId(new UniqueId(scheduleEntry->getPlaylistId()
                                                               ->getId()));
    try {
        playlist = storage->acquirePlaylist(sessionId, playlistId);
    } catch (Core::XmlRpcException &e) {
        std::string     errorMessage = "storage server error: ";
        errorMessage += e.what();
        throw std::logic_error(errorMessage);
    }

    audioPlayer->preload(*playlist->getUri());
    state = initialized;
}

/*------------------------------------------------------------------------------
 *  De-initialize the event object.
 *----------------------------------------------------------------------------*/
void
PlaylistEvent :: deInitialize(void)                throw ()
{
    DEBUG_BLOCK

    if (state != stopped) {
        // TODO: handle error?
        return;
    }

    try {
        storage->releasePlaylist(playlist);
    } catch (XmlRpcException &e) {
        std::cerr << e.what() << std::endl;
        // TODO: handle error?
    }
    playlist.reset();
    state = deInitialized;
}

/*------------------------------------------------------------------------------
 *  Initialize the event object.
 *----------------------------------------------------------------------------*/
void
PlaylistEvent :: start(void)                       throw ()
{
    DEBUG_BLOCK

    if (state != initialized) {
        // TODO: handle error?
        error() << "Not initialized. Aborting." << endl;
        return;
    }

    try {
        audioPlayer->open(*playlist->getUri());
        audioPlayer->start();

        playLog->addPlayLogEntry(playlist->getId(), TimeConversion::now());
    } catch (std::invalid_argument &e) {
        std::cerr << e.what() << std::endl;
        // TODO: handle error?
    } catch (std::runtime_error &e) {
        std::cerr << e.what() << std::endl;
        // TODO: handle error?
    }
    state = running;
}

/*------------------------------------------------------------------------------
 *  Initialize the event object.
 *----------------------------------------------------------------------------*/
void
PlaylistEvent :: stop(void)                        throw ()
{
    DEBUG_BLOCK

    if (state != running) {
        // TODO: handle error?
        return;
    }

    try {
        audioPlayer->stop();
        audioPlayer->close();
    } catch (std::logic_error &e) {
        // TODO: handle error
        // NOTE: this may not be an error, because the user may have stopped
        // the playback manually (see Scheduler::StopCurrentlyPlayingMethod)
        std::cerr << "PlaylistEvent::stop error: " << std::endl;
        std::cerr << e.what() << std::endl;
    }
    state = stopped;
}

<|endoftext|>


<commit_before><commit_msg>convert remnants of String to CHART2 module to OUString<commit_after><|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: DataPointItemConverter.hxx,v $
 *
 *  $Revision: 1.10 $
 *
 *  last change: $Author: rt $ $Date: 2007-07-25 08:39:12 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#ifndef CHART_DATAPOINTITEMCONVERTER_HXX
#define CHART_DATAPOINTITEMCONVERTER_HXX

#include "ItemConverter.hxx"
#include "GraphicPropertyItemConverter.hxx"
#include "chartview/NumberFormatterWrapper.hxx"

#ifndef _COM_SUN_STAR_AWT_SIZE_HPP_
#include <com/sun/star/awt/Size.hpp>
#endif
#ifndef _COM_SUN_STAR_FRAME_XMODEL_HPP_
#include <com/sun/star/frame/XModel.hpp>
#endif
#ifndef _COM_SUN_STAR_UNO_XCOMPONENTCONTEXT_HPP_
#include <com/sun/star/uno/XComponentContext.hpp>
#endif

#include <memory>
#include <vector>

class SdrModel;

namespace chart
{
namespace wrapper
{

class DataPointItemConverter :
        public ::comphelper::ItemConverter
{
public:
    DataPointItemConverter(
        const ::com::sun::star::uno::Reference<
            ::com::sun::star::frame::XModel > & xChartModel,
        const ::com::sun::star::uno::Reference<
            ::com::sun::star::uno::XComponentContext > & xContext,
        const ::com::sun::star::uno::Reference<
            ::com::sun::star::beans::XPropertySet > & rPropertySet,
        SfxItemPool& rItemPool,
        SdrModel& rDrawModel,
        NumberFormatterWrapper * pNumFormatter,
        const ::com::sun::star::uno::Reference<
            ::com::sun::star::lang::XMultiServiceFactory > & xNamedPropertyContainerFactory,
        GraphicPropertyItemConverter::eGraphicObjectType eMapTo =
            GraphicPropertyItemConverter::FILLED_DATA_POINT,
        ::std::auto_ptr< ::com::sun::star::awt::Size > pRefSize =
            ::std::auto_ptr< ::com::sun::star::awt::Size >(),
        bool bDataSeries = false,
        bool bUseSpecialFillColor = false,
        sal_Int32 nSpecialFillColor = 0,
        bool bOverwriteLabelsForAttributedDataPointsAlso=false );

    virtual ~DataPointItemConverter();

    virtual void FillItemSet( SfxItemSet & rOutItemSet ) const;
    virtual bool ApplyItemSet( const SfxItemSet & rItemSet );

protected:
    virtual const USHORT * GetWhichPairs() const;
    virtual bool GetItemProperty( tWhichIdType nWhichId, tPropertyNameWithMemberId & rOutProperty ) const;

    virtual void FillSpecialItem( USHORT nWhichId, SfxItemSet & rOutItemSet ) const
        throw( ::com::sun::star::uno::Exception );
    virtual bool ApplySpecialItem( USHORT nWhichId, const SfxItemSet & rItemSet )
        throw( ::com::sun::star::uno::Exception );

private:
    ::std::vector< ItemConverter * >    m_aConverters;
    NumberFormatterWrapper *            m_pNumberFormatterWrapper;
    bool                                m_bDataSeries;
    bool                                m_bOverwriteLabelsForAttributedDataPointsAlso;
    bool                                m_bColorPerPoint;
    bool                                m_bUseSpecialFillColor;
    sal_Int32                           m_nSpecialFillColor;
};

} //  namespace wrapper
} //  namespace chart

// CHART_DATAPOINTITEMCONVERTER_HXX
#endif
<commit_msg>INTEGRATION: CWS chart14 (1.10.12); FILE MERGED 2007/10/01 15:10:39 iha 1.10.12.1: #i37792# #i79194# numberformat for data label - select automatic default in dialog<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: DataPointItemConverter.hxx,v $
 *
 *  $Revision: 1.11 $
 *
 *  last change: $Author: vg $ $Date: 2007-10-22 16:49:37 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#ifndef CHART_DATAPOINTITEMCONVERTER_HXX
#define CHART_DATAPOINTITEMCONVERTER_HXX

#include "ItemConverter.hxx"
#include "GraphicPropertyItemConverter.hxx"
#include "chartview/NumberFormatterWrapper.hxx"

#ifndef _COM_SUN_STAR_AWT_SIZE_HPP_
#include <com/sun/star/awt/Size.hpp>
#endif
#ifndef _COM_SUN_STAR_FRAME_XMODEL_HPP_
#include <com/sun/star/frame/XModel.hpp>
#endif
#ifndef _COM_SUN_STAR_UNO_XCOMPONENTCONTEXT_HPP_
#include <com/sun/star/uno/XComponentContext.hpp>
#endif

#include <memory>
#include <vector>

class SdrModel;

namespace chart
{
namespace wrapper
{

class DataPointItemConverter :
        public ::comphelper::ItemConverter
{
public:
    DataPointItemConverter(
        const ::com::sun::star::uno::Reference<
            ::com::sun::star::frame::XModel > & xChartModel,
        const ::com::sun::star::uno::Reference<
            ::com::sun::star::uno::XComponentContext > & xContext,
        const ::com::sun::star::uno::Reference<
            ::com::sun::star::beans::XPropertySet > & rPropertySet,
        SfxItemPool& rItemPool,
        SdrModel& rDrawModel,
        NumberFormatterWrapper * pNumFormatter,
        const ::com::sun::star::uno::Reference<
            ::com::sun::star::lang::XMultiServiceFactory > & xNamedPropertyContainerFactory,
        GraphicPropertyItemConverter::eGraphicObjectType eMapTo =
            GraphicPropertyItemConverter::FILLED_DATA_POINT,
        ::std::auto_ptr< ::com::sun::star::awt::Size > pRefSize =
            ::std::auto_ptr< ::com::sun::star::awt::Size >(),
        bool bDataSeries = false,
        bool bUseSpecialFillColor = false,
        sal_Int32 nSpecialFillColor = 0,
        bool bOverwriteLabelsForAttributedDataPointsAlso=false,
        sal_Int32 nNumberFormat=0,
        sal_Int32 nPercentNumberFormat=0);

    virtual ~DataPointItemConverter();

    virtual void FillItemSet( SfxItemSet & rOutItemSet ) const;
    virtual bool ApplyItemSet( const SfxItemSet & rItemSet );

protected:
    virtual const USHORT * GetWhichPairs() const;
    virtual bool GetItemProperty( tWhichIdType nWhichId, tPropertyNameWithMemberId & rOutProperty ) const;

    virtual void FillSpecialItem( USHORT nWhichId, SfxItemSet & rOutItemSet ) const
        throw( ::com::sun::star::uno::Exception );
    virtual bool ApplySpecialItem( USHORT nWhichId, const SfxItemSet & rItemSet )
        throw( ::com::sun::star::uno::Exception );

private:
    ::std::vector< ItemConverter * >    m_aConverters;
    NumberFormatterWrapper *            m_pNumberFormatterWrapper;
    bool                                m_bDataSeries;
    bool                                m_bOverwriteLabelsForAttributedDataPointsAlso;
    bool                                m_bColorPerPoint;
    bool                                m_bUseSpecialFillColor;
    sal_Int32                           m_nSpecialFillColor;
    sal_Int32                           m_nNumberFormat;
    sal_Int32                           m_nPercentNumberFormat;
};

} //  namespace wrapper
} //  namespace chart

// CHART_DATAPOINTITEMCONVERTER_HXX
#endif
<|endoftext|>


<commit_before><commit_msg>Fix a bug in LanguageConfigView where it didn't update the view when a language is removed.<commit_after><|endoftext|>


<commit_before>#include "include/vkalloc.h"

#include <vector>
#include <unordered_map>

#ifndef VKA_ALLOC_SIZE
#define VKA_ALLOC_SIZE 1024*1024*4
#endif

namespace vka {
    struct Node {
        Node* next = nullptr;
        uint64_t offset = 0;
        uint64_t size = 0;
    };

    struct Page {
        VkDeviceMemory deviceMemory = VK_NULL_HANDLE;
        Node* head = nullptr;
    };

    struct PageMapping {
        uint32_t heapIndex;
        uint32_t pageIndex;
    };

    VkDevice device;
    VkPhysicalDeviceMemoryProperties properties;
    VkAllocationCallbacks* callbacks;
    std::unordered_map<VkDeviceMemory, PageMapping> pageMap;    //caches index into one of the vector<Page> heaps
    std::vector<Page> heaps[32];    //32 possible heaps
}

void vkaInit(VkPhysicalDevice physicalDevice, VkDevice device, VkAllocationCallbacks* allocator){
    vka::device = device;
    vka::callbacks = allocator;

    vkGetPhysicalDeviceMemoryProperties(physicalDevice, &vka::properties);
}

using namespace vka;
static VkAllocation AttemptAlloc(std::vector<Page>& heap, uint32_t heapIndex, VkMemoryRequirements requirements);
static VkAllocation AttemptAlloc(Page& page, VkMemoryRequirements requirements);
static Page* AllocNewPage(std::vector<Page>& heap, uint32_t heapIndex, VkMemoryRequirements requirements);
static void Split(Node* current, Node** last, uint64_t start, uint64_t size);
static void Free(VkAllocation allocation, Page& page);
static void Merge(VkAllocation allocation, Node* current);

void vkaTerminate(){
    pageMap.clear();

    for (size_t i = 0; i < 32; i++) {
        for (size_t j = 0; j < heaps[i].size(); j++) {
            Node* current = heaps[i][j].head;

            while (current){
                Node* next = current->next;
                delete current;
                current = next;
            }

            vkFreeMemory(device, heaps[i][j].deviceMemory, callbacks);
        }

        heaps[i].clear();
    }
}

VkAllocation vkaAllocFlag(VkMemoryRequirements requirements, VkMemoryPropertyFlags flags) {
    for (size_t i = 0; i < properties.memoryTypeCount; i++) {
        if ((requirements.memoryTypeBits & (1 << i))
            && properties.memoryTypes[i].propertyFlags == flags) {
            std::vector<Page> &heap = heaps[i];

            VkAllocation result = AttemptAlloc(heap, static_cast<uint32_t>(i), requirements);
            if (result.deviceMemory != VK_NULL_HANDLE) {
                return result;
            }
        }
    }

    return {};
}

void vkaFree(VkAllocation allocation){
    if (allocation.deviceMemory == VK_NULL_HANDLE) return;

    PageMapping& mapping = pageMap[allocation.deviceMemory];
    Page& page = heaps[mapping.heapIndex][mapping.pageIndex];

    Free(allocation, page);
}

VkAllocation vkaAlloc(VkMemoryRequirements requirements){
    return vkaAllocFlag(requirements, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
}

VkAllocation vkaAllocHost(VkMemoryRequirements requirements){
    return vkaAllocFlag(requirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
}

static VkAllocation AttemptAlloc(std::vector<Page>& heap, uint32_t heapIndex, VkMemoryRequirements requirements) {
    //attempt to allocate from existing pages
    for (size_t i = 0; i < heap.size(); i++) {
        Page& page = heap[i];

        VkAllocation result = AttemptAlloc(page, requirements);
        if (result.deviceMemory != VK_NULL_HANDLE) {
            return result;
        }
    }

    //attempt to allocate from new page
    Page* newPage = AllocNewPage(heap, heapIndex, requirements);
    if (newPage) {
        VkAllocation result = AttemptAlloc(*newPage, requirements);
        if (result.deviceMemory != VK_NULL_HANDLE) {
            return result;
        }
    }

    //give up
    return {};
}

static VkAllocation AttemptAlloc(Page& page, VkMemoryRequirements requirements) {
    Node* current = page.head;
    Node** last = &page.head;

    while (current) {
        uint64_t start = current->offset;
        uint64_t available = current->size;

        uint64_t unalign = (start % requirements.alignment);
        uint64_t align;

        if (unalign == 0) {
            align = 0;
        } else {
            align = requirements.alignment - unalign;
        }

        start += align;
        available -= align;

        if (available >= requirements.size) {
            Split(current, last, start, requirements.size);
            VkAllocation result;
            result.deviceMemory = page.deviceMemory;
            result.offset = start;
            result.size = requirements.size;
            return result;
        }

        last = &(current->next);
        current = current->next;
    }

    return {};
}

static void Split(Node* current, Node** last, uint64_t start, uint64_t size) {
    uint64_t startSpace = start - current->offset;
    uint64_t endSpace = current->size - (start + size);

    if (startSpace == 0 && endSpace == 0) {
        *last = current->next;
        delete current;
    } else if (startSpace == 0 && endSpace > 0) {
        current->offset = start + size;
        current->size = endSpace;
    } else if (startSpace > 0 && endSpace == 0) {
        current->size = startSpace;
    } else {    //startSpace > 0 && endSpace > 0
        Node* newNode = new Node;
        newNode->next = current->next;
        newNode->offset = start + size;
        newNode->size = endSpace;

        current->next = newNode;
        current->size = startSpace;
    }
}

static Page* AllocNewPage(std::vector<Page>& heap, uint32_t heapIndex, VkMemoryRequirements requirements) {
    uint64_t allocSize = VKA_ALLOC_SIZE;
    if (requirements.size > allocSize) {
        allocSize = requirements.size;
    }

    VkMemoryAllocateInfo info = {};
    info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    info.allocationSize = allocSize;
    info.memoryTypeIndex = heapIndex;

    VkDeviceMemory memory;
    VkResult result = vkAllocateMemory(device, &info, callbacks, &memory);

    if (result == VK_SUCCESS){
        Page page = {};
        page.deviceMemory = memory;

        Node* node = new Node;
        node->size = allocSize;

        page.head = node;

        heap.emplace_back(page);

        pageMap[memory] = {heapIndex, static_cast<uint32_t>(heap.size() - 1)};

        return &(heap[heap.size() - 1]);
    } else {
        //TODO: add way for user to check the error
        return nullptr;
    }
}

static void Free(VkAllocation allocation, Page& page) {
    Node* current = page.head;

    while (current) {
        if (allocation.offset > current->offset && allocation.offset < current->next->offset) {
            Merge(allocation, current);
            return;
        }
    }
}

static void Merge(VkAllocation allocation, Node* current) {
    if (allocation.offset == current->offset + current->size) {
        current->size += allocation.size;
    } else {
        Node* newNode = new Node;
        newNode->next = current->next,
        newNode->offset = allocation.offset,
        newNode->size = allocation.size;

        current = newNode;
    }

    Node* next = current->next;
    if (next->offset == current->offset + current->size) {
        current->size += next->size;
        current->next = next->next;
        delete next;
    }
}<commit_msg>Change how heaps are represented internally<commit_after>#include "include/vkalloc.h"

#include <vector>
#include <unordered_map>

#ifndef VKA_ALLOC_SIZE
#define VKA_ALLOC_SIZE 1024*1024*4
#endif

namespace vka {
    struct Node {
        Node* next = nullptr;
        uint64_t offset = 0;
        uint64_t size = 0;
    };

    struct Page {
        VkDeviceMemory deviceMemory = VK_NULL_HANDLE;
        Node* head = nullptr;
    };

    struct PageMapping {
        uint32_t heapIndex;
        uint32_t pageIndex;
    };

    VkDevice device;
    VkPhysicalDeviceMemoryProperties properties;
    VkAllocationCallbacks* callbacks;
    std::unordered_map<VkDeviceMemory, PageMapping> pageMap;    //caches index into one of the vector<Page> heaps
    std::vector<std::vector<Page>> heaps;
}

void vkaInit(VkPhysicalDevice physicalDevice, VkDevice device, VkAllocationCallbacks* allocator){
    vka::device = device;
    vka::callbacks = allocator;

    vkGetPhysicalDeviceMemoryProperties(physicalDevice, &vka::properties);

    heaps.resize(static_cast<uint32_t>(vka::properties.memoryHeapCount));
}

using namespace vka;
static VkAllocation AttemptAlloc(std::vector<Page>& heap, uint32_t heapIndex, VkMemoryRequirements requirements);
static VkAllocation AttemptAlloc(Page& page, VkMemoryRequirements requirements);
static Page* AllocNewPage(std::vector<Page>& heap, uint32_t heapIndex, VkMemoryRequirements requirements);
static void Split(Node* current, Node** last, uint64_t start, uint64_t size);
static void Free(VkAllocation allocation, Page& page);
static void Merge(VkAllocation allocation, Node* current);

void vkaTerminate(){
    pageMap.clear();

    for (auto& heap : heaps) {
        for (size_t j = 0; j < heap.size(); j++) {
            Node* current = heap[j].head;

            while (current){
                Node* next = current->next;
                delete current;
                current = next;
            }

            vkFreeMemory(device, heap[j].deviceMemory, callbacks);
        }

        heap.clear();
    }
}

VkAllocation vkaAllocFlag(VkMemoryRequirements requirements, VkMemoryPropertyFlags flags) {
    for (size_t i = 0; i < properties.memoryTypeCount; i++) {
        if ((requirements.memoryTypeBits & (1 << i))
            && (properties.memoryTypes[i].propertyFlags & flags)) {
            std::vector<Page>& heap = heaps[properties.memoryTypes[i].heapIndex];

            VkAllocation result = AttemptAlloc(heap, static_cast<uint32_t>(i), requirements);
            if (result.deviceMemory != VK_NULL_HANDLE) {
                return result;
            }
        }
    }

    return {};
}

void vkaFree(VkAllocation allocation){
    if (allocation.deviceMemory == VK_NULL_HANDLE) return;

    PageMapping& mapping = pageMap[allocation.deviceMemory];
    Page& page = heaps[mapping.heapIndex][mapping.pageIndex];

    Free(allocation, page);
}

VkAllocation vkaAlloc(VkMemoryRequirements requirements){
    return vkaAllocFlag(requirements, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
}

VkAllocation vkaAllocHost(VkMemoryRequirements requirements){
    return vkaAllocFlag(requirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
}

static VkAllocation AttemptAlloc(std::vector<Page>& heap, uint32_t heapIndex, VkMemoryRequirements requirements) {
    //attempt to allocate from existing pages
    for (size_t i = 0; i < heap.size(); i++) {
        Page& page = heap[i];

        VkAllocation result = AttemptAlloc(page, requirements);
        if (result.deviceMemory != VK_NULL_HANDLE) {
            return result;
        }
    }

    //attempt to allocate from new page
    Page* newPage = AllocNewPage(heap, heapIndex, requirements);
    if (newPage) {
        VkAllocation result = AttemptAlloc(*newPage, requirements);
        if (result.deviceMemory != VK_NULL_HANDLE) {
            return result;
        }
    }

    //give up
    return {};
}

static VkAllocation AttemptAlloc(Page& page, VkMemoryRequirements requirements) {
    Node* current = page.head;
    Node** last = &page.head;

    while (current) {
        uint64_t start = current->offset;
        uint64_t available = current->size;

        uint64_t unalign = (start % requirements.alignment);
        uint64_t align;

        if (unalign == 0) {
            align = 0;
        } else {
            align = requirements.alignment - unalign;
        }

        start += align;
        available -= align;

        if (available >= requirements.size) {
            Split(current, last, start, requirements.size);
            VkAllocation result;
            result.deviceMemory = page.deviceMemory;
            result.offset = start;
            result.size = requirements.size;
            return result;
        }

        last = &(current->next);
        current = current->next;
    }

    return {};
}

static void Split(Node* current, Node** last, uint64_t start, uint64_t size) {
    uint64_t startSpace = start - current->offset;
    uint64_t endSpace = current->size - (start + size);

    if (startSpace == 0 && endSpace == 0) {
        *last = current->next;
        delete current;
    } else if (startSpace == 0 && endSpace > 0) {
        current->offset = start + size;
        current->size = endSpace;
    } else if (startSpace > 0 && endSpace == 0) {
        current->size = startSpace;
    } else {    //startSpace > 0 && endSpace > 0
        Node* newNode = new Node;
        newNode->next = current->next;
        newNode->offset = start + size;
        newNode->size = endSpace;

        current->next = newNode;
        current->size = startSpace;
    }
}

static Page* AllocNewPage(std::vector<Page>& heap, uint32_t heapIndex, VkMemoryRequirements requirements) {
    uint64_t allocSize = VKA_ALLOC_SIZE;
    if (requirements.size > allocSize) {
        allocSize = requirements.size;
    }

    VkMemoryAllocateInfo info = {};
    info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    info.allocationSize = allocSize;
    info.memoryTypeIndex = heapIndex;

    VkDeviceMemory memory;
    VkResult result = vkAllocateMemory(device, &info, callbacks, &memory);

    if (result == VK_SUCCESS){
        Page page = {};
        page.deviceMemory = memory;

        Node* node = new Node;
        node->size = allocSize;

        page.head = node;

        heap.emplace_back(page);

        pageMap[memory] = {heapIndex, static_cast<uint32_t>(heap.size() - 1)};

        return &(heap[heap.size() - 1]);
    } else {
        //TODO: add way for user to check the error
        return nullptr;
    }
}

static void Free(VkAllocation allocation, Page& page) {
    Node* current = page.head;

    while (current) {
        if (allocation.offset > current->offset && allocation.offset < current->next->offset) {
            Merge(allocation, current);
            return;
        }
    }
}

static void Merge(VkAllocation allocation, Node* current) {
    if (allocation.offset == current->offset + current->size) {
        current->size += allocation.size;
    } else {
        Node* newNode = new Node;
        newNode->next = current->next,
        newNode->offset = allocation.offset,
        newNode->size = allocation.size;

        current = newNode;
    }

    Node* next = current->next;
    if (next->offset == current->offset + current->size) {
        current->size += next->size;
        current->next = next->next;
        delete next;
    }
}<|endoftext|>