Make wxFile::ReadAll() work for unseekable files too
Calling this function with an unseekable file, such as any file under /sys on Linux systems, would previously just hang as the loop condition was never satisfied when length was -1. Fix this by checking for this case and using a different approach by extending the buffer we read the data into as we go instead of preallocating it all at once. See #9965.
This commit is contained in:
@@ -106,6 +106,7 @@ All:
|
|||||||
- Add UTF-8 support to wxZipOutputStream (Tobias Taschner).
|
- Add UTF-8 support to wxZipOutputStream (Tobias Taschner).
|
||||||
- Update all bundled 3rd party libraries to their latest versions.
|
- Update all bundled 3rd party libraries to their latest versions.
|
||||||
- Use unique prefix for all zlib symbols to avoid link conflicts.
|
- Use unique prefix for all zlib symbols to avoid link conflicts.
|
||||||
|
- Make wxFile::ReadAll() work for unseekable files too.
|
||||||
|
|
||||||
All (GUI):
|
All (GUI):
|
||||||
|
|
||||||
|
@@ -377,6 +377,8 @@ public:
|
|||||||
/**
|
/**
|
||||||
Reads the entire contents of the file into a string.
|
Reads the entire contents of the file into a string.
|
||||||
|
|
||||||
|
Since wxWidgets 3.1.1 this method also works for unseekable files.
|
||||||
|
|
||||||
@param str
|
@param str
|
||||||
Non-@NULL pointer to a string to read data into.
|
Non-@NULL pointer to a string to read data into.
|
||||||
@param conv
|
@param conv
|
||||||
|
@@ -261,24 +261,71 @@ bool wxFile::ReadAll(wxString *str, const wxMBConv& conv)
|
|||||||
{
|
{
|
||||||
wxCHECK_MSG( str, false, wxS("Output string must be non-NULL") );
|
wxCHECK_MSG( str, false, wxS("Output string must be non-NULL") );
|
||||||
|
|
||||||
|
static const ssize_t READSIZE = 4096;
|
||||||
|
|
||||||
|
wxCharBuffer buf;
|
||||||
|
|
||||||
ssize_t length = Length();
|
ssize_t length = Length();
|
||||||
wxCHECK_MSG( (wxFileOffset)length == Length(), false, wxT("huge file not supported") );
|
if ( length != -1 )
|
||||||
|
|
||||||
wxCharBuffer buf(length);
|
|
||||||
char* p = buf.data();
|
|
||||||
for ( ;; )
|
|
||||||
{
|
{
|
||||||
static const ssize_t READSIZE = 4096;
|
wxCHECK_MSG( (wxFileOffset)length == Length(), false, wxT("huge file not supported") );
|
||||||
|
|
||||||
ssize_t nread = Read(p, length > READSIZE ? READSIZE : length);
|
if ( !buf.extend(length) )
|
||||||
if ( nread == wxInvalidOffset )
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
p += nread;
|
char* p = buf.data();
|
||||||
if ( length <= nread )
|
for ( ;; )
|
||||||
break;
|
{
|
||||||
|
ssize_t nread = Read(p, length > READSIZE ? READSIZE : length);
|
||||||
|
if ( nread == wxInvalidOffset )
|
||||||
|
return false;
|
||||||
|
|
||||||
length -= nread;
|
if ( nread == 0 )
|
||||||
|
{
|
||||||
|
// We have reached EOF before reading the entire length of the
|
||||||
|
// file. This can happen for some special files (e.g. those
|
||||||
|
// under /sys on Linux systems) or even for regular files if
|
||||||
|
// another process has truncated the file since we started
|
||||||
|
// reading it, so deal with it gracefully.
|
||||||
|
buf.shrink(p - buf.data());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
p += nread;
|
||||||
|
length -= nread;
|
||||||
|
|
||||||
|
if ( !length )
|
||||||
|
{
|
||||||
|
// Notice that we don't keep reading after getting the expected
|
||||||
|
// number of bytes, even though in principle a situation
|
||||||
|
// similar to the one described above, with another process
|
||||||
|
// extending the file since we started to read it, is possible.
|
||||||
|
// But returning just the data that was in the file when we
|
||||||
|
// originally started reading it isn't really wrong in this
|
||||||
|
// case, so keep things simple and just do it like this.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // File is not seekable
|
||||||
|
{
|
||||||
|
for ( ;; )
|
||||||
|
{
|
||||||
|
const size_t len = buf.length();
|
||||||
|
if ( !buf.extend(len + READSIZE) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ssize_t nread = Read(buf.data() + len, READSIZE);
|
||||||
|
if ( nread == wxInvalidOffset )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ( nread < READSIZE )
|
||||||
|
{
|
||||||
|
// We have reached EOF.
|
||||||
|
buf.shrink(len + nread);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
str->assign(buf, conv);
|
str->assign(buf, conv);
|
||||||
|
@@ -139,4 +139,28 @@ void FileTestCase::TempFile()
|
|||||||
CPPUNIT_ASSERT( wxRemoveFile(wxT("test2")) );
|
CPPUNIT_ASSERT( wxRemoveFile(wxT("test2")) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __LINUX__
|
||||||
|
|
||||||
|
// Check that GetSize() works correctly for special files.
|
||||||
|
TEST_CASE("wxFile::Special", "[file][linux][special-file]")
|
||||||
|
{
|
||||||
|
// This file is not seekable and has 0 size, but can still be read.
|
||||||
|
wxFile fileProc("/proc/diskstats");
|
||||||
|
CHECK( fileProc.IsOpened() );
|
||||||
|
|
||||||
|
wxString s;
|
||||||
|
CHECK( fileProc.ReadAll(&s) );
|
||||||
|
CHECK( !s.empty() );
|
||||||
|
|
||||||
|
// All files in /sys seem to have size of 4KiB currently, even if they
|
||||||
|
// don't have that much data in them.
|
||||||
|
wxFile fileSys("/sys/power/state");
|
||||||
|
CHECK( fileSys.IsOpened() );
|
||||||
|
CHECK( fileSys.ReadAll(&s) );
|
||||||
|
CHECK( !s.empty() );
|
||||||
|
CHECK( s.length() < 4096 );
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __LINUX__
|
||||||
|
|
||||||
#endif // wxUSE_FILE
|
#endif // wxUSE_FILE
|
||||||
|
Reference in New Issue
Block a user