binary reader/writer: Increase limit of length-prefixed strings

So strings with a size up to 0xFFFFFFFFFFFFFF byte can be handled.
This commit is contained in:
Martchus 2017-07-28 20:34:50 +02:00
parent f4faf652fd
commit 1d4a4bd2be
4 changed files with 28 additions and 20 deletions

View File

@ -100,7 +100,7 @@ istream::pos_type BinaryReader::readStreamsize()
*/ */
string BinaryReader::readLengthPrefixedString() string BinaryReader::readLengthPrefixedString()
{ {
static const int maxPrefixLength = 4; static constexpr int maxPrefixLength = 8;
int prefixLength = 1; int prefixLength = 1;
const byte beg = static_cast<byte>(m_stream->peek()); const byte beg = static_cast<byte>(m_stream->peek());
byte mask = 0x80; byte mask = 0x80;
@ -114,8 +114,7 @@ string BinaryReader::readLengthPrefixedString()
memset(m_buffer, 0, maxPrefixLength); memset(m_buffer, 0, maxPrefixLength);
m_stream->read(m_buffer + (maxPrefixLength - prefixLength), prefixLength); m_stream->read(m_buffer + (maxPrefixLength - prefixLength), prefixLength);
*(m_buffer + (maxPrefixLength - prefixLength)) ^= mask; *(m_buffer + (maxPrefixLength - prefixLength)) ^= mask;
uint32 prefix = BE::toUInt32(m_buffer); return readString(BE::toUInt64(m_buffer));
return readString(prefix);
} }
/*! /*!

View File

@ -77,21 +77,18 @@ void BinaryWriter::setStream(ostream *stream, bool giveOwnership)
*/ */
void BinaryWriter::writeLengthPrefixedString(const string &value) void BinaryWriter::writeLengthPrefixedString(const string &value)
{ {
size_t length = value.length(); const uint64 size = value.size();
if (length < 0x80) { uint64 boundCheck = 0x80;
m_buffer[0] = 0x80 | length; byte prefixLength = 1;
m_stream->write(m_buffer, 1); for (; boundCheck != 0x8000000000000000; boundCheck <<= 7, ++prefixLength) {
} else if (length < 0x4000) { if (size < boundCheck) {
BE::getBytes(static_cast<uint16>(0x4000 | length), m_buffer); BE::getBytes(size | boundCheck, m_buffer);
m_stream->write(m_buffer, 2); break;
} else if (length < 0x200000) { }
BE::getBytes(static_cast<uint32>(0x200000 | length), m_buffer); }
m_stream->write(m_buffer + 1, 3); if (prefixLength == 9) {
} else if (length < 0x10000000) {
BE::getBytes(static_cast<uint32>(0x10000000 | length), m_buffer);
m_stream->write(m_buffer, 4);
} else {
throw ConversionException("The size of the string exceeds the maximum."); throw ConversionException("The size of the string exceeds the maximum.");
} }
m_stream->write(value.c_str(), length); m_stream->write(m_buffer + 8 - prefixLength, prefixLength);
m_stream->write(value.data(), static_cast<streamsize>(size));
} }

Binary file not shown.

View File

@ -1,5 +1,6 @@
#include "./testutils.h" #include "./testutils.h"
#include "../conversion/conversionexception.h"
#include "../io/binaryreader.h" #include "../io/binaryreader.h"
#include "../io/binarywriter.h" #include "../io/binarywriter.h"
#include "../io/bitreader.h" #include "../io/bitreader.h"
@ -20,6 +21,7 @@ using namespace std;
using namespace IoUtilities; using namespace IoUtilities;
using namespace TestUtilities; using namespace TestUtilities;
using namespace TestUtilities::Literals; using namespace TestUtilities::Literals;
using namespace ConversionUtilities;
using namespace CPPUNIT_NS; using namespace CPPUNIT_NS;
@ -98,7 +100,7 @@ void IoTests::testBinaryReader()
testFile.exceptions(ios_base::failbit | ios_base::badbit); testFile.exceptions(ios_base::failbit | ios_base::badbit);
testFile.open(TestUtilities::testFilePath("some_data"), ios_base::in | ios_base::binary); testFile.open(TestUtilities::testFilePath("some_data"), ios_base::in | ios_base::binary);
BinaryReader reader(&testFile); BinaryReader reader(&testFile);
CPPUNIT_ASSERT_EQUAL(reader.readStreamsize(), static_cast<istream::pos_type>(95)); CPPUNIT_ASSERT_EQUAL(reader.readStreamsize(), static_cast<istream::pos_type>(398));
CPPUNIT_ASSERT(reader.readUInt16LE() == 0x0102u); CPPUNIT_ASSERT(reader.readUInt16LE() == 0x0102u);
CPPUNIT_ASSERT(reader.readUInt16BE() == 0x0102u); CPPUNIT_ASSERT(reader.readUInt16BE() == 0x0102u);
CPPUNIT_ASSERT(reader.readUInt24LE() == 0x010203u); CPPUNIT_ASSERT(reader.readUInt24LE() == 0x010203u);
@ -132,7 +134,14 @@ void IoTests::testBinaryReader()
CPPUNIT_ASSERT(reader.readBool() == true); CPPUNIT_ASSERT(reader.readBool() == true);
CPPUNIT_ASSERT(reader.readString(3) == "abc"); CPPUNIT_ASSERT(reader.readString(3) == "abc");
CPPUNIT_ASSERT(reader.readLengthPrefixedString() == "ABC"); CPPUNIT_ASSERT(reader.readLengthPrefixedString() == "ABC");
CPPUNIT_ASSERT(reader.readLengthPrefixedString() == "01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901"
"23456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123"
"45678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345"
"678901234567890123456789");
CPPUNIT_ASSERT(reader.readTerminatedString() == "def"); CPPUNIT_ASSERT(reader.readTerminatedString() == "def");
CPPUNIT_ASSERT_THROW(reader.readLengthPrefixedString(), ConversionException);
CPPUNIT_ASSERT_MESSAGE("pos in stream not advanced on conversion error", reader.readByte() == 0);
// test ownership // test ownership
reader.setStream(nullptr, true); reader.setStream(nullptr, true);
reader.setStream(new fstream(), true); reader.setStream(new fstream(), true);
@ -156,7 +165,7 @@ void IoTests::testBinaryWriter()
// prepare output stream // prepare output stream
stringstream outputStream(ios_base::in | ios_base::out | ios_base::binary); stringstream outputStream(ios_base::in | ios_base::out | ios_base::binary);
outputStream.exceptions(ios_base::failbit | ios_base::badbit); outputStream.exceptions(ios_base::failbit | ios_base::badbit);
char testData[95]; char testData[397];
outputStream.rdbuf()->pubsetbuf(testData, sizeof(testData)); outputStream.rdbuf()->pubsetbuf(testData, sizeof(testData));
// write test data // write test data
@ -205,6 +214,9 @@ void IoTests::testBinaryWriter()
writer.writeBool(true); writer.writeBool(true);
writer.writeString("abc"); writer.writeString("abc");
writer.writeLengthPrefixedString("ABC"); writer.writeLengthPrefixedString("ABC");
writer.writeLengthPrefixedString("012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901"
"234567890123456789012345678901234567890123456789012345678901234567890123456789");
writer.writeTerminatedString("def"); writer.writeTerminatedString("def");
// test written values // test written values