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).
|
||||
- Update all bundled 3rd party libraries to their latest versions.
|
||||
- Use unique prefix for all zlib symbols to avoid link conflicts.
|
||||
- Make wxFile::ReadAll() work for unseekable files too.
|
||||
|
||||
All (GUI):
|
||||
|
||||
|
@@ -377,6 +377,8 @@ public:
|
||||
/**
|
||||
Reads the entire contents of the file into a string.
|
||||
|
||||
Since wxWidgets 3.1.1 this method also works for unseekable files.
|
||||
|
||||
@param str
|
||||
Non-@NULL pointer to a string to read data into.
|
||||
@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") );
|
||||
|
||||
static const ssize_t READSIZE = 4096;
|
||||
|
||||
wxCharBuffer buf;
|
||||
|
||||
ssize_t length = Length();
|
||||
if ( length != -1 )
|
||||
{
|
||||
wxCHECK_MSG( (wxFileOffset)length == Length(), false, wxT("huge file not supported") );
|
||||
|
||||
wxCharBuffer buf(length);
|
||||
if ( !buf.extend(length) )
|
||||
return false;
|
||||
|
||||
char* p = buf.data();
|
||||
for ( ;; )
|
||||
{
|
||||
static const ssize_t READSIZE = 4096;
|
||||
|
||||
ssize_t nread = Read(p, length > READSIZE ? READSIZE : length);
|
||||
if ( nread == wxInvalidOffset )
|
||||
return false;
|
||||
|
||||
p += nread;
|
||||
if ( 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);
|
||||
|
@@ -139,4 +139,28 @@ void FileTestCase::TempFile()
|
||||
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
|
||||
|
Reference in New Issue
Block a user