putil: fix hang reading bam due to macOS libc++ bug in istream::eof()

Apparently eof() in older versions of libc++ returns true if the last character has just been read, whereas the proper behavior is only to return true when attempting to read past the end of the stream.  Since failbit is reliably set in all cases when reading past the length of the stream, we should only trust the result of eof() if fail() also returns true.
This commit is contained in:
rdb 2019-01-09 22:56:40 +01:00
parent 3e5f2cf672
commit 3ccfbfaba7

View File

@ -109,7 +109,7 @@ read_header(std::string &header, size_t num_bytes) {
nassertr(buffer != nullptr, false); nassertr(buffer != nullptr, false);
_in->read(buffer, num_bytes); _in->read(buffer, num_bytes);
if (_in->fail() || _in->eof()) { if (_in->fail()) {
return false; return false;
} }
@ -130,7 +130,7 @@ get_datagram(Datagram &data) {
// First, get the size of the upcoming datagram. // First, get the size of the upcoming datagram.
StreamReader reader(_in, false); StreamReader reader(_in, false);
uint32_t num_bytes_32 = reader.get_uint32(); uint32_t num_bytes_32 = reader.get_uint32();
if (_in->fail() || _in->eof()) { if (_in->fail()) {
return false; return false;
} }
@ -141,16 +141,29 @@ get_datagram(Datagram &data) {
return true; return true;
} }
if (_in->eof()) {
return false;
}
size_t num_bytes = (size_t)num_bytes_32; size_t num_bytes = (size_t)num_bytes_32;
if (num_bytes_32 == (uint32_t)-1) { if (num_bytes_32 == (uint32_t)-1) {
// Another special case for a value larger than 32 bits. // Another special case for a value larger than 32 bits.
uint64_t num_bytes_64 = reader.get_uint64(); uint64_t num_bytes_64 = reader.get_uint64();
if (_in->fail() || _in->eof()) { if (_in->fail()) {
_error = true; _error = true;
return false; return false;
} }
if (num_bytes_64 == 0) {
data.clear();
return false;
}
if (_in->eof()) {
return false;
}
num_bytes = (size_t)num_bytes_64; num_bytes = (size_t)num_bytes_64;
// Make sure we have a reasonable datagram size for putting into memory. // Make sure we have a reasonable datagram size for putting into memory.
@ -169,6 +182,11 @@ get_datagram(Datagram &data) {
while (bytes_read < num_bytes) { while (bytes_read < num_bytes) {
size_t bytes_left = num_bytes - bytes_read; size_t bytes_left = num_bytes - bytes_read;
if (_in->eof()) {
_error = true;
return false;
}
// Hold up a second - datagrams >4MB are pretty large by bam/network // Hold up a second - datagrams >4MB are pretty large by bam/network
// standards. Let's take it 4MB at a time just in case the length is // standards. Let's take it 4MB at a time just in case the length is
// corrupt, so we don't allocate potentially a few GBs of RAM only to // corrupt, so we don't allocate potentially a few GBs of RAM only to
@ -180,7 +198,7 @@ get_datagram(Datagram &data) {
unsigned char *ptr = &buffer.p()[bytes_read]; unsigned char *ptr = &buffer.p()[bytes_read];
_in->read((char *)ptr, (streamsize)bytes_left); _in->read((char *)ptr, (streamsize)bytes_left);
if (_in->fail() || _in->eof()) { if (_in->fail()) {
_error = true; _error = true;
return false; return false;
} }
@ -210,7 +228,7 @@ save_datagram(SubfileInfo &info) {
// First, get the size of the upcoming datagram. // First, get the size of the upcoming datagram.
StreamReader reader(_in, false); StreamReader reader(_in, false);
size_t num_bytes_32 = reader.get_uint32(); size_t num_bytes_32 = reader.get_uint32();
if (_in->fail() || _in->eof()) { if (_in->fail() || (_in->eof() && num_bytes_32 > 0)) {
return false; return false;
} }