/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. This program 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 3 of the License, or (at your option) any later version. This program 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 this program. If not, see . You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ #include "hsSTLStream.h" hsVectorStream::hsVectorStream() : fEnd(0) { } hsVectorStream::hsVectorStream(UInt32 chunkSize) { fVector.reserve(chunkSize); } hsVectorStream::~hsVectorStream() { } hsBool hsVectorStream::AtEnd() { return (fBytesRead >= fEnd); } UInt32 hsVectorStream::Read(UInt32 byteCount, void *buffer) { if (fBytesRead + byteCount > fEnd) { // hsStatusMessageF("Reading past end of hsVectorStream (read %u of %u requested bytes)", fEnd-fBytesRead, byteCount); byteCount = fEnd - fBytesRead; } memcpy(buffer, &fVector[fBytesRead], byteCount); fBytesRead += byteCount; fPosition += byteCount; return byteCount; } UInt32 hsVectorStream::Write(UInt32 byteCount, const void* buffer) { // If we are at the end of the vector, we can just do a block insert of the data if (fPosition == fVector.size()) fVector.insert(fVector.end(), (Byte*)buffer, (Byte*)buffer+byteCount); // If we are in the middle, I don't know how to just overwrite a block of the vector. // So, we make sure there is enough space and copy the elements one by one else { fVector.reserve(fPosition+byteCount); for (UInt32 i = 0; i < byteCount; i++) fVector[fPosition+i] = ((Byte*)buffer)[i]; } fPosition += byteCount; if (fPosition > fEnd) fEnd = fPosition; return byteCount; } void hsVectorStream::Skip(UInt32 deltaByteCount) { fBytesRead += deltaByteCount; fPosition += deltaByteCount; } void hsVectorStream::Rewind() { fBytesRead = 0; fPosition = 0; } void hsVectorStream::FastFwd() { fBytesRead = fPosition = fEnd; } void hsVectorStream::Truncate() { fVector.erase(fVector.begin()+fPosition, fVector.end()); fEnd = fPosition-1; } UInt32 hsVectorStream::GetEOF() { return fEnd; } void hsVectorStream::CopyToMem(void* mem) { memcpy(mem, &fVector[0], fEnd); } void hsVectorStream::Erase(UInt32 bytes) { hsAssert(fPosition+bytes <= fEnd, "Erasing past end of stream"); fVector.erase(fVector.begin()+fPosition, fVector.begin()+fPosition+bytes); fEnd -= bytes; } void hsVectorStream::Reset() { fBytesRead = 0; fPosition = 0; fEnd = 0; fVector.clear(); } const void *hsVectorStream::GetData() { if (fVector.size() > 0) return &fVector[0]; else return nil; } ///////////////////////////////////////////////////////////////////////////////////////// #ifdef HS_BUILD_FOR_WIN32 hsNamedPipeStream::hsNamedPipeStream(UInt8 flags, UInt32 timeout) : fFlags(flags), fPipe(INVALID_HANDLE_VALUE), fReadMode(false), fTimeout(timeout) { memset(&fOverlap, 0, sizeof(OVERLAPPED)); fOverlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); } hsNamedPipeStream::~hsNamedPipeStream() { CloseHandle(fOverlap.hEvent); fOverlap.hEvent = INVALID_HANDLE_VALUE; } hsBool hsNamedPipeStream::WaitForClientConnect() { // Look for a client connect (this should return zero since it's overlapped) BOOL ret = ConnectNamedPipe(fPipe, &fOverlap); if (ret) return true; else { switch (GetLastError()) { // Waiting for client to connect case ERROR_IO_PENDING: if (WaitForSingleObject(fOverlap.hEvent, fTimeout) == WAIT_OBJECT_0) return true; break; // Client is already connected case ERROR_PIPE_CONNECTED: // if (SetEvent(fOverlap.hEvent)) return true; break; } } return false; } hsBool hsNamedPipeStream::Open(const char *name, const char *mode) { wchar* wName = hsStringToWString(name); wchar* wMode = hsStringToWString(mode); hsBool ret = Open(wName, wMode); delete [] wName; delete [] wMode; return ret; } hsBool hsNamedPipeStream::Open(const wchar *name, const wchar *mode) { if (wcschr(mode, L'w')) { fReadMode = false; // Try to create the pipe fPipe = CreateNamedPipeW(name, PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, 1, 1024, 1024, fTimeout, NULL); if (fPipe != INVALID_HANDLE_VALUE) return true; } else if (wcschr(mode, L'r')) { fReadMode = true; fPipe = CreateFileW(name, GENERIC_READ, 0, // no sharing NULL, // no security attributes OPEN_EXISTING, // opens existing pipe FILE_FLAG_OVERLAPPED, // default attributes NULL); // no template file if (fPipe != INVALID_HANDLE_VALUE) return true; } return false; } hsBool hsNamedPipeStream::Close() { if (fPipe == INVALID_HANDLE_VALUE) return false; if (fReadMode) { CloseHandle(fPipe); // Close our end of the pipe fPipe = INVALID_HANDLE_VALUE; } else { FlushFileBuffers(fPipe); // Make sure the client is done reading DisconnectNamedPipe(fPipe); // Disconnect the pipe from the client CloseHandle(fPipe); // Close our end of the pipe fPipe = INVALID_HANDLE_VALUE; } return true; } hsBool hsNamedPipeStream::ICheckOverlappedResult(BOOL result, UInt32 &numTransferred) { // Read/Write succeeded, return now if (result) return true; // Read failed because the operation is taking a while. Wait for it else if (GetLastError() == ERROR_IO_PENDING) { if (WaitForSingleObject(fOverlap.hEvent, fTimeout) == WAIT_OBJECT_0) { BOOL oResult = GetOverlappedResult(fPipe, &fOverlap, &numTransferred, FALSE); if (oResult) return true; hsAssert(oResult, "GetOverlappedResult failed"); } else hsAssert(0, "Wait failed"); } else hsAssert(0, "Read/Write failed"); return false; } hsBool hsNamedPipeStream::IRead(UInt32 byteCount, void *buffer, UInt32 &numRead) { numRead = 0; if (fPipe != INVALID_HANDLE_VALUE && fReadMode) { BOOL result = ReadFile(fPipe, buffer, byteCount, &numRead, &fOverlap); if (ICheckOverlappedResult(result, numRead)) return true; } // If we got here, the pipe is probably broken. Throw if it is enabled. if (fFlags & kThrowOnError) throw this; return false; } hsBool hsNamedPipeStream::IWrite(UInt32 byteCount, const void *buffer, UInt32 &numWritten) { numWritten = 0; if (fPipe != INVALID_HANDLE_VALUE && !fReadMode) { BOOL result = WriteFile(fPipe, buffer, byteCount, &numWritten, &fOverlap); if (ICheckOverlappedResult(result, numWritten)) return true; } // If we got here, the pipe is probably broken. Throw if it is enabled. if (fFlags & kThrowOnError) throw this; return false; } UInt32 hsNamedPipeStream::Read(UInt32 byteCount, void *buffer) { UInt32 totalRead = 0; // Read until we get all our data or an error UInt32 numRead = 0; while (IRead(byteCount-totalRead, (void*)((UInt32)buffer+totalRead), numRead)) { totalRead += numRead; if (totalRead >= byteCount) return totalRead; } return totalRead; } UInt32 hsNamedPipeStream::Write(UInt32 byteCount, const void *buffer) { UInt32 totalWritten = 0; // Write until we get all our data or an error UInt32 numWritten = 0; while (IWrite(byteCount-totalWritten, (const void*)((UInt32)buffer+totalWritten), numWritten)) { totalWritten += numWritten; if (totalWritten >= byteCount) return totalWritten; } return totalWritten; } #ifdef __SGI_STL_PORT using std::min; #endif void hsNamedPipeStream::Skip(UInt32 deltaByteCount) { char buf[256]; // Read until we get all our data or an error UInt32 totalRead = 0; UInt32 numRead = 0; while (IRead(min((UInt32)256L, deltaByteCount-totalRead), buf, numRead)) { totalRead += numRead; if (totalRead >= deltaByteCount) return; } } void hsNamedPipeStream::Rewind() { hsAssert(0, "Rewind not allowed on a pipe"); } #endif // HS_BUILD_FOR_WIN32