/*** Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuIOBufferedProcessor.cpp Date: 2022-6-6 Author: Reece ***/ #include #include #include "AuIOBufferedProcessor.hpp" namespace Aurora::IO::Utility { struct IOBufferedProcessor : IIOBufferedProcessor, AuEnableSharedFromThis { AuSPtr pSource; AuSPtr pDrain; AuSPtr pProcessor; AuSPtr pProcessorEx; AuUInt32 uBufferSize {}; AuByteBuffer buffer; AuByteBuffer buffer2; bool bErrored {}; AU_DEFINE_FOR_VA(IOBufferedProcessor, (AU_DEFINE_CTOR_VA, // initializer-list-like ctor (extending a struct or adding a ctor will break initializer lists) AU_DEFINE_THIS_MOVE_CTOR_VA, // add move `Object(Object &&)` AU_DEFINE_EQUALS_VA, // add equals operator AU_DEFINE_MOVE_VA, // add move assignment operator AU_DEFINE_COPY_VA), // add copy assignment operator (pSource, pDrain, pProcessor, uBufferSize)); IOBufferedProcessor(AuSPtr pSource, AuSPtr pDrain, AuSPtr pProcessor, AuUInt32 uBufferSize) : pSource(pSource), pDrain(pDrain), pProcessorEx(pProcessor), uBufferSize(uBufferSize) { } AuUInt32 TryProcessBuffered() override; AuUInt32 GetRawBytesBuffered() override; AuUInt32 GetRawBytesLimit() override; AuUInt32 TryPump(); }; AuUInt32 IOBufferedProcessor::TryProcessBuffered() { if (this->buffer.IsEmpty()) { this->buffer.Allocate(this->uBufferSize); this->buffer.flagCircular = true; if (this->pProcessorEx) { this->buffer2.Allocate(this->uBufferSize); this->buffer2.flagCircular = true; } } if (!this->buffer || (this->pProcessorEx && !this->buffer2)) { return TryPump(); } AuUInt read {}; try { if (this->pSource->Read(AuMemoryViewStreamWrite(AuMemoryViewWrite(this->buffer), read)) != AuIO::EStreamError::eErrorNone) { return this->TryPump(); } } catch (...) { SysPushErrorCatch(); } this->buffer.writePtr += read; return this->TryPump(); } AuUInt32 IOBufferedProcessor::TryPump() { AuUInt uBytesProcessedTotal {}; AuUInt uBytesProcessed {}; if (this->bErrored) { return 0; } do { try { if (this->pProcessor) { if (!this->pProcessor->OnDataAvailable(AuMemoryViewStreamRead(AuMemoryViewRead(this->buffer), uBytesProcessed), this->pDrain)) { break; } } else if (this->pProcessorEx) { auto pRead = this->buffer.readPtr; if (!this->pProcessorEx->OnDataAvailable(AuSharedPointerFromThis(&this->buffer), AuSharedPointerFromThis(&this->buffer2), {})) { this->buffer.readPtr = pRead; break; } AuUInt uWritten {}; if (AuIO::WriteAll(this->pDrain.get(), { AuMemoryViewRead(this->buffer2), uWritten }) != AuIO::EStreamError::eErrorNone) { this->bErrored = true; } this->buffer2.readPtr += uWritten; uBytesProcessed = this->buffer.calcDifferenceBetweenHeadsUnsigned(this->buffer.readPtr, pRead); } } catch (...) { SysPushErrorCatch(); } this->buffer.readPtr += uBytesProcessed; uBytesProcessedTotal += uBytesProcessed; } while (AuExchange(uBytesProcessedTotal, 0)); return uBytesProcessedTotal; } AuUInt32 IOBufferedProcessor::GetRawBytesBuffered() { return this->buffer.RemainingBytes(); } AuUInt32 IOBufferedProcessor::GetRawBytesLimit() { return this->uBufferSize; } AUKN_SYM AuSPtr NewBufferedProcessor(const AuSPtr &pSource, const AuSPtr &pProcessor, const AuSPtr &pDrain, AuUInt32 uBufferSize) { SysCheckArgNotNull(pProcessor, {}); SysCheckArgNotNull(pDrain, {}); SysCheckArgNotNull(pSource, {}); return AuMakeShared(pSource, pDrain, pProcessor, uBufferSize); } AUKN_SYM AuSPtr NewBufferedProcessorEx(const AuSPtr &pSource, const AuSPtr &pProcessor, const AuSPtr &pDrain, AuUInt32 uBufferSize) { SysCheckArgNotNull(pProcessor, {}); SysCheckArgNotNull(pDrain, {}); SysCheckArgNotNull(pSource, {}); return AuMakeShared(pSource, pDrain, pProcessor, uBufferSize); } }