CXBMCTinyXML: use fast direct file reading #1445

Merged
merged 16 commits into from Aug 6, 2013
+113 −75
View
65 xbmc/guilib/Texture.cpp
@@ -25,6 +25,7 @@
#include "DDSImage.h"
#include "filesystem/SpecialProtocol.h"
#include "filesystem/File.h"
+#include "utils/FileUtils.h"
#if defined(TARGET_DARWIN_IOS)
#include <ImageIO/ImageIO.h>
#include "filesystem/File.h"
@@ -299,74 +300,20 @@ bool CBaseTexture::LoadFromFileInternal(const CStdString& texturePath, unsigned
unsigned int height = maxHeight ? std::min(maxHeight, g_Windowing.GetMaxTextureSize()) : g_Windowing.GetMaxTextureSize();
// Read image into memory to use our vfs
- unsigned char *inputBuff = NULL;
- unsigned int inputBuffSize = 0;
+ void *inputBuff = NULL;
+ unsigned int inputBuffSize = CFileUtils::LoadFile(texturePath, inputBuff);
- XFILE::CFile file;
- if (file.Open(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 ? (filesize + 1) : std::max(65536U, (unsigned int)file.GetChunkSize());
- unsigned int maxchunksize = 2048*1024U; /* max 2MB chunksize */
- unsigned char *tempinputBuff = NULL;
-
- unsigned int total_read = 0, free_space = 0;
- while (true)
- {
- if (!free_space)
- { // (re)alloc
- inputBuffSize += chunksize;
- tempinputBuff = (unsigned char *)realloc(inputBuff, inputBuffSize);
- if (!tempinputBuff)
- {
- CLog::Log(LOGERROR, "%s unable to allocate buffer of size %u", __FUNCTION__, inputBuffSize);
- free(inputBuff);
- file.Close();
- return false;
- }
- inputBuff = tempinputBuff;
- free_space = chunksize;
- chunksize = std::min(chunksize*2, maxchunksize);
- }
- unsigned int read = file.Read(inputBuff + total_read, free_space);
- free_space -= read;
- total_read += read;
- if (!read)
- break;
- }
- inputBuffSize = total_read;
- file.Close();
-
- if (inputBuffSize == 0)
- return false;
- }
- else
+ if (inputBuffSize == 0)
return false;
CURL url(texturePath);
IImage* pImage = ImageFactory::CreateLoader(url);
- if(!LoadIImage(pImage, inputBuff, inputBuffSize, width, height, autoRotate))
+ if(!LoadIImage(pImage, (unsigned char *) inputBuff, inputBuffSize, width, height, autoRotate))
{
delete pImage;
pImage = NULL;
pImage = ImageFactory::CreateFallbackLoader(texturePath);
- if(!LoadIImage(pImage, inputBuff, inputBuffSize, width, height))
+ if(!LoadIImage(pImage, (unsigned char *) inputBuff, inputBuffSize, width, height))
{
CLog::Log(LOGDEBUG, "%s - Load of %s failed.", __FUNCTION__, texturePath.c_str());
delete pImage;
View
100 xbmc/utils/FileUtils.cpp
@@ -128,4 +128,102 @@ bool CFileUtils::RemoteAccessAllowed(const CStdString &strPath)
return true;
}
return false;
-}
+}
+
+
+unsigned int CFileUtils::LoadFile(const std::string &filename, void* &outputBuffer)
+{
+ static const unsigned int max_file_size = 0x7FFFFFFF;
+ static const unsigned int min_chunk_size = 64*1024U;
+ static const unsigned int max_chunk_size = 2048*1024U;
+
+ outputBuffer = NULL;
+ if (filename.empty())
+ return 0;
+
+ XFILE::CFile file;
+ if (!file.Open(filename, READ_TRUNCATED))
+ return 0;
+
+ /*
+ 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 lowest value larger than min_chunk_size aligned
+ to 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 read while chunksize is lower
+ than max_chunk_size.
+ */
+ int64_t filesize = file.GetLength();
+ if (filesize > max_file_size)
+ { /* file is too large for this function */
+ file.Close();
+ return 0;
+ }
+ unsigned int chunksize = (filesize > 0) ? (unsigned int)(filesize + 1) : CFile::GetChunkSize(file.GetChunkSize(), min_chunk_size);
+ unsigned char *inputBuff = NULL;
+ unsigned int inputBuffSize = 0;
+
+ unsigned int total_read = 0, free_space = 0;
+ while (true)
+ {
+ if (!free_space)
+ { // (re)alloc
+ inputBuffSize += chunksize;
+ unsigned char *tempinputBuff = NULL;
+ if (inputBuffSize <= max_file_size)
+ tempinputBuff = (unsigned char *)realloc(inputBuff, inputBuffSize);
+ if (!tempinputBuff)
+ {
+ CLog::Log(LOGERROR, "%s unable to (re)allocate buffer of size %u for file \"%s\"", __FUNCTION__, inputBuffSize, filename.c_str());
+ free(inputBuff);
+ file.Close();
+ return 0;
+ }
+ inputBuff = tempinputBuff;
+ free_space = chunksize;
+ if (chunksize < max_chunk_size)
+ chunksize *= 2;
+ }
+ unsigned int read = file.Read(inputBuff + total_read, free_space);
+ free_space -= read;
+ total_read += read;
+ if (!read)
+ break;
+ }
+
+ file.Close();
+
+ if (total_read == 0)
+ {
+ free(inputBuff);
+ return 0;
+ }
+
+ if (total_read + 1 < inputBuffSize)
+ {
+ /* free extra memory if more than 1 byte (cases 1 and 3) */
+ unsigned char *tempinputBuff = (unsigned char *)realloc(inputBuff, total_read);
+ if (!tempinputBuff)
+ {
+ /* just a precaution, shouldn't really happen */
+ CLog::Log(LOGERROR, "%s unable to reallocate buffer for file \"%s\"", __FUNCTION__, filename.c_str());
+ free(inputBuff);
+ return 0;
+ }
+ inputBuff = tempinputBuff;
+ }
+
+ outputBuffer = (void *) inputBuff;
+ return total_read;
+}
+
View
1 xbmc/utils/FileUtils.h
@@ -8,4 +8,5 @@ class CFileUtils
static bool DeleteItem(const CStdString &strPath, bool force=false);
static bool RenameFile(const CStdString &strFile);
static bool RemoteAccessAllowed(const CStdString &strPath);
+ static unsigned int LoadFile(const std::string &filename, void* &outputBuffer);
};
View
22 xbmc/utils/XBMCTinyXML.cpp
@@ -19,6 +19,7 @@
#include "XBMCTinyXML.h"
#include "filesystem/File.h"
+#include "utils/FileUtils.h"
#include "RegExp.h"
#define MAX_ENTITY_LENGTH 8 // size of largest entity "&#xNNNN;"
@@ -52,18 +53,11 @@ bool CXBMCTinyXML::LoadFile(const char *_filename, TiXmlEncoding encoding)
bool CXBMCTinyXML::LoadFile(const CStdString &_filename, TiXmlEncoding encoding)
{
- // There was a really terrifying little bug here. The code:
- // value = filename
- // in the STL case, cause the assignment method of the std::string to
- // be called. What is strange, is that the std::string had the same
- // address as it's c_str() method, and so bad things happen. Looks
- // like a bug in the Microsoft STL implementation.
- // Add an extra string to avoid the crash.
- CStdString filename(_filename);
- value = filename;
+ value = _filename.c_str();
- XFILE::CFileStream file;
- if (!file.Open(value))
+ void * buffPtr;
+ unsigned int buffSize = CFileUtils::LoadFile(value, buffPtr);
+ if (buffSize == 0)
{
SetError(TIXML_ERROR_OPENING_FILE, NULL, NULL, TIXML_ENCODING_UNKNOWN);
return false;
@@ -73,10 +67,8 @@ bool CXBMCTinyXML::LoadFile(const CStdString &_filename, TiXmlEncoding encoding)
Clear();
location.Clear();
- CStdString data;
- data.reserve(8 * 1000);
- StreamIn(&file, &data);
- file.Close();
+ CStdString data ((char*) buffPtr, (size_t) buffSize);
+ free(buffPtr);
Parse(data, NULL, encoding);