XFILE::CFile file;
if (file.Open(m_texturePath.c_str(), READ_TRUNCATED))
{
+ /*
+ GetLength() will typically return values that fall into three cases:
+ 1. The real filesize. This is the typical case.
+ 2. Zero. This is the case for some http:// streams for example.
+ 3. Some value smaller than the real filesize. This is the case for an expanding file.
+
+ In order to handle all three cases, we read the file in chunks, relying on Read()
+ returning 0 at EOF. To minimize (re)allocation of the buffer, the chunksize in
+ cases 1 and 3 is set to one byte larger** than the value returned by GetLength().
+ The chunksize in case 2 is set to the larger of 64k and GetChunkSize().
+
+ We fill the buffer entirely before reallocation. Thus, reallocation never occurs in case 1
+ as the buffer is larger than the file, so we hit EOF before we hit the end of buffer.
+
+ To minimize reallocation, we double the chunksize each time up to a maxchunksize of 2MB.
+ */
unsigned int filesize = (unsigned int)file.GetLength();
- unsigned int chunksize = filesize, maxchunksize = filesize;
- if (!chunksize)
- { // no size information, so try reading chunked
- chunksize = std::max(65536U, (unsigned int)file.GetChunkSize());
- maxchunksize = std::max(chunksize, 2048*1024U); // max 2MB
- }
- unsigned int total = 0, amount = 0;
+ unsigned int chunksize = filesize ? (filesize + 1) : std::max(65536U, (unsigned int)file.GetChunkSize());
+ unsigned int maxchunksize = 2048*1024U; /* max 2MB chunksize */
+
+ unsigned int total_read = 0, free_space = 0;
while (true)
{
- if (!amount)
+ if (!free_space)
{ // (re)alloc
m_inputBuffSize += chunksize;
m_inputBuff = (unsigned char *)realloc(m_inputBuff, m_inputBuffSize);
CLog::Log(LOGERROR, "%s unable to allocate buffer of size %u", __FUNCTION__, m_inputBuffSize);
return false;
}
- amount = chunksize;
+ free_space = chunksize;
chunksize = std::min(chunksize*2, maxchunksize);
}
- unsigned int read = file.Read(m_inputBuff + total, amount);
- amount -= read;
- total += read;
- if (!read || total == filesize)
+ unsigned int read = file.Read(m_inputBuff + total_read, free_space);
+ free_space -= read;
+ total_read += read;
+ if (!read)
break;
}
- m_inputBuffSize = total;
+ m_inputBuffSize = total_read;
file.Close();
if (m_inputBuffSize == 0)