summaryrefslogtreecommitdiff
path: root/indra/llcommon/llstreamtools.cpp
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2022-11-22 16:21:44 -0500
committerNat Goodspeed <nat@lindenlab.com>2022-11-22 16:21:44 -0500
commitabe62c23d74d5319691a49881719b03cc9b5b090 (patch)
treed63f782540580ab5ad6f28d144c4909a420dec11 /indra/llcommon/llstreamtools.cpp
parent15b5dedb2dc139c85461e7c867164b65cc6fc628 (diff)
SL-18330: Make LLSDSerialize::deserialize() default to notation.
LLSDSerialize::serialize() emits a header string, e.g. "<? llsd/notation ?>" for notation format. Until now, LLSDSerialize::deserialize() has required that header to properly decode the input stream. But none of LLSDBinaryFormatter, LLSDXMLFormatter or LLSDNotationFormatter emit that header themselves. Nor do any of the Python llsd.format_binary(), format_xml() or format_notation() functions. Until now, you could not use LLSD::deserialize() to parse an arbitrary-format LLSD stream serialized by anything but LLSDSerialize::serialize(). Change LLSDSerialize::deserialize() so that if no header is recognized, instead of failing, it attempts to parse as notation. Add tests to exercise this case. The tricky part about this processing is that deserialize() necessarily reads some number of bytes from the input stream first, to try to recognize the header. If it fails to do so, it must prepend the bytes it has already read to the rest of the input stream since they're probably the beginning of the serialized data. To support this use case, introduce cat_streambuf, a std::streambuf subclass that (virtually) concatenates other std::streambuf instances. When read by a std::istream, the sequence of underlying std::streambufs appears to the consumer as a single continuous stream.
Diffstat (limited to 'indra/llcommon/llstreamtools.cpp')
-rw-r--r--indra/llcommon/llstreamtools.cpp26
1 files changed, 26 insertions, 0 deletions
diff --git a/indra/llcommon/llstreamtools.cpp b/indra/llcommon/llstreamtools.cpp
index d7a6f47932..1674f6edc2 100644
--- a/indra/llcommon/llstreamtools.cpp
+++ b/indra/llcommon/llstreamtools.cpp
@@ -523,3 +523,29 @@ std::istream& operator>>(std::istream& str, const char *tocheck)
}
return str;
}
+
+int cat_streambuf::underflow()
+{
+ if (gptr() == egptr())
+ {
+ // here because our buffer is empty
+ std::streamsize size;
+ // Until we've run out of mInputs, try reading the first of them
+ // into mBuffer. If that fetches some characters, break the loop.
+ while (! mInputs.empty()
+ && ! (size = mInputs.front()->sgetn(mBuffer.data(), mBuffer.size())))
+ {
+ // We tried to read mInputs.front() but got zero characters.
+ // Discard the first streambuf and try the next one.
+ mInputs.pop_front();
+ }
+ // Either we ran out of mInputs or we succeeded in reading some
+ // characters, that is, size != 0. Tell base class what we have.
+ setg(mBuffer.data(), mBuffer.data(), mBuffer.data() + size);
+ }
+ // If we fell out of the above loop with mBuffer still empty, return
+ // eof(), otherwise return the next character.
+ return (gptr() == egptr())
+ ? std::char_traits<char>::eof()
+ : std::char_traits<char>::to_int_type(*gptr());
+}