// Copyright (c) 2013 by István Váradi // This file is part of XPLRA, a remote-access plugin for X-Plane // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. 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. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // The views and conclusions contained in the software and documentation are those // of the authors and should not be interpreted as representing official policies, // either expressed or implied, of the FreeBSD Project. //------------------------------------------------------------------------------ #include "MultiBuffer.h" #include "XPlane.h" #include #include #include #include //------------------------------------------------------------------------------ using hu::varadiistvan::xplra::MultiBuffer; using xplra::Protocol; using std::vector; using std::string; using std::min; //------------------------------------------------------------------------------ inline MultiBuffer::DataRef::DataRef(const string& name, uint8_t type, const DataRef* previous, size_t length, size_t offset) throw() : name(name), type(type), length(length), offset(offset), dataOffset(0) { if (previous!=0) { size_t alignment = getAlignment(); dataOffset = previous->dataOffset + previous->getSize(); dataOffset += alignment - 1; dataOffset &= ~(alignment-1); } } //------------------------------------------------------------------------------ inline size_t MultiBuffer::DataRef::getSize() const throw() { switch (type) { case Protocol::TYPE_INT: return sizeof(int32_t); case Protocol::TYPE_FLOAT: return sizeof(float); case Protocol::TYPE_DOUBLE: return sizeof(double); case Protocol::TYPE_FLOAT_ARRAY: return sizeof(float) * length; case Protocol::TYPE_INT_ARRAY: return sizeof(int32_t) * length; case Protocol::TYPE_BYTE_ARRAY: return sizeof(uint8_t) * length; default: return 0; } } //------------------------------------------------------------------------------ inline size_t MultiBuffer::DataRef::getAlignment() const throw() { switch (type) { case Protocol::TYPE_INT: case Protocol::TYPE_INT_ARRAY: return sizeof(int32_t); case Protocol::TYPE_FLOAT: case Protocol::TYPE_FLOAT_ARRAY: return sizeof(float); case Protocol::TYPE_DOUBLE: return sizeof(double); case Protocol::TYPE_BYTE_ARRAY: default: return sizeof(uint8_t); } } //------------------------------------------------------------------------------ bool MultiBuffer::DataRef::isArray() const throw() { return type==Protocol::TYPE_FLOAT_ARRAY || type==Protocol::TYPE_INT_ARRAY || type==Protocol::TYPE_BYTE_ARRAY; } //------------------------------------------------------------------------------ inline size_t MultiBuffer::DataRef::getCopyLength(size_t userLength, size_t userOffset) const throw() { size_t maxLength = (userOffset>length) ? 0 : (length - userOffset); return (userLength==0) ? maxLength : min(userLength, maxLength); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ inline void MultiBuffer::unfinalize() throw() { delete data; data = 0; } //------------------------------------------------------------------------------ void MultiBuffer::forgetRegistration() throw() { registeredID = -1; } //------------------------------------------------------------------------------ inline const MultiBuffer::DataRef* MultiBuffer::getLastDataRef() const throw() { return dataRefs.empty() ? 0 : &(dataRefs.back()); } //------------------------------------------------------------------------------ inline const MultiBuffer::DataRef& MultiBuffer::getDataRef(size_t id, uint8_t type) const throw(InvalidIDException, TypeMismatchException) { if (id>=dataRefs.size()) throw InvalidIDException(); const DataRef& dataRef = dataRefs[id]; if (dataRef.type!=type) throw TypeMismatchException(); return dataRef; } //------------------------------------------------------------------------------ inline void* MultiBuffer::getData(size_t id, uint8_t type) throw(InvalidIDException, TypeMismatchException) { const DataRef& dataRef = getDataRef(id, type); finalize(); return data + dataRef.dataOffset; } //------------------------------------------------------------------------------ inline const void* MultiBuffer::getData(size_t id, uint8_t type) const throw(InvalidIDException, TypeMismatchException) { const DataRef& dataRef = getDataRef(id, type); finalize(); return data + dataRef.dataOffset; } //------------------------------------------------------------------------------ template size_t inline MultiBuffer::setArray(size_t id, const T* value, size_t length, size_t offset) throw(InvalidIDException, TypeMismatchException) { const DataRef& dataRef = getDataRef(id, type); finalize(); size_t toCopy = dataRef.getCopyLength(length, offset); if (toCopy>0) { memcpy( data + dataRef.dataOffset + offset * sizeof(T), value, toCopy * sizeof(T) ); } return toCopy; } //------------------------------------------------------------------------------ template inline size_t MultiBuffer::getArray(size_t id, T* value, size_t length, size_t offset) const throw(InvalidIDException, TypeMismatchException) { const DataRef& dataRef = getDataRef(id, type); finalize(); size_t toCopy = dataRef.getCopyLength(length, offset); if (toCopy>0) { memcpy( value, data + dataRef.dataOffset + offset * sizeof(T), toCopy * sizeof(T) ); } return toCopy; } //------------------------------------------------------------------------------ template inline const T* MultiBuffer::getArray(size_t id, size_t offset) const throw(InvalidIDException, TypeMismatchException) { const DataRef& dataRef = getDataRef(id, type); finalize(); return (offset(data + dataRef.dataOffset + offset * sizeof(T)) : 0; } //------------------------------------------------------------------------------ MultiBuffer::MultiBuffer(XPlane& xplane, uint8_t registerCommand, uint8_t unregisterCommand) throw() : data(0), xplane(xplane), registerCommand(registerCommand), unregisterCommand(unregisterCommand), registeredID(-1) { } //------------------------------------------------------------------------------ MultiBuffer::~MultiBuffer() throw() { unregisterSafelyFromXPlane(); delete[] data; } //------------------------------------------------------------------------------ size_t MultiBuffer::addInt(const string& name) throw() { unfinalize(); dataRefs.push_back(DataRef(name, Protocol::TYPE_INT, getLastDataRef())); return dataRefs.size() - 1; } //------------------------------------------------------------------------------ size_t MultiBuffer::addFloat(const string& name) throw() { unfinalize(); dataRefs.push_back(DataRef(name, Protocol::TYPE_FLOAT, getLastDataRef())); return dataRefs.size() - 1; } //------------------------------------------------------------------------------ size_t MultiBuffer::addDouble(const string& name) throw() { unfinalize(); dataRefs.push_back(DataRef(name, Protocol::TYPE_DOUBLE, getLastDataRef())); return dataRefs.size() - 1; } //------------------------------------------------------------------------------ size_t MultiBuffer::addFloatArray(const string& name, size_t length, size_t offset) throw() { unfinalize(); dataRefs.push_back(DataRef(name, Protocol::TYPE_FLOAT_ARRAY, getLastDataRef(), length, offset)); return dataRefs.size() - 1; } //------------------------------------------------------------------------------ size_t MultiBuffer::addIntArray(const string& name, size_t length, size_t offset) throw() { unfinalize(); dataRefs.push_back(DataRef(name, Protocol::TYPE_INT_ARRAY, getLastDataRef(), length, offset)); return dataRefs.size() - 1; } //------------------------------------------------------------------------------ size_t MultiBuffer::addByteArray(const string& name, size_t length, size_t offset) throw() { unfinalize(); dataRefs.push_back(DataRef(name, Protocol::TYPE_BYTE_ARRAY, getLastDataRef(), length, offset)); return dataRefs.size() - 1; } //------------------------------------------------------------------------------ bool MultiBuffer::finalize() const { if (data==0 && !dataRefs.empty()) { const DataRef* lastDataRef = getLastDataRef(); size_t dataSize = lastDataRef->dataOffset + lastDataRef->getSize(); data = new unsigned char[dataSize]; memset(data, 0, dataSize); return true; } else { return data!=0; } } //------------------------------------------------------------------------------ void MultiBuffer::registerInXPlane() throw(Exception) { if (finalize() && registeredID<0) { xplane.checkStream(); writeSpec(registerCommand); uint32_t id = xplane.stream->readU32(); xplane.checkStream(); registeredID = static_cast(id); } } //------------------------------------------------------------------------------ void MultiBuffer::unregisterFromXPlane() throw(Exception) { if (registeredID>=0) { xplane.stream->writeU8(unregisterCommand); xplane.stream->writeU32(static_cast(registeredID)); xplane.stream->flush(); xplane.checkResult(); registeredID = -1; } } //------------------------------------------------------------------------------ bool MultiBuffer::unregisterSafelyFromXPlane() throw() { try { unregisterFromXPlane(); return true; } catch(...) { registeredID = -1; return false; } } //------------------------------------------------------------------------------ void MultiBuffer::execute() throw(Exception) { if (data==0 && registeredID>=0) { unregisterFromXPlane(); registerInXPlane(); } else { finalize(); } if (registeredID>=0) { doExecute(); } else { doExecuteUnregistered(); } } //------------------------------------------------------------------------------ void MultiBuffer::setInt(size_t id, int value) throw(InvalidIDException, TypeMismatchException) { *reinterpret_cast(getData(id, Protocol::TYPE_INT)) = value; } //------------------------------------------------------------------------------ int MultiBuffer::getInt(size_t id) const throw(InvalidIDException, TypeMismatchException) { return *reinterpret_cast(getData(id, Protocol::TYPE_INT)); } //------------------------------------------------------------------------------ const int32_t& MultiBuffer::getIntRef(size_t id) const throw(InvalidIDException, TypeMismatchException) { return *reinterpret_cast(getData(id, Protocol::TYPE_INT)); } //------------------------------------------------------------------------------ int32_t& MultiBuffer::getIntRef(size_t id) throw(InvalidIDException, TypeMismatchException) { return *reinterpret_cast(getData(id, Protocol::TYPE_INT)); } //------------------------------------------------------------------------------ void MultiBuffer::setFloat(size_t id, float value) throw(InvalidIDException, TypeMismatchException) { *reinterpret_cast(getData(id, Protocol::TYPE_FLOAT)) = value; } //------------------------------------------------------------------------------ float MultiBuffer::getFloat(size_t id) const throw(InvalidIDException, TypeMismatchException) { return *reinterpret_cast(getData(id, Protocol::TYPE_FLOAT)); } //------------------------------------------------------------------------------ const float& MultiBuffer::getFloatRef(size_t id) const throw(InvalidIDException, TypeMismatchException) { return *reinterpret_cast(getData(id, Protocol::TYPE_FLOAT)); } //------------------------------------------------------------------------------ float& MultiBuffer::getFloatRef(size_t id) throw(InvalidIDException, TypeMismatchException) { return *reinterpret_cast(getData(id, Protocol::TYPE_FLOAT)); } //------------------------------------------------------------------------------ void MultiBuffer::setDouble(size_t id, double value) throw(InvalidIDException, TypeMismatchException) { *reinterpret_cast(getData(id, Protocol::TYPE_DOUBLE)) = value; } //------------------------------------------------------------------------------ double MultiBuffer::getDouble(size_t id) const throw(InvalidIDException, TypeMismatchException) { return *reinterpret_cast(getData(id, Protocol::TYPE_DOUBLE)); } //------------------------------------------------------------------------------ const double& MultiBuffer::getDoubleRef(size_t id) const throw(InvalidIDException, TypeMismatchException) { return *reinterpret_cast(getData(id, Protocol::TYPE_DOUBLE)); } //------------------------------------------------------------------------------ double& MultiBuffer::getDoubleRef(size_t id) throw(InvalidIDException, TypeMismatchException) { return *reinterpret_cast(getData(id, Protocol::TYPE_DOUBLE)); } //------------------------------------------------------------------------------ size_t MultiBuffer::setFloatArray(size_t id, const float* value, size_t length, size_t offset) throw(InvalidIDException, TypeMismatchException) { return setArray(id, value, length, offset); } //------------------------------------------------------------------------------ size_t MultiBuffer::getFloatArray(size_t id, float* value, size_t length, size_t offset) const throw(InvalidIDException, TypeMismatchException) { return getArray(id, value, length, offset); } //------------------------------------------------------------------------------ const float* MultiBuffer::getFloatArray(size_t id, size_t offset) const throw(InvalidIDException, TypeMismatchException) { return getArray(id, offset); } //------------------------------------------------------------------------------ size_t MultiBuffer::setIntArray(size_t id, const int32_t* value, size_t length, size_t offset) throw(InvalidIDException, TypeMismatchException) { return setArray(id, value, length, offset); } //------------------------------------------------------------------------------ size_t MultiBuffer::getIntArray(size_t id, int32_t* value, size_t length, size_t offset) const throw(InvalidIDException, TypeMismatchException) { return getArray(id, value, length, offset); } //------------------------------------------------------------------------------ const int32_t* MultiBuffer::getIntArray(size_t id, size_t offset) const throw(InvalidIDException, TypeMismatchException) { return getArray(id, offset); } //------------------------------------------------------------------------------ size_t MultiBuffer::setByteArray(size_t id, const uint8_t* value, size_t length, size_t offset) throw(InvalidIDException, TypeMismatchException) { return setArray(id, value, length, offset); } //------------------------------------------------------------------------------ size_t MultiBuffer::getByteArray(size_t id, uint8_t* value, size_t length, size_t offset) const throw(InvalidIDException, TypeMismatchException) { return getArray(id, value, length, offset); } //------------------------------------------------------------------------------ const uint8_t* MultiBuffer::getByteArray(size_t id, size_t offset) const throw(InvalidIDException, TypeMismatchException) { return getArray(id, offset); } //------------------------------------------------------------------------------ size_t MultiBuffer::setString(size_t id, const string& value, size_t offset) throw(InvalidIDException, TypeMismatchException) { return setByteArray(id, reinterpret_cast(value.c_str()), value.length(), offset); } //------------------------------------------------------------------------------ string MultiBuffer::getString(size_t id, size_t offset) const throw(InvalidIDException, TypeMismatchException) { const DataRef& dataRef = getDataRef(id, Protocol::TYPE_BYTE_ARRAY); finalize(); size_t toCopy = dataRef.getCopyLength(0, offset); const uint8_t* str = data + dataRef.dataOffset + offset; size_t length = 0; if (toCopy!=0) { while(length(str), length); } //------------------------------------------------------------------------------ const char* MultiBuffer::getStringPtr(size_t id, size_t offset) const throw(InvalidIDException, TypeMismatchException) { return reinterpret_cast(getByteArray(id, offset)); } //------------------------------------------------------------------------------ void MultiBuffer::writeSpec(uint8_t command) const throw(Exception) { xplane.stream->writeU8(command); xplane.stream->writeU32(dataRefs.size()); for(vector::const_iterator i = dataRefs.begin(); i!=dataRefs.end(); ++i) { const DataRef& dataRef = *i; xplane.stream->writeString(dataRef.name); xplane.stream->writeU8(dataRef.type); if (dataRef.isArray()) { xplane.stream->writeS32(dataRef.length); xplane.stream->writeS32(dataRef.offset); } } xplane.stream->flush(); xplane.checkResult(true); } //------------------------------------------------------------------------------ void MultiBuffer::reregisterInXPlane() throw(Exception) { if (registeredID>=0) { int origRegisteredID = registeredID; try { registeredID = -1; registerInXPlane(); } catch(...) { registeredID = origRegisteredID; throw; } } } //------------------------------------------------------------------------------ // Local Variables: // mode: C++ // c-basic-offset: 4 // indent-tabs-mode: nil // End: