Home | History | Annotate | Download | only in Common
      1 // FileStreams.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #ifndef _WIN32
      6 #include <fcntl.h>
      7 #include <unistd.h>
      8 #include <errno.h>
      9 #endif
     10 
     11 #ifdef SUPPORT_DEVICE_FILE
     12 #include "../../../C/Alloc.h"
     13 #include "../../Common/Defs.h"
     14 #endif
     15 
     16 #include "FileStreams.h"
     17 
     18 static inline HRESULT ConvertBoolToHRESULT(bool result)
     19 {
     20   #ifdef _WIN32
     21   if (result)
     22     return S_OK;
     23   DWORD lastError = ::GetLastError();
     24   if (lastError == 0)
     25     return E_FAIL;
     26   return HRESULT_FROM_WIN32(lastError);
     27   #else
     28   return result ? S_OK: E_FAIL;
     29   #endif
     30 }
     31 
     32 bool CInFileStream::Open(LPCTSTR fileName)
     33 {
     34   return File.Open(fileName);
     35 }
     36 
     37 #ifdef USE_WIN_FILE
     38 #ifndef _UNICODE
     39 bool CInFileStream::Open(LPCWSTR fileName)
     40 {
     41   return File.Open(fileName);
     42 }
     43 #endif
     44 #endif
     45 
     46 bool CInFileStream::OpenShared(LPCTSTR fileName, bool shareForWrite)
     47 {
     48   return File.OpenShared(fileName, shareForWrite);
     49 }
     50 
     51 #ifdef USE_WIN_FILE
     52 #ifndef _UNICODE
     53 bool CInFileStream::OpenShared(LPCWSTR fileName, bool shareForWrite)
     54 {
     55   return File.OpenShared(fileName, shareForWrite);
     56 }
     57 #endif
     58 #endif
     59 
     60 #ifdef SUPPORT_DEVICE_FILE
     61 
     62 static const UInt32 kClusterSize = 1 << 18;
     63 CInFileStream::CInFileStream():
     64   VirtPos(0),
     65   PhyPos(0),
     66   Buffer(0),
     67   BufferSize(0)
     68 {
     69 }
     70 
     71 #endif
     72 
     73 CInFileStream::~CInFileStream()
     74 {
     75   #ifdef SUPPORT_DEVICE_FILE
     76   MidFree(Buffer);
     77   #endif
     78 }
     79 
     80 STDMETHODIMP CInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize)
     81 {
     82   #ifdef USE_WIN_FILE
     83 
     84   #ifdef SUPPORT_DEVICE_FILE
     85   if (processedSize != NULL)
     86     *processedSize = 0;
     87   if (size == 0)
     88     return S_OK;
     89   if (File.IsDeviceFile)
     90   {
     91     if (File.LengthDefined)
     92     {
     93       if (VirtPos >= File.Length)
     94         return VirtPos == File.Length ? S_OK : E_FAIL;
     95       UInt64 rem = File.Length - VirtPos;
     96       if (size > rem)
     97         size = (UInt32)rem;
     98     }
     99     for (;;)
    100     {
    101       const UInt32 mask = kClusterSize - 1;
    102       UInt64 mask2 = ~(UInt64)mask;
    103       UInt64 alignedPos = VirtPos & mask2;
    104       if (BufferSize > 0 && BufferStartPos == alignedPos)
    105       {
    106         UInt32 pos = (UInt32)VirtPos & mask;
    107         if (pos >= BufferSize)
    108           return S_OK;
    109         UInt32 rem = MyMin(BufferSize - pos, size);
    110         memcpy(data, Buffer + pos, rem);
    111         VirtPos += rem;
    112         if (processedSize != NULL)
    113           *processedSize += rem;
    114         return S_OK;
    115       }
    116 
    117       bool useBuffer = false;
    118       if ((VirtPos & mask) != 0 || ((ptrdiff_t)data & mask) != 0 )
    119         useBuffer = true;
    120       else
    121       {
    122         UInt64 end = VirtPos + size;
    123         if ((end & mask) != 0)
    124         {
    125           end &= mask2;
    126           if (end <= VirtPos)
    127             useBuffer = true;
    128           else
    129             size = (UInt32)(end - VirtPos);
    130         }
    131       }
    132       if (!useBuffer)
    133         break;
    134       if (alignedPos != PhyPos)
    135       {
    136         UInt64 realNewPosition;
    137         bool result = File.Seek(alignedPos, FILE_BEGIN, realNewPosition);
    138         if (!result)
    139           return ConvertBoolToHRESULT(result);
    140         PhyPos = realNewPosition;
    141       }
    142 
    143       BufferStartPos = alignedPos;
    144       UInt32 readSize = kClusterSize;
    145       if (File.LengthDefined)
    146         readSize = (UInt32)MyMin(File.Length - PhyPos, (UInt64)kClusterSize);
    147 
    148       if (Buffer == 0)
    149       {
    150         Buffer = (Byte *)MidAlloc(kClusterSize);
    151         if (Buffer == 0)
    152           return E_OUTOFMEMORY;
    153       }
    154       bool result = File.Read1(Buffer, readSize, BufferSize);
    155       if (!result)
    156         return ConvertBoolToHRESULT(result);
    157 
    158       if (BufferSize == 0)
    159         return S_OK;
    160       PhyPos += BufferSize;
    161     }
    162 
    163     if (VirtPos != PhyPos)
    164     {
    165       UInt64 realNewPosition;
    166       bool result = File.Seek(VirtPos, FILE_BEGIN, realNewPosition);
    167       if (!result)
    168         return ConvertBoolToHRESULT(result);
    169       PhyPos = VirtPos = realNewPosition;
    170     }
    171   }
    172   #endif
    173 
    174   UInt32 realProcessedSize;
    175   bool result = File.ReadPart(data, size, realProcessedSize);
    176   if (processedSize != NULL)
    177     *processedSize = realProcessedSize;
    178   #ifdef SUPPORT_DEVICE_FILE
    179   VirtPos += realProcessedSize;
    180   PhyPos += realProcessedSize;
    181   #endif
    182   return ConvertBoolToHRESULT(result);
    183 
    184   #else
    185 
    186   if (processedSize != NULL)
    187     *processedSize = 0;
    188   ssize_t res = File.Read(data, (size_t)size);
    189   if (res == -1)
    190     return E_FAIL;
    191   if (processedSize != NULL)
    192     *processedSize = (UInt32)res;
    193   return S_OK;
    194 
    195   #endif
    196 }
    197 
    198 #ifdef UNDER_CE
    199 STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize)
    200 {
    201   size_t s2 = fread(data, 1, size, stdout);
    202   if (processedSize != 0)
    203     *processedSize = s2;
    204   return (s2 = size) ? S_OK : E_FAIL;
    205 }
    206 #else
    207 STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize)
    208 {
    209   #ifdef _WIN32
    210 
    211   DWORD realProcessedSize;
    212   UInt32 sizeTemp = (1 << 20);
    213   if (sizeTemp > size)
    214     sizeTemp = size;
    215   BOOL res = ::ReadFile(GetStdHandle(STD_INPUT_HANDLE), data, sizeTemp, &realProcessedSize, NULL);
    216   if (processedSize != NULL)
    217     *processedSize = realProcessedSize;
    218   if (res == FALSE && GetLastError() == ERROR_BROKEN_PIPE)
    219     return S_OK;
    220   return ConvertBoolToHRESULT(res != FALSE);
    221 
    222   #else
    223 
    224   if (processedSize != NULL)
    225     *processedSize = 0;
    226   ssize_t res;
    227   do
    228   {
    229     res = read(0, data, (size_t)size);
    230   }
    231   while (res < 0 && (errno == EINTR));
    232   if (res == -1)
    233     return E_FAIL;
    234   if (processedSize != NULL)
    235     *processedSize = (UInt32)res;
    236   return S_OK;
    237 
    238   #endif
    239 }
    240 
    241 #endif
    242 
    243 STDMETHODIMP CInFileStream::Seek(Int64 offset, UInt32 seekOrigin,
    244     UInt64 *newPosition)
    245 {
    246   if (seekOrigin >= 3)
    247     return STG_E_INVALIDFUNCTION;
    248 
    249   #ifdef USE_WIN_FILE
    250 
    251   #ifdef SUPPORT_DEVICE_FILE
    252   if (File.IsDeviceFile)
    253   {
    254     UInt64 newVirtPos = offset;
    255     switch(seekOrigin)
    256     {
    257       case STREAM_SEEK_SET: break;
    258       case STREAM_SEEK_CUR: newVirtPos += VirtPos; break;
    259       case STREAM_SEEK_END: newVirtPos += File.Length; break;
    260       default: return STG_E_INVALIDFUNCTION;
    261     }
    262     VirtPos = newVirtPos;
    263     if (newPosition)
    264       *newPosition = newVirtPos;
    265     return S_OK;
    266   }
    267   #endif
    268 
    269   UInt64 realNewPosition;
    270   bool result = File.Seek(offset, seekOrigin, realNewPosition);
    271 
    272   #ifdef SUPPORT_DEVICE_FILE
    273   PhyPos = VirtPos = realNewPosition;
    274   #endif
    275 
    276   if (newPosition != NULL)
    277     *newPosition = realNewPosition;
    278   return ConvertBoolToHRESULT(result);
    279 
    280   #else
    281 
    282   off_t res = File.Seek(offset, seekOrigin);
    283   if (res == -1)
    284     return E_FAIL;
    285   if (newPosition != NULL)
    286     *newPosition = (UInt64)res;
    287   return S_OK;
    288 
    289   #endif
    290 }
    291 
    292 STDMETHODIMP CInFileStream::GetSize(UInt64 *size)
    293 {
    294   return ConvertBoolToHRESULT(File.GetLength(*size));
    295 }
    296 
    297 
    298 //////////////////////////
    299 // COutFileStream
    300 
    301 HRESULT COutFileStream::Close()
    302 {
    303   return ConvertBoolToHRESULT(File.Close());
    304 }
    305 
    306 STDMETHODIMP COutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
    307 {
    308   #ifdef USE_WIN_FILE
    309 
    310   UInt32 realProcessedSize;
    311   bool result = File.WritePart(data, size, realProcessedSize);
    312   ProcessedSize += realProcessedSize;
    313   if (processedSize != NULL)
    314     *processedSize = realProcessedSize;
    315   return ConvertBoolToHRESULT(result);
    316 
    317   #else
    318 
    319   if (processedSize != NULL)
    320     *processedSize = 0;
    321   ssize_t res = File.Write(data, (size_t)size);
    322   if (res == -1)
    323     return E_FAIL;
    324   if (processedSize != NULL)
    325     *processedSize = (UInt32)res;
    326   ProcessedSize += res;
    327   return S_OK;
    328 
    329   #endif
    330 }
    331 
    332 STDMETHODIMP COutFileStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
    333 {
    334   if (seekOrigin >= 3)
    335     return STG_E_INVALIDFUNCTION;
    336   #ifdef USE_WIN_FILE
    337 
    338   UInt64 realNewPosition;
    339   bool result = File.Seek(offset, seekOrigin, realNewPosition);
    340   if (newPosition != NULL)
    341     *newPosition = realNewPosition;
    342   return ConvertBoolToHRESULT(result);
    343 
    344   #else
    345 
    346   off_t res = File.Seek(offset, seekOrigin);
    347   if (res == -1)
    348     return E_FAIL;
    349   if (newPosition != NULL)
    350     *newPosition = (UInt64)res;
    351   return S_OK;
    352 
    353   #endif
    354 }
    355 
    356 STDMETHODIMP COutFileStream::SetSize(UInt64 newSize)
    357 {
    358   #ifdef USE_WIN_FILE
    359   UInt64 currentPos;
    360   if (!File.Seek(0, FILE_CURRENT, currentPos))
    361     return E_FAIL;
    362   bool result = File.SetLength(newSize);
    363   UInt64 currentPos2;
    364   result = result && File.Seek(currentPos, currentPos2);
    365   return result ? S_OK : E_FAIL;
    366   #else
    367   return E_FAIL;
    368   #endif
    369 }
    370 
    371 #ifdef UNDER_CE
    372 STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
    373 {
    374   size_t s2 = fwrite(data, 1, size, stdout);
    375   if (processedSize != 0)
    376     *processedSize = s2;
    377   return (s2 = size) ? S_OK : E_FAIL;
    378 }
    379 #else
    380 STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
    381 {
    382   if (processedSize != NULL)
    383     *processedSize = 0;
    384 
    385   #ifdef _WIN32
    386   UInt32 realProcessedSize;
    387   BOOL res = TRUE;
    388   if (size > 0)
    389   {
    390     // Seems that Windows doesn't like big amounts writing to stdout.
    391     // So we limit portions by 32KB.
    392     UInt32 sizeTemp = (1 << 15);
    393     if (sizeTemp > size)
    394       sizeTemp = size;
    395     res = ::WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
    396         data, sizeTemp, (DWORD *)&realProcessedSize, NULL);
    397     size -= realProcessedSize;
    398     data = (const void *)((const Byte *)data + realProcessedSize);
    399     if (processedSize != NULL)
    400       *processedSize += realProcessedSize;
    401   }
    402   return ConvertBoolToHRESULT(res != FALSE);
    403 
    404   #else
    405 
    406   ssize_t res;
    407   do
    408   {
    409     res = write(1, data, (size_t)size);
    410   }
    411   while (res < 0 && (errno == EINTR));
    412   if (res == -1)
    413     return E_FAIL;
    414   if (processedSize != NULL)
    415     *processedSize = (UInt32)res;
    416   return S_OK;
    417 
    418   return S_OK;
    419   #endif
    420 }
    421 
    422 #endif
    423