From 44d86f32964dcf886a63b818b7f2be68fe43ddd1 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Sat, 17 Feb 2024 22:20:53 -0600 Subject: [PATCH] Base: Add ASCIIInputStream Based on the modifications to InputStream from the LinkStage3 fork. Needed for correct restoration of StringHasher. --- src/App/StringHasher.cpp | 8 ++- src/Base/Stream.cpp | 62 +++++++++++++++++++++ src/Base/Stream.h | 116 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 185 insertions(+), 1 deletion(-) diff --git a/src/App/StringHasher.cpp b/src/App/StringHasher.cpp index f4d0f94cb4..37c1cb1626 100644 --- a/src/App/StringHasher.cpp +++ b/src/App/StringHasher.cpp @@ -621,6 +621,7 @@ void StringHasher::RestoreDocFile(Base::Reader& reader) void StringHasher::restoreStreamNew(std::istream& stream, std::size_t count) { + Base::ASCIIInputStream asciiStream (stream); _hashes->clear(); std::string content; boost::io::ios_flags_saver ifs(stream); @@ -688,7 +689,7 @@ void StringHasher::restoreStreamNew(std::istream& stream, std::size_t count) } if (!d.isPostfixed()) { - stream >> content; + asciiStream >> content; if (d.isHashed() || d.isBinary()) { d._data = QByteArray::fromBase64(content.c_str()); } @@ -815,7 +816,12 @@ void StringHasher::Restore(Base::XMLReader& reader) std::size_t count = reader.getAttributeAsUnsigned("count"); if (newTag) { + try { restoreStreamNew(reader.beginCharStream(), count); + } catch (const Base::Exception &e) { + e.ReportException(); + FC_ERR("Failed to restore string table: full-document recompute strongly recommended."); + } reader.readEndElement("StringHasher2"); return; } diff --git a/src/Base/Stream.cpp b/src/Base/Stream.cpp index 57d3efe630..a62de1a245 100644 --- a/src/Base/Stream.cpp +++ b/src/Base/Stream.cpp @@ -832,3 +832,65 @@ std::streambuf::pos_type Streambuf::seekpos(std::streambuf::pos_type pos, { return seekoff(pos, std::ios_base::beg); } + +// The custom string handler written by realthunder for the LinkStage3 toponaming code, to handle +// reading multi-line strings directly into a std::string. Imported from LinkStage3 and refactored +// during the TNP mitigation project in February 2024. +ASCIIInputStream& ASCIIInputStream::operator>>(std::string& outputString) +{ + uint32_t numberOfLines; + char inputChar; + // The number of lines is followed by a colon as the delimiter. The string itself is then + // allowed to start with any character. + _in >> numberOfLines >> inputChar; + + _ss.str(""); + for (uint32_t lineNumber = 0; lineNumber < numberOfLines && _in; ++lineNumber) { + while (true) { + if (!_in.get(inputChar)) { + break; + } + // Normalize \r\n to \n + if (inputChar == '\r') { + if (!_in.get(inputChar)) { + break; + } + if (inputChar == '\n') { + break; + } + _ss.put('\r'); + _ss.put(inputChar); + } + else { + _ss.put(inputChar); + if (inputChar == '\n') { + break; + } + } + } + } + + // Reading the last line + while (_in.get(inputChar)) { + // Normalize \r\n to \n, but DO NOT insert '\n' into the extracted + // line, because the last '\n' is inserted by us (See OutputStream + // operator>>(const char*) above) + if (inputChar == '\r') { + if (!_in.get(inputChar)) { + break; + } + if (inputChar == '\n') { + break; + } + _ss.put('\r'); + } + else if (inputChar == '\n') { + break; + } + + _ss.put(inputChar); + } + + outputString = _ss.str(); + return *this; +} diff --git a/src/Base/Stream.h b/src/Base/Stream.h index 43eea806e2..7193275fe6 100644 --- a/src/Base/Stream.h +++ b/src/Base/Stream.h @@ -30,6 +30,7 @@ #include #include +#include #include #include #include "FileInfo.h" @@ -140,6 +141,121 @@ private: std::istream& _in; }; +/** + * The ASCIIInputStream class provides reading of ASCII data from an istream, with custom handling + * for std::string to make it easier to read a single multi-line (or multi-word) string. This is + * designed for easy compatibility with the LinkStage3 implementation of the InputStream class, used + * to store StringHashers for the toponaming mitigation technique. + */ +class BaseExport ASCIIInputStream: public Stream +{ +public: + /** Constructor + * @param rin: upstream input + */ + explicit ASCIIInputStream(std::istream& rin) + : _in(rin) + {} + + ASCIIInputStream(const ASCIIInputStream&) = delete; + + ASCIIInputStream(const ASCIIInputStream&&) noexcept = delete; + + void operator=(const ASCIIInputStream&) = delete; + + void operator=(const ASCIIInputStream&&) = delete; + + ~ASCIIInputStream() override = default; + + ASCIIInputStream& operator>>(bool& input) + { + _in >> input; + return *this; + } + + ASCIIInputStream& operator>>(int8_t& ch) + { + int index {}; + _in >> index; + ch = (int8_t)index; + return *this; + } + + ASCIIInputStream& operator>>(uint8_t& uch) + { + unsigned uns {}; + _in >> uns; + uch = (uint8_t)uns; + return *this; + } + + ASCIIInputStream& operator>>(int16_t& int16) + { + _in >> int16; + return *this; + } + + ASCIIInputStream& operator>>(uint16_t& us) + { + _in >> us; + return *this; + } + + ASCIIInputStream& operator>>(int32_t& int32) + { + _in >> int32; + return *this; + } + + ASCIIInputStream& operator>>(uint32_t& ui) + { + _in >> ui; + return *this; + } + + ASCIIInputStream& operator>>(int64_t& int64) + { + _in >> int64; + return *this; + } + + ASCIIInputStream& operator>>(uint64_t& ul) + { + _in >> ul; + return *this; + } + + ASCIIInputStream& operator>>(float& flt) + { + _in >> flt; + return *this; + } + + ASCIIInputStream& operator>>(double& dbl) + { + _in >> dbl; + return *this; + } + + ASCIIInputStream& operator>>(std::string& str); + + ASCIIInputStream& operator>>(char& chr) + { + chr = (char)_in.get(); + return *this; + } + + explicit operator bool() const + { + // test if _Ipfx succeeded + return !_in.eof(); + } + +private: + std::istream& _in; + std::ostringstream _ss; +}; + // ---------------------------------------------------------------------------- /**