diff options
Diffstat (limited to 'src/foundation/IOStreams.cpp')
-rw-r--r-- | src/foundation/IOStreams.cpp | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/src/foundation/IOStreams.cpp b/src/foundation/IOStreams.cpp new file mode 100644 index 0000000..f1adfbc --- /dev/null +++ b/src/foundation/IOStreams.cpp @@ -0,0 +1,384 @@ +/**************************************************************************** +** +** Copyright (C) 1993-2009 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "foundation/IOStreams.h" +#include "foundation/FileTools.h" +#include "foundation/StrConvertUTF.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "foundation/Qt3DSMutex.h" +#include "foundation/Qt3DSSync.h" +#include "foundation/Qt3DSThread.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSMemoryBuffer.h" + +using namespace qt3ds::foundation; + +#ifndef _WIN32 + +inline int _fseeki64(FILE *inFile, int64_t pos, int seekFlags) +{ + return fseek(inFile, (int32_t)pos, seekFlags); +} + +inline int64_t _ftelli64(FILE *inFile) +{ + return ftell(inFile); +} + +#endif + +CFileSeekableIOStream::CFileSeekableIOStream(const char *inFileName, FileOpenFlags inFlags) +{ + openFile(QString(inFileName), inFlags); +} + +#ifdef WIDE_IS_DIFFERENT_TYPE_THAN_CHAR16_T + +CFileSeekableIOStream::CFileSeekableIOStream(const wchar_t *inFileName, FileOpenFlags inFlags) +{ + openFile(QString::fromWCharArray(inFileName), inFlags); +} + +#endif + +CFileSeekableIOStream::CFileSeekableIOStream(const char16_t *inWideName, FileOpenFlags inFlags) +{ + openFile(QString::fromUtf16(inWideName), inFlags); +} + +CFileSeekableIOStream::CFileSeekableIOStream(const QString &inFIle, FileOpenFlags inFlags) +{ + openFile(inFIle, inFlags); +} + +void CFileSeekableIOStream::openFile(const QString &path, FileOpenFlags inFlags) +{ + if (path.isEmpty()) + return; + + QIODevice::OpenMode fileFlags = QIODevice::ReadOnly; + if (inFlags & FileOpenFlagValues::Write) + fileFlags = QIODevice::ReadWrite; + if (inFlags & FileOpenFlagValues::Truncate) + fileFlags |= QIODevice::Truncate; + + m_File.setFileName(CFileTools::NormalizePathForQtUsage(path)); + if (!m_File.open(fileFlags)) { + qCCritical(INTERNAL_ERROR) << "failed to open file" + << path << "with error" << m_File.errorString(); + QT3DS_ASSERT(false); + } +} + +CFileSeekableIOStream::~CFileSeekableIOStream() +{ + m_File.close(); +} + +bool CFileSeekableIOStream::IsOpen() +{ + return m_File.isOpen(); +} + +void CFileSeekableIOStream::SetPosition(QT3DSI64 inOffset, SeekPosition::Enum inEnum) +{ + if (inOffset > QT3DS_MAX_I32 || inOffset < QT3DS_MIN_I32) { + qCCritical(INVALID_OPERATION, "Attempt to seek further than platform allows"); + QT3DS_ASSERT(false); + return; + } else { + CFileTools::SetStreamPosition(m_File, inOffset, inEnum); + } +} + +QT3DSI64 CFileSeekableIOStream::GetPosition() const +{ + return m_File.pos(); +} + +QT3DSU32 CFileSeekableIOStream::Read(NVDataRef<QT3DSU8> data) +{ + return m_File.read((char *)data.begin(), data.size()); +} + +bool CFileSeekableIOStream::Write(NVConstDataRef<QT3DSU8> data) +{ + if (!m_File.isOpen()) { + QT3DS_ASSERT(false); + return 0; + } + qint64 numBytes = m_File.write((char*)data.begin(), data.size()); + return numBytes == data.size(); +} + +CMemorySeekableIOStream::CMemorySeekableIOStream(NVAllocatorCallback &inAlloc, + const char *inAllocName) + : m_Allocator(inAlloc) + , m_AllocationName(inAllocName) + , m_Data(NULL) + , m_Size(0) + , m_Offset(0) + , m_Capacity(0) +{ +} + +CMemorySeekableIOStream::~CMemorySeekableIOStream() +{ + if (m_Data) + m_Allocator.deallocate(m_Data); + m_Data = NULL; + m_Size = 0; + m_Capacity = 0; + m_Offset = 0; +} + +void CMemorySeekableIOStream::SetPosition(QT3DSI64 inOffset, SeekPosition::Enum inEnum) +{ + QT3DSI64 startPos = 0; + + switch (inEnum) { + case SeekPosition::Begin: + startPos = 0; + break; + case SeekPosition::Current: + startPos = m_Offset; + break; + case SeekPosition::End: + startPos = m_Size; + break; + default: + QT3DS_ASSERT(false); + break; + } + + startPos += inOffset; + if (m_Size == 0 && inOffset != 0) { + QT3DS_ASSERT(false); + return; + } + if (startPos < 0) { + QT3DS_ASSERT(false); + startPos = 0; + } + if (startPos >= m_Size && startPos != 0) { + QT3DS_ASSERT(false); + startPos = m_Size - 1; + } + m_Offset = static_cast<QT3DSU32>(startPos); +} + +QT3DSU32 CMemorySeekableIOStream::Read(NVDataRef<QT3DSU8> data) +{ + if (m_Data == NULL) + return 0; + QT3DSU32 amountLeft = m_Size - m_Offset; + QT3DSU32 numBytes = NVMin(amountLeft, data.size()); + intrinsics::memCopy(data.begin(), m_Data + m_Offset, numBytes); + m_Offset += numBytes; + return numBytes; +} + +bool CMemorySeekableIOStream::Write(NVConstDataRef<QT3DSU8> data) +{ + reserve(data.size() + m_Offset); + intrinsics::memCopy(m_Data + m_Offset, data.begin(), data.size()); + m_Offset += data.size(); + m_Size = NVMax(m_Size, m_Offset); + return true; +} + +void CMemorySeekableIOStream::reserve(QT3DSU32 inNewSize) +{ + if (inNewSize > m_Capacity) { + if (inNewSize < 100000) + inNewSize *= 2; + QT3DSU8 *newData = + (QT3DSU8 *)m_Allocator.allocate(inNewSize, m_AllocationName, __FILE__, __LINE__); + if (m_Size) { + intrinsics::memCopy(newData, m_Data, m_Size); + m_Allocator.deallocate(m_Data); + } + m_Data = newData; + m_Capacity = inNewSize; + } +} + +namespace { + +struct WriteBufferedStreamImpl; +struct WriteBufferThread : public Thread +{ + // When a buffer is available + WriteBufferedStreamImpl &m_Impl; + WriteBufferThread(NVFoundationBase &fnd, WriteBufferedStreamImpl &i) + : Thread(fnd) + , m_Impl(i) + { + setName("WriteBufferThread"); + } + void execute() override; +}; + +#ifdef _WIN32 +#pragma warning(disable : 4355) +#endif +/* Double buffered stream implementation with a sending thread constantly + * pulling data from the main thread and writing it out to socket. + */ +struct WriteBufferedStreamImpl : public WriteBufferedOutStream +{ + NVFoundationBase &m_Foundation; + IOutStream &m_Stream; + MemoryBuffer<> m_Buf1; + MemoryBuffer<> m_Buf2; + MemoryBuffer<> *m_CurrentBuffer; + MemoryBuffer<> *m_WriteBuffer; + QT3DSU32 m_BufferSize; + volatile bool m_StreamValid; + Mutex m_BufferLock; + Sync m_DataAvailable; + Sync m_WriteFinished; + WriteBufferThread m_Thread; + QT3DSI32 mRefCount; + + WriteBufferedStreamImpl(NVFoundationBase &fnd, QT3DSU32 totalBufSize, IOutStream &s) + : m_Foundation(fnd) + , m_Stream(s) + , m_Buf1(ForwardingAllocator(fnd.getAllocator(), "WriteBufferedStreamImpl::buffer")) + , m_Buf2(ForwardingAllocator(fnd.getAllocator(), "WriteBufferedStreamImpl::buffer")) + , m_CurrentBuffer(&m_Buf1) + , m_WriteBuffer(NULL) + , m_BufferSize(totalBufSize / 2) + , m_StreamValid(true) + , m_BufferLock(fnd.getAllocator()) + , m_DataAvailable(fnd.getAllocator()) + , m_WriteFinished(fnd.getAllocator()) + , m_Thread(fnd, *this) + , mRefCount(0) + { + m_Buf1.reserve(m_BufferSize); + m_Buf2.reserve(m_BufferSize); + } + ~WriteBufferedStreamImpl() + { + m_Thread.signalQuit(); + m_DataAvailable.set(); + m_Thread.waitForQuit(); + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Foundation.getAllocator()) + + bool Write(NVConstDataRef<QT3DSU8> data) override + { + while (data.size() && m_StreamValid) { + QT3DSU32 currentBufferSize; + QT3DSU32 amountCanWrite; + { + Mutex::ScopedLock locker(m_BufferLock); + currentBufferSize = m_CurrentBuffer->size(); + amountCanWrite = NVMin(data.size(), m_BufferSize - currentBufferSize); + m_CurrentBuffer->write(data.begin(), amountCanWrite); + currentBufferSize += amountCanWrite; + } + m_DataAvailable.set(); + if (currentBufferSize == m_BufferSize) { + m_WriteFinished.wait(); + m_WriteFinished.reset(); + // Blocking call if we are already sending data. + data = NVConstDataRef<QT3DSU8>(data.begin() + amountCanWrite, + data.size() - amountCanWrite); + } + } + return m_StreamValid; + } + + IOutStream &wrappedStream() override { return m_Stream; } + + QT3DSU32 getTotalBufferSize() + { + Mutex::ScopedLock locker(m_BufferLock); + QT3DSU32 retval = m_CurrentBuffer->size(); + if (m_WriteBuffer) + retval += m_WriteBuffer->size(); + return retval; + } + + QT3DSU32 getWriteBufferSize() + { + Mutex::ScopedLock locker(m_BufferLock); + if (m_WriteBuffer) + return m_WriteBuffer->size(); + return 0; + } + + void flush() override + { + while (getTotalBufferSize()) { + m_WriteFinished.wait(); + m_WriteFinished.reset(); + } + } +}; + +void WriteBufferThread::execute() +{ + while (!quitIsSignalled()) { + m_Impl.m_DataAvailable.wait(); + + if (!quitIsSignalled() && m_Impl.m_StreamValid) { + m_Impl.m_DataAvailable.reset(); + { + Mutex::ScopedLock locker(m_Impl.m_BufferLock); + m_Impl.m_WriteBuffer = m_Impl.m_CurrentBuffer; + m_Impl.m_CurrentBuffer = + m_Impl.m_CurrentBuffer == &m_Impl.m_Buf1 ? &m_Impl.m_Buf2 : &m_Impl.m_Buf1; + QT3DS_ASSERT(m_Impl.m_WriteBuffer != m_Impl.m_CurrentBuffer); + } + NVConstDataRef<QT3DSU8> dataBuffer(*m_Impl.m_WriteBuffer); + if (dataBuffer.size()) { + m_Impl.m_StreamValid = m_Impl.m_Stream.Write(dataBuffer); + { + Mutex::ScopedLock locker(m_Impl.m_BufferLock); + m_Impl.m_WriteBuffer->clear(); + m_Impl.m_WriteBuffer = NULL; + } + } + m_Impl.m_WriteFinished.set(); + } + } + quit(); +} +} + +NVScopedRefCounted<WriteBufferedOutStream> +WriteBufferedOutStreamCreate(NVFoundationBase &fnd, IOutStream &stream, QT3DSU32 totalBufferSize) +{ + return QT3DS_NEW(fnd.getAllocator(), WriteBufferedStreamImpl)(fnd, totalBufferSize, stream); +} |