ptrdiff_t BufferedStream::find(const std::string &str, size_t sanitySize, bool throwIfNotFound) { if (supportsSeek()) flush(false); if (sanitySize == (size_t)~0) sanitySize = 2 * m_bufferSize; sanitySize += str.size(); while (true) { size_t readAvailable = m_readBuffer.readAvailable(); if (readAvailable > 0) { ptrdiff_t result = m_readBuffer.find(str, std::min(sanitySize, readAvailable)); if (result != -1) { return result; } } if (readAvailable >= sanitySize) { if (throwIfNotFound) MORDOR_THROW_EXCEPTION(BufferOverflowException()); return -(ptrdiff_t)m_readBuffer.readAvailable() - 1; } MORDOR_LOG_TRACE(g_log) << this << " parent()->read(" << m_bufferSize << ")"; size_t result = parent()->read(m_readBuffer, m_bufferSize); MORDOR_LOG_DEBUG(g_log) << this << " parent()->read(" << m_bufferSize << "): " << result; if (result == 0) { // EOF if (throwIfNotFound) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); return -(ptrdiff_t)m_readBuffer.readAvailable() - 1; } } }
size_t HandleStream::write(const void *buffer, size_t length) { if (m_cancelWrite) MORDOR_THROW_EXCEPTION(OperationAbortedException()); SchedulerSwitcher switcher(m_ioManager ? NULL : m_scheduler); DWORD written; OVERLAPPED *overlapped = NULL; if (m_ioManager) { MORDOR_ASSERT(Scheduler::getThis()); m_ioManager->registerEvent(&m_writeEvent); overlapped = &m_writeEvent.overlapped; if (supportsSeek()) { overlapped->Offset = (DWORD)m_pos; overlapped->OffsetHigh = (DWORD)(m_pos >> 32); } } length = (std::min)(length, m_maxOpSize); BOOL ret = WriteFile(m_hFile, buffer, (DWORD)length, &written, overlapped); Log::Level level = Log::DEBUG; if (!ret) { if (m_ioManager && GetLastError() == ERROR_IO_PENDING) level = Log::TRACE; else level = Log::ERROR; } DWORD error = GetLastError(); MORDOR_LOG_LEVEL(g_log, level) << this << " WriteFile(" << m_hFile << ", " << length << "): " << ret << " - " << written << " (" << error << ")"; if (m_ioManager) { if (!ret && error != ERROR_IO_PENDING) { m_ioManager->unregisterEvent(&m_writeEvent); MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("WriteFile"); } if (m_skipCompletionPortOnSuccess && ret) m_ioManager->unregisterEvent(&m_writeEvent); else Scheduler::yieldTo(); DWORD error = pRtlNtStatusToDosError((NTSTATUS)m_writeEvent.overlapped.Internal); MORDOR_LOG_LEVEL(g_log, error ? Log::ERROR : Log::VERBOSE) << this << " WriteFile(" << m_hFile << ", " << length << "): " << m_writeEvent.overlapped.InternalHigh << " (" << error << ")"; if (error) MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "WriteFile"); if (supportsSeek()) { m_pos = ((long long)overlapped->Offset | ((long long)overlapped->OffsetHigh << 32)) + m_writeEvent.overlapped.InternalHigh; } return m_writeEvent.overlapped.InternalHigh; } if (!ret) MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "WriteFile"); return written; }
size_t BufferedStream::readInternal(T &buffer, size_t length) { if (supportsSeek()) flush(false); size_t remaining = length; size_t buffered = std::min(m_readBuffer.readAvailable(), remaining); m_readBuffer.copyOut(buffer, buffered); m_readBuffer.consume(buffered); remaining -= buffered; MORDOR_LOG_VERBOSE(g_log) << this << " read(" << length << "): " << buffered << " read from buffer"; if (remaining == 0) return length; if (buffered == 0 || !m_allowPartialReads) { size_t result; do { // Read enough to satisfy this request, plus up to a multiple of // the buffer size size_t todo = ((remaining - 1) / m_bufferSize + 1) * m_bufferSize; try { MORDOR_LOG_TRACE(g_log) << this << " parent()->read(" << todo << ")"; result = parent()->read(m_readBuffer, todo); MORDOR_LOG_DEBUG(g_log) << this << " parent()->read(" << todo << "): " << result; } catch (...) { if (remaining == length) { MORDOR_LOG_VERBOSE(g_log) << this << " forwarding exception"; throw; } else { MORDOR_LOG_VERBOSE(g_log) << this << " swallowing exception"; // Swallow the exception return length - remaining; } } buffered = std::min(m_readBuffer.readAvailable(), remaining); m_readBuffer.copyOut(buffer, buffered); m_readBuffer.consume(buffered); advance(buffer, buffered); remaining -= buffered; } while (remaining > 0 && !m_allowPartialReads && result != 0); } return length - remaining; }
long long BufferedStream::seek(long long offset, Anchor anchor) { MORDOR_ASSERT(parent()->supportsTell()); long long parentPos = parent()->tell(); long long bufferedPos = parentPos - m_readBuffer.readAvailable() + m_writeBuffer.readAvailable(); long long parentSize = parent()->supportsSize() ? -1ll : parent()->size(); // Check for no change in position if ((offset == 0 && anchor == CURRENT) || (offset == bufferedPos && anchor == BEGIN) || (parentSize != -1ll && offset + parentSize == bufferedPos && anchor == END)) return bufferedPos; MORDOR_ASSERT(supportsSeek()); flush(false); MORDOR_ASSERT(m_writeBuffer.readAvailable() == 0u); switch (anchor) { case BEGIN: MORDOR_ASSERT(offset >= 0); if (offset >= bufferedPos && offset <= parentPos) { m_readBuffer.consume((size_t)(offset - bufferedPos)); return offset; } m_readBuffer.clear(); break; case CURRENT: if (offset > 0 && offset <= (long long)m_readBuffer.readAvailable()) { m_readBuffer.consume((size_t)offset); return bufferedPos + offset; } offset -= m_readBuffer.readAvailable(); m_readBuffer.clear(); break; case END: if (parentSize == -1ll) throw std::invalid_argument("Can't seek from end without known size"); if (parentSize + offset >= bufferedPos && parentSize + offset <= parentPos) { m_readBuffer.consume((size_t)(parentSize + offset - bufferedPos)); return parentSize + offset; } m_readBuffer.clear(); break; default: MORDOR_NOTREACHED(); } return parent()->seek(offset, anchor); }
long long HandleStream::seek(long long offset, Anchor anchor) { SchedulerSwitcher switcher(m_ioManager ? NULL : m_scheduler); if (m_ioManager) { if (supportsSeek()) { switch (anchor) { case BEGIN: if (offset < 0) { MORDOR_THROW_EXCEPTION(std::invalid_argument("resulting offset is negative")); } return m_pos = offset; case CURRENT: if (m_pos + offset < 0) { MORDOR_THROW_EXCEPTION(std::invalid_argument("resulting offset is negative")); } return m_pos += offset; case END: { long long end = size(); if (end + offset < 0) { MORDOR_THROW_EXCEPTION(std::invalid_argument("resulting offset is negative")); } return m_pos = end + offset; } default: MORDOR_ASSERT(false); } } else { MORDOR_ASSERT(false); } } long long pos; BOOL ret = SetFilePointerEx(m_hFile, *(LARGE_INTEGER*)&offset, (LARGE_INTEGER*)&pos, (DWORD)anchor); DWORD error = GetLastError(); MORDOR_LOG_LEVEL(g_log, ret ? Log::VERBOSE : Log::ERROR) << this << " SetFilePointerEx(" << m_hFile << ", " << offset << ", " << pos << ", " << anchor << "): " << ret << " (" << error << ")"; if (!ret) MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "SetFilePointerEx"); return pos; }
size_t BufferedStream::flushWrite(size_t length) { while (m_writeBuffer.readAvailable() >= m_bufferSize) { size_t result; try { if (m_readBuffer.readAvailable() && supportsSeek()) { parent()->seek(-(long long)m_readBuffer.readAvailable(), CURRENT); m_readBuffer.clear(); } size_t toWrite = m_writeBuffer.readAvailable(); if (m_flushMultiplesOfBuffer) toWrite = toWrite / m_bufferSize * m_bufferSize; MORDOR_LOG_TRACE(g_log) << this << " parent()->write(" << m_writeBuffer.readAvailable() << ")"; result = parent()->write(m_writeBuffer, toWrite); MORDOR_LOG_DEBUG(g_log) << this << " parent()->write(" << m_writeBuffer.readAvailable() << "): " << result; m_writeBuffer.consume(result); } catch (...) { // If this entire write is still in our buffer, // back it out and report the error if (m_writeBuffer.readAvailable() >= length) { MORDOR_LOG_VERBOSE(g_log) << this << " forwarding exception"; Buffer tempBuffer; tempBuffer.copyIn(m_writeBuffer, m_writeBuffer.readAvailable() - length); m_writeBuffer.clear(); m_writeBuffer.copyIn(tempBuffer); throw; } else { // Otherwise we have to say we succeeded, // because we're not allowed to have a partial // write, and we can't report an error because // the caller will think he needs to repeat // the entire write MORDOR_LOG_VERBOSE(g_log) << this << " swallowing exception"; return length; } } } return length; }
void BufferedStream::flush(bool flushParent) { while (m_writeBuffer.readAvailable()) { if (m_readBuffer.readAvailable() && supportsSeek()) { parent()->seek(-(long long)m_readBuffer.readAvailable(), CURRENT); m_readBuffer.clear(); } MORDOR_LOG_TRACE(g_log) << this << " parent()->write(" << m_writeBuffer.readAvailable() << ")"; size_t result = parent()->write(m_writeBuffer, m_writeBuffer.readAvailable()); MORDOR_LOG_DEBUG(g_log) << this << " parent()->write(" << m_writeBuffer.readAvailable() << "): " << result; MORDOR_ASSERT(result > 0); m_writeBuffer.consume(result); } if (flushParent) parent()->flush(); }