// FileStreams.cpp | |
#include "StdAfx.h" | |
#ifndef _WIN32 | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <errno.h> | |
#endif | |
#ifdef SUPPORT_DEVICE_FILE | |
#include "../../../C/Alloc.h" | |
#include "../../Common/Defs.h" | |
#endif | |
#include "FileStreams.h" | |
static inline HRESULT ConvertBoolToHRESULT(bool result) | |
{ | |
#ifdef _WIN32 | |
if (result) | |
return S_OK; | |
DWORD lastError = ::GetLastError(); | |
if (lastError == 0) | |
return E_FAIL; | |
return HRESULT_FROM_WIN32(lastError); | |
#else | |
return result ? S_OK: E_FAIL; | |
#endif | |
} | |
#ifdef SUPPORT_DEVICE_FILE | |
static const UInt32 kClusterSize = 1 << 18; | |
CInFileStream::CInFileStream(): | |
VirtPos(0), | |
PhyPos(0), | |
Buf(0), | |
BufSize(0), | |
SupportHardLinks(false) | |
{ | |
} | |
#endif | |
CInFileStream::~CInFileStream() | |
{ | |
#ifdef SUPPORT_DEVICE_FILE | |
MidFree(Buf); | |
#endif | |
} | |
STDMETHODIMP CInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize) | |
{ | |
#ifdef USE_WIN_FILE | |
#ifdef SUPPORT_DEVICE_FILE | |
if (processedSize) | |
*processedSize = 0; | |
if (size == 0) | |
return S_OK; | |
if (File.IsDeviceFile) | |
{ | |
if (File.SizeDefined) | |
{ | |
if (VirtPos >= File.Size) | |
return VirtPos == File.Size ? S_OK : E_FAIL; | |
UInt64 rem = File.Size - VirtPos; | |
if (size > rem) | |
size = (UInt32)rem; | |
} | |
for (;;) | |
{ | |
const UInt32 mask = kClusterSize - 1; | |
const UInt64 mask2 = ~(UInt64)mask; | |
UInt64 alignedPos = VirtPos & mask2; | |
if (BufSize > 0 && BufStartPos == alignedPos) | |
{ | |
UInt32 pos = (UInt32)VirtPos & mask; | |
if (pos >= BufSize) | |
return S_OK; | |
UInt32 rem = MyMin(BufSize - pos, size); | |
memcpy(data, Buf + pos, rem); | |
VirtPos += rem; | |
if (processedSize) | |
*processedSize += rem; | |
return S_OK; | |
} | |
bool useBuf = false; | |
if ((VirtPos & mask) != 0 || ((ptrdiff_t)data & mask) != 0 ) | |
useBuf = true; | |
else | |
{ | |
UInt64 end = VirtPos + size; | |
if ((end & mask) != 0) | |
{ | |
end &= mask2; | |
if (end <= VirtPos) | |
useBuf = true; | |
else | |
size = (UInt32)(end - VirtPos); | |
} | |
} | |
if (!useBuf) | |
break; | |
if (alignedPos != PhyPos) | |
{ | |
UInt64 realNewPosition; | |
bool result = File.Seek(alignedPos, FILE_BEGIN, realNewPosition); | |
if (!result) | |
return ConvertBoolToHRESULT(result); | |
PhyPos = realNewPosition; | |
} | |
BufStartPos = alignedPos; | |
UInt32 readSize = kClusterSize; | |
if (File.SizeDefined) | |
readSize = (UInt32)MyMin(File.Size - PhyPos, (UInt64)kClusterSize); | |
if (!Buf) | |
{ | |
Buf = (Byte *)MidAlloc(kClusterSize); | |
if (!Buf) | |
return E_OUTOFMEMORY; | |
} | |
bool result = File.Read1(Buf, readSize, BufSize); | |
if (!result) | |
return ConvertBoolToHRESULT(result); | |
if (BufSize == 0) | |
return S_OK; | |
PhyPos += BufSize; | |
} | |
if (VirtPos != PhyPos) | |
{ | |
UInt64 realNewPosition; | |
bool result = File.Seek(VirtPos, FILE_BEGIN, realNewPosition); | |
if (!result) | |
return ConvertBoolToHRESULT(result); | |
PhyPos = VirtPos = realNewPosition; | |
} | |
} | |
#endif | |
UInt32 realProcessedSize; | |
bool result = File.ReadPart(data, size, realProcessedSize); | |
if (processedSize) | |
*processedSize = realProcessedSize; | |
#ifdef SUPPORT_DEVICE_FILE | |
VirtPos += realProcessedSize; | |
PhyPos += realProcessedSize; | |
#endif | |
return ConvertBoolToHRESULT(result); | |
#else | |
if (processedSize) | |
*processedSize = 0; | |
ssize_t res = File.Read(data, (size_t)size); | |
if (res == -1) | |
return E_FAIL; | |
if (processedSize) | |
*processedSize = (UInt32)res; | |
return S_OK; | |
#endif | |
} | |
#ifdef UNDER_CE | |
STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize) | |
{ | |
size_t s2 = fread(data, 1, size, stdin); | |
int error = ferror(stdin); | |
if (processedSize) | |
*processedSize = s2; | |
if (s2 <= size && error == 0) | |
return S_OK; | |
return E_FAIL; | |
} | |
#else | |
STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize) | |
{ | |
#ifdef _WIN32 | |
DWORD realProcessedSize; | |
UInt32 sizeTemp = (1 << 20); | |
if (sizeTemp > size) | |
sizeTemp = size; | |
BOOL res = ::ReadFile(GetStdHandle(STD_INPUT_HANDLE), data, sizeTemp, &realProcessedSize, NULL); | |
if (processedSize) | |
*processedSize = realProcessedSize; | |
if (res == FALSE && GetLastError() == ERROR_BROKEN_PIPE) | |
return S_OK; | |
return ConvertBoolToHRESULT(res != FALSE); | |
#else | |
if (processedSize) | |
*processedSize = 0; | |
ssize_t res; | |
do | |
{ | |
res = read(0, data, (size_t)size); | |
} | |
while (res < 0 && (errno == EINTR)); | |
if (res == -1) | |
return E_FAIL; | |
if (processedSize) | |
*processedSize = (UInt32)res; | |
return S_OK; | |
#endif | |
} | |
#endif | |
STDMETHODIMP CInFileStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) | |
{ | |
if (seekOrigin >= 3) | |
return STG_E_INVALIDFUNCTION; | |
#ifdef USE_WIN_FILE | |
#ifdef SUPPORT_DEVICE_FILE | |
if (File.IsDeviceFile && (File.SizeDefined || seekOrigin != STREAM_SEEK_END)) | |
{ | |
switch (seekOrigin) | |
{ | |
case STREAM_SEEK_SET: break; | |
case STREAM_SEEK_CUR: offset += VirtPos; break; | |
case STREAM_SEEK_END: offset += File.Size; break; | |
default: return STG_E_INVALIDFUNCTION; | |
} | |
if (offset < 0) | |
return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; | |
VirtPos = offset; | |
if (newPosition) | |
*newPosition = offset; | |
return S_OK; | |
} | |
#endif | |
UInt64 realNewPosition; | |
bool result = File.Seek(offset, seekOrigin, realNewPosition); | |
#ifdef SUPPORT_DEVICE_FILE | |
PhyPos = VirtPos = realNewPosition; | |
#endif | |
if (newPosition) | |
*newPosition = realNewPosition; | |
return ConvertBoolToHRESULT(result); | |
#else | |
off_t res = File.Seek((off_t)offset, seekOrigin); | |
if (res == -1) | |
return E_FAIL; | |
if (newPosition) | |
*newPosition = (UInt64)res; | |
return S_OK; | |
#endif | |
} | |
STDMETHODIMP CInFileStream::GetSize(UInt64 *size) | |
{ | |
return ConvertBoolToHRESULT(File.GetLength(*size)); | |
} | |
#ifdef USE_WIN_FILE | |
STDMETHODIMP CInFileStream::GetProps(UInt64 *size, FILETIME *cTime, FILETIME *aTime, FILETIME *mTime, UInt32 *attrib) | |
{ | |
BY_HANDLE_FILE_INFORMATION info; | |
if (File.GetFileInformation(&info)) | |
{ | |
if (size) *size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow; | |
if (cTime) *cTime = info.ftCreationTime; | |
if (aTime) *aTime = info.ftLastAccessTime; | |
if (mTime) *mTime = info.ftLastWriteTime; | |
if (attrib) *attrib = info.dwFileAttributes; | |
return S_OK; | |
} | |
return GetLastError(); | |
} | |
STDMETHODIMP CInFileStream::GetProps2(CStreamFileProps *props) | |
{ | |
BY_HANDLE_FILE_INFORMATION info; | |
if (File.GetFileInformation(&info)) | |
{ | |
props->Size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow; | |
props->VolID = info.dwVolumeSerialNumber; | |
props->FileID_Low = (((UInt64)info.nFileIndexHigh) << 32) + info.nFileIndexLow; | |
props->FileID_High = 0; | |
props->NumLinks = SupportHardLinks ? info.nNumberOfLinks : 1; | |
props->Attrib = info.dwFileAttributes; | |
props->CTime = info.ftCreationTime; | |
props->ATime = info.ftLastAccessTime; | |
props->MTime = info.ftLastWriteTime; | |
return S_OK; | |
} | |
return GetLastError(); | |
} | |
#endif | |
////////////////////////// | |
// COutFileStream | |
HRESULT COutFileStream::Close() | |
{ | |
return ConvertBoolToHRESULT(File.Close()); | |
} | |
STDMETHODIMP COutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize) | |
{ | |
#ifdef USE_WIN_FILE | |
UInt32 realProcessedSize; | |
bool result = File.WritePart(data, size, realProcessedSize); | |
ProcessedSize += realProcessedSize; | |
if (processedSize) | |
*processedSize = realProcessedSize; | |
return ConvertBoolToHRESULT(result); | |
#else | |
if (processedSize) | |
*processedSize = 0; | |
ssize_t res = File.Write(data, (size_t)size); | |
if (res == -1) | |
return E_FAIL; | |
if (processedSize) | |
*processedSize = (UInt32)res; | |
ProcessedSize += res; | |
return S_OK; | |
#endif | |
} | |
STDMETHODIMP COutFileStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) | |
{ | |
if (seekOrigin >= 3) | |
return STG_E_INVALIDFUNCTION; | |
#ifdef USE_WIN_FILE | |
UInt64 realNewPosition; | |
bool result = File.Seek(offset, seekOrigin, realNewPosition); | |
if (newPosition) | |
*newPosition = realNewPosition; | |
return ConvertBoolToHRESULT(result); | |
#else | |
off_t res = File.Seek((off_t)offset, seekOrigin); | |
if (res == -1) | |
return E_FAIL; | |
if (newPosition) | |
*newPosition = (UInt64)res; | |
return S_OK; | |
#endif | |
} | |
STDMETHODIMP COutFileStream::SetSize(UInt64 newSize) | |
{ | |
#ifdef USE_WIN_FILE | |
UInt64 currentPos; | |
if (!File.Seek(0, FILE_CURRENT, currentPos)) | |
return E_FAIL; | |
bool result = File.SetLength(newSize); | |
UInt64 currentPos2; | |
result = result && File.Seek(currentPos, currentPos2); | |
return result ? S_OK : E_FAIL; | |
#else | |
return E_FAIL; | |
#endif | |
} | |
#ifdef UNDER_CE | |
STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize) | |
{ | |
size_t s2 = fwrite(data, 1, size, stdout); | |
if (processedSize) | |
*processedSize = s2; | |
return (s2 == size) ? S_OK : E_FAIL; | |
} | |
#else | |
STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize) | |
{ | |
if (processedSize) | |
*processedSize = 0; | |
#ifdef _WIN32 | |
UInt32 realProcessedSize; | |
BOOL res = TRUE; | |
if (size > 0) | |
{ | |
// Seems that Windows doesn't like big amounts writing to stdout. | |
// So we limit portions by 32KB. | |
UInt32 sizeTemp = (1 << 15); | |
if (sizeTemp > size) | |
sizeTemp = size; | |
res = ::WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), | |
data, sizeTemp, (DWORD *)&realProcessedSize, NULL); | |
size -= realProcessedSize; | |
data = (const void *)((const Byte *)data + realProcessedSize); | |
if (processedSize) | |
*processedSize += realProcessedSize; | |
} | |
return ConvertBoolToHRESULT(res != FALSE); | |
#else | |
ssize_t res; | |
do | |
{ | |
res = write(1, data, (size_t)size); | |
} | |
while (res < 0 && (errno == EINTR)); | |
if (res == -1) | |
return E_FAIL; | |
if (processedSize) | |
*processedSize = (UInt32)res; | |
return S_OK; | |
return S_OK; | |
#endif | |
} | |
#endif |