summaryrefslogtreecommitdiff
path: root/indra/llcommon/llsdserialize.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon/llsdserialize.cpp')
-rw-r--r--indra/llcommon/llsdserialize.cpp1621
1 files changed, 1621 insertions, 0 deletions
diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp
new file mode 100644
index 0000000000..fb8efc91ca
--- /dev/null
+++ b/indra/llcommon/llsdserialize.cpp
@@ -0,0 +1,1621 @@
+/**
+ * @file llsdserialize.cpp
+ * @author Phoenix
+ * @date 2006-03-05
+ * @brief Implementation of LLSD parsers and formatters
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llsdserialize.h"
+#include "llmemory.h"
+#include "llstreamtools.h" // for fullread
+
+#include <iostream>
+#include "apr-1/apr_base64.h"
+
+#if !LL_WINDOWS
+#include <netinet/in.h> // htonl & ntohl
+#endif
+
+#include "lldate.h"
+#include "llsd.h"
+#include "lluri.h"
+
+// File constants
+static const int MAX_HDR_LEN = 20;
+static const char LEGACY_NON_HEADER[] = "<llsd>";
+
+//static
+const char* LLSDSerialize::LLSDBinaryHeader = "LLSD/Binary";
+
+//static
+const char* LLSDSerialize::LLSDXMLHeader = "LLSD/XML";
+
+// virtual
+LLSDParser::~LLSDParser()
+{ }
+
+// virtual
+LLSDNotationParser::~LLSDNotationParser()
+{ }
+
+
+// static
+void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize type, U32 options)
+{
+ LLPointer<LLSDFormatter> f = NULL;
+
+ switch (type)
+ {
+ case LLSD_BINARY:
+ str << "<? " << LLSDBinaryHeader << " ?>\n";
+ f = new LLSDBinaryFormatter;
+ break;
+
+ case LLSD_XML:
+ str << "<? " << LLSDXMLHeader << " ?>\n";
+ f = new LLSDXMLFormatter;
+ break;
+
+ default:
+ llwarns << "serialize request for unkown ELLSD_Serialize" << llendl;
+ }
+
+ if (f.notNull())
+ {
+ f->format(sd, str, options);
+ }
+}
+
+// static
+bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str)
+{
+ LLPointer<LLSDParser> p = NULL;
+ char hdr_buf[MAX_HDR_LEN + 1] = ""; /* Flawfinder: ignore */
+ int i;
+ int inbuf = 0;
+ bool legacy_no_header = false;
+ bool fail_if_not_legacy = false;
+ std::string header = "";
+
+ /*
+ * Get the first line before anything.
+ */
+ str.get(hdr_buf, MAX_HDR_LEN, '\n');
+ if (str.fail())
+ {
+ str.clear();
+ fail_if_not_legacy = true;
+ }
+
+ if (!strncasecmp(LEGACY_NON_HEADER, hdr_buf, strlen(LEGACY_NON_HEADER))) /* Flawfinder: ignore */
+ {
+ legacy_no_header = true;
+ inbuf = str.gcount();
+ }
+ else
+ {
+ if (fail_if_not_legacy)
+ goto fail;
+ /*
+ * Remove the newline chars
+ */
+ for (i = 0; i < MAX_HDR_LEN; i++)
+ {
+ if (hdr_buf[i] == 0 || hdr_buf[i] == '\r' ||
+ hdr_buf[i] == '\n')
+ {
+ hdr_buf[i] = 0;
+ break;
+ }
+ }
+ header = hdr_buf;
+
+ std::string::size_type start = std::string::npos;
+ std::string::size_type end = std::string::npos;
+ start = header.find_first_not_of("<? ");
+ if (start != std::string::npos)
+ {
+ end = header.find_first_of(" ?", start);
+ }
+ if ((start == std::string::npos) || (end == std::string::npos))
+ goto fail;
+
+ header = header.substr(start, end - start);
+ ws(str);
+ }
+ /*
+ * Create the parser as appropriate
+ */
+ if (legacy_no_header)
+ {
+ LLSDXMLParser *x = new LLSDXMLParser;
+ x->parsePart(hdr_buf, inbuf);
+ p = x;
+ }
+ else if (header == LLSDBinaryHeader)
+ {
+ p = new LLSDBinaryParser;
+ }
+ else if (header == LLSDXMLHeader)
+ {
+ p = new LLSDXMLParser;
+ }
+ else
+ {
+ llwarns << "deserialize request for unknown ELLSD_Serialize" << llendl;
+ }
+
+ if (p.notNull())
+ {
+ p->parse(str, sd);
+ return true;
+ }
+
+fail:
+ llwarns << "deserialize LLSD parse failure" << llendl;
+ return false;
+}
+
+/**
+ * Endian handlers
+ */
+#if LL_BIG_ENDIAN
+U64 ll_htonll(U64 hostlonglong) { return hostlonglong; }
+U64 ll_ntohll(U64 netlonglong) { return netlonglong; }
+F64 ll_htond(F64 hostlonglong) { return hostlonglong; }
+F64 ll_ntohd(F64 netlonglong) { return netlonglong; }
+#else
+// I read some comments one a indicating that doing an integer add
+// here would be faster than a bitwise or. For now, the or has
+// programmer clarity, since the intended outcome matches the
+// operation.
+U64 ll_htonll(U64 hostlonglong)
+{
+ return ((U64)(htonl((U32)((hostlonglong >> 32) & 0xFFFFFFFF))) |
+ ((U64)(htonl((U32)(hostlonglong & 0xFFFFFFFF))) << 32));
+}
+U64 ll_ntohll(U64 netlonglong)
+{
+ return ((U64)(ntohl((U32)((netlonglong >> 32) & 0xFFFFFFFF))) |
+ ((U64)(ntohl((U32)(netlonglong & 0xFFFFFFFF))) << 32));
+}
+union LLEndianSwapper
+{
+ F64 d;
+ U64 i;
+};
+F64 ll_htond(F64 hostdouble)
+{
+ LLEndianSwapper tmp;
+ tmp.d = hostdouble;
+ tmp.i = ll_htonll(tmp.i);
+ return tmp.d;
+}
+F64 ll_ntohd(F64 netdouble)
+{
+ LLEndianSwapper tmp;
+ tmp.d = netdouble;
+ tmp.i = ll_ntohll(tmp.i);
+ return tmp.d;
+}
+#endif
+
+/**
+ * Local functions.
+ */
+bool deserialize_string(std::istream& str, std::string& value);
+bool deserialize_string_delim(std::istream& str, std::string& value, char d);
+bool deserialize_string_raw(std::istream& str, std::string& value);
+void serialize_string(const std::string& value, std::ostream& str);
+
+/**
+ * Local constants.
+ */
+static const std::string NOTATION_TRUE_SERIAL("true");
+static const std::string NOTATION_FALSE_SERIAL("false");
+
+static const char BINARY_TRUE_SERIAL = '1';
+static const char BINARY_FALSE_SERIAL = '0';
+
+static const S32 NOTATION_PARSE_FAILURE = -1;
+
+/**
+ * LLSDParser
+ */
+LLSDParser::LLSDParser()
+{
+}
+
+/**
+ * LLSDNotationParser
+ */
+// virtual
+S32 LLSDNotationParser::parse(std::istream& istr, LLSD& data) const
+{
+ // map: { string:object, string:object }
+ // array: [ object, object, object ]
+ // undef: !
+ // boolean: true | false | 1 | 0 | T | F | t | f | TRUE | FALSE
+ // integer: i####
+ // real: r####
+ // uuid: u####
+ // string: "g'day" | 'have a "nice" day' | s(size)"raw data"
+ // uri: l"escaped"
+ // date: d"YYYY-MM-DDTHH:MM:SS.FFZ"
+ // binary: b##"ff3120ab1" | b(size)"raw data"
+ char c;
+ c = istr.peek();
+ while(isspace(c))
+ {
+ // pop the whitespace.
+ c = istr.get();
+ c = istr.peek();
+ continue;
+ }
+ if(!istr.good())
+ {
+ return 0;
+ }
+ S32 parse_count = 1;
+ switch(c)
+ {
+ case '{':
+ parse_count += parseMap(istr, data);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading map." << llendl;
+ }
+ if(data.isUndefined())
+ {
+ parse_count = NOTATION_PARSE_FAILURE;
+ }
+ break;
+
+ case '[':
+ parse_count += parseArray(istr, data);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading array." << llendl;
+ }
+ if(data.isUndefined())
+ {
+ parse_count = NOTATION_PARSE_FAILURE;
+ }
+ break;
+
+ case '!':
+ c = istr.get();
+ data.clear();
+ break;
+
+ case '0':
+ c = istr.get();
+ data = false;
+ break;
+
+ case 'F':
+ case 'f':
+ do
+ {
+ istr.ignore();
+ c = istr.peek();
+ } while (isalpha(c));
+ data = false;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading boolean." << llendl;
+ }
+ break;
+
+ case '1':
+ c = istr.get();
+ data = true;
+ break;
+
+ case 'T':
+ case 't':
+ do
+ {
+ istr.ignore();
+ c = istr.peek();
+ } while (isalpha(c));
+ data = true;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading boolean." << llendl;
+ }
+ break;
+
+ case 'i':
+ {
+ c = istr.get();
+ S32 integer = 0;
+ istr >> integer;
+ data = integer;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading integer." << llendl;
+ }
+ break;
+ }
+
+ case 'r':
+ {
+ c = istr.get();
+ F64 real = 0.0;
+ istr >> real;
+ data = real;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading real." << llendl;
+ }
+ break;
+ }
+
+ case 'u':
+ {
+ c = istr.get();
+ LLUUID id;
+ istr >> id;
+ data = id;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading uuid." << llendl;
+ }
+ break;
+ }
+
+ case '\"':
+ case '\'':
+ case 's':
+ parseString(istr, data);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading string." << llendl;
+ }
+ if(data.isUndefined())
+ {
+ parse_count = NOTATION_PARSE_FAILURE;
+ }
+ break;
+
+ case 'l':
+ {
+ c = istr.get(); // pop the 'l'
+ c = istr.get(); // pop the delimiter
+ std::string str;
+ deserialize_string_delim(istr, str, c);
+ data = LLURI(str);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading link." << llendl;
+ }
+ break;
+ }
+
+ case 'd':
+ {
+ c = istr.get(); // pop the 'd'
+ c = istr.get(); // pop the delimiter
+ std::string str;
+ deserialize_string_delim(istr, str, c);
+ data = LLDate(str);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading date." << llendl;
+ }
+ break;
+ }
+
+ case 'b':
+ parseBinary(istr, data);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading data." << llendl;
+ }
+ if(data.isUndefined())
+ {
+ parse_count = NOTATION_PARSE_FAILURE;
+ }
+ break;
+
+ default:
+ data.clear();
+ parse_count = NOTATION_PARSE_FAILURE;
+ llinfos << "Unrecognized character while parsing: int(" << (int)c
+ << ")" << llendl;
+ break;
+ }
+ return parse_count;
+}
+
+// static
+LLSD LLSDNotationParser::parse(std::istream& istr)
+{
+ LLSDNotationParser parser;
+ LLSD rv;
+ S32 count = parser.parse(istr, rv);
+ lldebugs << "LLSDNotationParser::parse parsed " << count << " objects."
+ << llendl;
+ return rv;
+}
+
+S32 LLSDNotationParser::parseMap(std::istream& istr, LLSD& map) const
+{
+ // map: { string:object, string:object }
+ map = LLSD::emptyMap();
+ S32 parse_count = 0;
+ char c = istr.get();
+ if(c == '{')
+ {
+ // eat commas, white
+ bool found_name = false;
+ std::string name;
+ c = istr.get();
+ while(c != '}' && istr.good())
+ {
+ if(!found_name)
+ {
+ if((c == '\"') || (c == '\'') || (c == 's'))
+ {
+ istr.putback(c);
+ found_name = true;
+ deserialize_string(istr, name);
+ }
+ c = istr.get();
+ }
+ else
+ {
+ if(isspace(c) || (c == ':'))
+ {
+ c = istr.get();
+ continue;
+ }
+ istr.putback(c);
+ LLSD child;
+ S32 count = parse(istr, child);
+ if(count > 0)
+ {
+ parse_count += count;
+ map.insert(name, child);
+ }
+ else
+ {
+ map.clear();
+ return NOTATION_PARSE_FAILURE;
+ }
+ found_name = false;
+ c = istr.get();
+ }
+ }
+ }
+ return parse_count;
+}
+
+S32 LLSDNotationParser::parseArray(std::istream& istr, LLSD& array) const
+{
+ // array: [ object, object, object ]
+ array = LLSD::emptyArray();
+ S32 parse_count = 0;
+ char c = istr.get();
+ if(c == '[')
+ {
+ // eat commas, white
+ c = istr.get();
+ while((c != ']') && istr.good())
+ {
+ LLSD child;
+ if(isspace(c) || (c == ','))
+ {
+ c = istr.get();
+ continue;
+ }
+ istr.putback(c);
+ S32 count = parse(istr, child);
+ if(count > 0)
+ {
+ parse_count += count;
+ array.append(child);
+ }
+ else
+ {
+ array.clear();
+ return NOTATION_PARSE_FAILURE;
+ }
+ c = istr.get();
+ }
+ }
+ return parse_count;
+}
+
+void LLSDNotationParser::parseString(std::istream& istr, LLSD& data) const
+{
+ std::string value;
+ if(deserialize_string(istr, value))
+ {
+ data = value;
+ }
+ else
+ {
+ // failed to parse.
+ data.clear();
+ }
+}
+
+void LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const
+{
+ // binary: b##"ff3120ab1"
+ // or: b(len)"..."
+
+ // I want to manually control those values here to make sure the
+ // parser doesn't break when someone changes a constant somewhere
+ // else.
+ const U32 BINARY_BUFFER_SIZE = 256;
+ const U32 STREAM_GET_COUNT = 255;
+
+ // need to read the base out.
+ char buf[BINARY_BUFFER_SIZE]; /* Flawfinder: ignore */
+ istr.get(buf, STREAM_GET_COUNT, '"');
+ char c = istr.get();
+ if((c == '"') && (0 == strncmp("b(", buf, 2)))
+ {
+ // We probably have a valid raw binary stream. determine
+ // the size, and read it.
+ // *FIX: Should we set a maximum size?
+ S32 len = strtol(buf + 2, NULL, 0);
+ std::vector<U8> value;
+ if(len)
+ {
+ value.resize(len);
+ fullread(istr, (char *)&value[0], len);
+ }
+ c = istr.get(); // strip off the trailing double-quote
+ data = value;
+ }
+ else if((c == '"') && (0 == strncmp("b64", buf, 3)))
+ {
+ // *FIX: A bit inefficient, but works for now. To make the
+ // format better, I would need to add a hint into the
+ // serialization format that indicated how long it was.
+ std::stringstream coded_stream;
+ istr.get(*(coded_stream.rdbuf()), '\"');
+ c = istr.get();
+ std::string encoded(coded_stream.str());
+ S32 len = apr_base64_decode_len(encoded.c_str());
+ std::vector<U8> value;
+ value.resize(len);
+ len = apr_base64_decode_binary(&value[0], encoded.c_str());
+ value.resize(len);
+ data = value;
+ }
+ else if((c == '"') && (0 == strncmp("b16", buf, 3)))
+ {
+ // yay, base 16. We pop the next character which is either a
+ // double quote or base 16 data. If it's a double quote, we're
+ // done parsing. If it's not, put the data back, and read the
+ // stream until the next double quote.
+ char* read; /*Flawfinder: ignore*/
+ U8 byte;
+ U8 byte_buffer[BINARY_BUFFER_SIZE];
+ U8* write;
+ std::vector<U8> value;
+ c = istr.get();
+ while(c != '"')
+ {
+ istr.putback(c);
+ read = buf;
+ write = byte_buffer;
+ istr.get(buf, STREAM_GET_COUNT, '"');
+ c = istr.get();
+ while(*read != '\0') /*Flawfinder: ignore*/
+ {
+ byte = hex_as_nybble(*read++);
+ byte = byte << 4;
+ byte |= hex_as_nybble(*read++);
+ *write++ = byte;
+ }
+ // copy the data out of the byte buffer
+ value.insert(value.end(), byte_buffer, write);
+ }
+ data = value;
+ }
+ else
+ {
+ data.clear();
+ }
+}
+
+
+/**
+ * LLSDBinaryParser
+ */
+LLSDBinaryParser::LLSDBinaryParser()
+{
+}
+
+// virtual
+LLSDBinaryParser::~LLSDBinaryParser()
+{
+}
+
+// virtual
+S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const
+{
+/**
+ * Undefined: '!'<br>
+ * Boolean: 't' for true 'f' for false<br>
+ * Integer: 'i' + 4 bytes network byte order<br>
+ * Real: 'r' + 8 bytes IEEE double<br>
+ * UUID: 'u' + 16 byte unsigned integer<br>
+ * String: 's' + 4 byte integer size + string<br>
+ * strings also secretly support the notation format
+ * Date: 'd' + 8 byte IEEE double for seconds since epoch<br>
+ * URI: 'l' + 4 byte integer size + string uri<br>
+ * Binary: 'b' + 4 byte integer size + binary data<br>
+ * Array: '[' + 4 byte integer size + all values + ']'<br>
+ * Map: '{' + 4 byte integer size every(key + value) + '}'<br>
+ * map keys are serialized as s + 4 byte integer size + string or in the
+ * notation format.
+ */
+ char c;
+ c = istr.get();
+ if(!istr.good())
+ {
+ return 0;
+ }
+ S32 parse_count = 1;
+ switch(c)
+ {
+ case '{':
+ parse_count += parseMap(istr, data);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary map." << llendl;
+ }
+ break;
+
+ case '[':
+ parse_count += parseArray(istr, data);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary array." << llendl;
+ }
+ break;
+
+ case '!':
+ data.clear();
+ break;
+
+ case '0':
+ data = false;
+ break;
+
+ case '1':
+ data = true;
+ break;
+
+ case 'i':
+ {
+ U32 value_nbo = 0;
+ istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ data = (S32)ntohl(value_nbo);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary integer." << llendl;
+ }
+ break;
+ }
+
+ case 'r':
+ {
+ F64 real_nbo = 0.0;
+ istr.read((char*)&real_nbo, sizeof(F64)); /*Flawfinder: ignore*/
+ data = ll_ntohd(real_nbo);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary real." << llendl;
+ }
+ break;
+ }
+
+ case 'u':
+ {
+ LLUUID id;
+ istr.read((char*)(&id.mData), UUID_BYTES); /*Flawfinder: ignore*/
+ data = id;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary uuid." << llendl;
+ }
+ break;
+ }
+
+ case '\'':
+ case '"':
+ {
+ std::string value;
+ deserialize_string_delim(istr, value, c);
+ data = value;
+ break;
+ }
+
+ case 's':
+ {
+ std::string value;
+ parseString(istr, value);
+ data = value;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary string." << llendl;
+ }
+ break;
+ }
+
+ case 'l':
+ {
+ std::string value;
+ parseString(istr, value);
+ data = LLURI(value);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary link." << llendl;
+ }
+ break;
+ }
+
+ case 'd':
+ {
+ F64 real = 0.0;
+ istr.read((char*)&real, sizeof(F64)); /*Flawfinder: ignore*/
+ data = LLDate(real);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary date." << llendl;
+ }
+ break;
+ }
+
+ case 'b':
+ {
+ // We probably have a valid raw binary stream. determine
+ // the size, and read it.
+ // *FIX: Should we set a maximum size?
+ U32 size_nbo = 0;
+ istr.read((char*)&size_nbo, sizeof(U32));
+ S32 size = (S32)ntohl(size_nbo);
+ std::vector<U8> value;
+ if(size)
+ {
+ value.resize(size);
+ istr.read((char*)&value[0], size); /*Flawfinder: ignore*/
+ }
+ data = value;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary." << llendl;
+ }
+ break;
+ }
+
+ default:
+ --parse_count;
+ llinfos << "Unrecognized character while parsing: int(" << (int)c
+ << ")" << llendl;
+ break;
+ }
+ return parse_count;
+}
+
+// static
+LLSD LLSDBinaryParser::parse(std::istream& istr)
+{
+ LLSDBinaryParser parser;
+ LLSD rv;
+ S32 count = parser.parse(istr, rv);
+ lldebugs << "LLSDBinaryParser::parse parsed " << count << " objects."
+ << llendl;
+ return rv;
+}
+
+S32 LLSDBinaryParser::parseMap(std::istream& istr, LLSD& map) const
+{
+ map = LLSD::emptyMap();
+ U32 value_nbo = 0;
+ istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ S32 size = (S32)ntohl(value_nbo);
+ S32 parse_count = 0;
+ S32 count = 0;
+ char c = istr.get();
+ while(c != '}' && (count < size) && istr.good())
+ {
+ std::string name;
+ switch(c)
+ {
+ case 'k':
+ parseString(istr, name);
+ break;
+ case '\'':
+ case '"':
+ deserialize_string_delim(istr, name, c);
+ break;
+ }
+ LLSD child;
+ S32 child_count = parse(istr, child);
+ if(child_count)
+ {
+ parse_count += child_count;
+ map.insert(name, child);
+ }
+ ++count;
+ c = istr.get();
+ }
+ return parse_count;
+}
+
+S32 LLSDBinaryParser::parseArray(std::istream& istr, LLSD& array) const
+{
+ array = LLSD::emptyArray();
+ U32 value_nbo = 0;
+ istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ S32 size = (S32)ntohl(value_nbo);
+
+ // *FIX: This would be a good place to reserve some space in the
+ // array...
+
+ S32 parse_count = 0;
+ S32 count = 0;
+ char c = istr.peek();
+ while((c != ']') && (count < size) && istr.good())
+ {
+ LLSD child;
+ S32 child_count = parse(istr, child);
+ if(child_count)
+ {
+ parse_count += child_count;
+ array.append(child);
+ }
+ ++count;
+ c = istr.peek();
+ }
+ c = istr.get();
+ return parse_count;
+}
+
+void LLSDBinaryParser::parseString(
+ std::istream& istr,
+ std::string& value) const
+{
+ // *FIX: This is memory inefficient.
+ U32 value_nbo = 0;
+ istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ S32 size = (S32)ntohl(value_nbo);
+ std::vector<char> buf;
+ buf.resize(size);
+ istr.read(&buf[0], size); /*Flawfinder: ignore*/
+ value.assign(buf.begin(), buf.end());
+}
+
+
+/**
+ * LLSDFormatter
+ */
+LLSDFormatter::LLSDFormatter() :
+ mBoolAlpha(false)
+{
+}
+
+// virtual
+LLSDFormatter::~LLSDFormatter()
+{ }
+
+void LLSDFormatter::boolalpha(bool alpha)
+{
+ mBoolAlpha = alpha;
+}
+
+void LLSDFormatter::realFormat(const std::string& format)
+{
+ mRealFormat = format;
+}
+
+void LLSDFormatter::formatReal(LLSD::Real real, std::ostream& ostr) const
+{
+ char buffer[MAX_STRING]; /* Flawfinder: ignore */
+ snprintf(buffer, MAX_STRING, mRealFormat.c_str(), real);
+ ostr << buffer;
+}
+
+/**
+ * LLSDNotationFormatter
+ */
+LLSDNotationFormatter::LLSDNotationFormatter()
+{
+}
+
+// virtual
+LLSDNotationFormatter::~LLSDNotationFormatter()
+{ }
+
+// static
+std::string LLSDNotationFormatter::escapeString(const std::string& in)
+{
+ std::ostringstream ostr;
+ serialize_string(in, ostr);
+ return ostr.str();
+}
+
+// virtual
+S32 LLSDNotationFormatter::format(const LLSD& data, std::ostream& ostr, U32 options) const
+{
+ S32 format_count = 1;
+ switch(data.type())
+ {
+ case LLSD::TypeMap:
+ {
+ ostr << "{";
+ bool need_comma = false;
+ LLSD::map_const_iterator iter = data.beginMap();
+ LLSD::map_const_iterator end = data.endMap();
+ for(; iter != end; ++iter)
+ {
+ if(need_comma) ostr << ",";
+ need_comma = true;
+ ostr << '\'';
+ serialize_string((*iter).first, ostr);
+ ostr << "':";
+ format_count += format((*iter).second, ostr);
+ }
+ ostr << "}";
+ break;
+ }
+
+ case LLSD::TypeArray:
+ {
+ ostr << "[";
+ bool need_comma = false;
+ LLSD::array_const_iterator iter = data.beginArray();
+ LLSD::array_const_iterator end = data.endArray();
+ for(; iter != end; ++iter)
+ {
+ if(need_comma) ostr << ",";
+ need_comma = true;
+ format_count += format(*iter, ostr);
+ }
+ ostr << "]";
+ break;
+ }
+
+ case LLSD::TypeUndefined:
+ ostr << "!";
+ break;
+
+ case LLSD::TypeBoolean:
+ if(mBoolAlpha ||
+#if( LL_WINDOWS || __GNUC__ > 2)
+ (ostr.flags() & std::ios::boolalpha)
+#else
+ (ostr.flags() & 0x0100)
+#endif
+ )
+ {
+ ostr << (data.asBoolean()
+ ? NOTATION_TRUE_SERIAL : NOTATION_FALSE_SERIAL);
+ }
+ else
+ {
+ ostr << (data.asBoolean() ? 1 : 0);
+ }
+ break;
+
+ case LLSD::TypeInteger:
+ ostr << "i" << data.asInteger();
+ break;
+
+ case LLSD::TypeReal:
+ ostr << "r";
+ if(mRealFormat.empty())
+ {
+ ostr << data.asReal();
+ }
+ else
+ {
+ formatReal(data.asReal(), ostr);
+ }
+ break;
+
+ case LLSD::TypeUUID:
+ ostr << "u" << data.asUUID();
+ break;
+
+ case LLSD::TypeString:
+ ostr << '\'';
+ serialize_string(data.asString(), ostr);
+ ostr << '\'';
+ break;
+
+ case LLSD::TypeDate:
+ ostr << "d\"" << data.asDate() << "\"";
+ break;
+
+ case LLSD::TypeURI:
+ ostr << "l\"";
+ serialize_string(data.asString(), ostr);
+ ostr << "\"";
+ break;
+
+ case LLSD::TypeBinary:
+ {
+ // *FIX: memory inefficient.
+ std::vector<U8> buffer = data.asBinary();
+ ostr << "b(" << buffer.size() << ")\"";
+ if(buffer.size()) ostr.write((const char*)&buffer[0], buffer.size());
+ ostr << "\"";
+ break;
+ }
+
+ default:
+ // *NOTE: This should never happen.
+ ostr << "!";
+ break;
+ }
+ return format_count;
+}
+
+
+/**
+ * LLSDBinaryFormatter
+ */
+LLSDBinaryFormatter::LLSDBinaryFormatter()
+{
+}
+
+// virtual
+LLSDBinaryFormatter::~LLSDBinaryFormatter()
+{ }
+
+// virtual
+S32 LLSDBinaryFormatter::format(const LLSD& data, std::ostream& ostr, U32 options) const
+{
+ S32 format_count = 1;
+ switch(data.type())
+ {
+ case LLSD::TypeMap:
+ {
+ ostr.put('{');
+ U32 size_nbo = htonl(data.size());
+ ostr.write((const char*)(&size_nbo), sizeof(U32));
+ LLSD::map_const_iterator iter = data.beginMap();
+ LLSD::map_const_iterator end = data.endMap();
+ for(; iter != end; ++iter)
+ {
+ ostr.put('k');
+ formatString((*iter).first, ostr);
+ format_count += format((*iter).second, ostr);
+ }
+ ostr.put('}');
+ break;
+ }
+
+ case LLSD::TypeArray:
+ {
+ ostr.put('[');
+ U32 size_nbo = htonl(data.size());
+ ostr.write((const char*)(&size_nbo), sizeof(U32));
+ LLSD::array_const_iterator iter = data.beginArray();
+ LLSD::array_const_iterator end = data.endArray();
+ for(; iter != end; ++iter)
+ {
+ format_count += format(*iter, ostr);
+ }
+ ostr.put(']');
+ break;
+ }
+
+ case LLSD::TypeUndefined:
+ ostr.put('!');
+ break;
+
+ case LLSD::TypeBoolean:
+ if(data.asBoolean()) ostr.put(BINARY_TRUE_SERIAL);
+ else ostr.put(BINARY_FALSE_SERIAL);
+ break;
+
+ case LLSD::TypeInteger:
+ {
+ ostr.put('i');
+ U32 value_nbo = htonl(data.asInteger());
+ ostr.write((const char*)(&value_nbo), sizeof(U32));
+ break;
+ }
+
+ case LLSD::TypeReal:
+ {
+ ostr.put('r');
+ F64 value_nbo = ll_htond(data.asReal());
+ ostr.write((const char*)(&value_nbo), sizeof(F64));
+ break;
+ }
+
+ case LLSD::TypeUUID:
+ ostr.put('u');
+ ostr.write((const char*)(&(data.asUUID().mData)), UUID_BYTES);
+ break;
+
+ case LLSD::TypeString:
+ ostr.put('s');
+ formatString(data.asString(), ostr);
+ break;
+
+ case LLSD::TypeDate:
+ {
+ ostr.put('d');
+ F64 value = data.asReal();
+ ostr.write((const char*)(&value), sizeof(F64));
+ break;
+ }
+
+ case LLSD::TypeURI:
+ ostr.put('l');
+ formatString(data.asString(), ostr);
+ break;
+
+ case LLSD::TypeBinary:
+ {
+ // *FIX: memory inefficient.
+ ostr.put('b');
+ std::vector<U8> buffer = data.asBinary();
+ U32 size_nbo = htonl(buffer.size());
+ ostr.write((const char*)(&size_nbo), sizeof(U32));
+ if(buffer.size()) ostr.write((const char*)&buffer[0], buffer.size());
+ break;
+ }
+
+ default:
+ // *NOTE: This should never happen.
+ ostr.put('!');
+ break;
+ }
+ return format_count;
+}
+
+void LLSDBinaryFormatter::formatString(
+ const std::string& string,
+ std::ostream& ostr) const
+{
+ U32 size_nbo = htonl(string.size());
+ ostr.write((const char*)(&size_nbo), sizeof(U32));
+ ostr.write(string.c_str(), string.size());
+}
+
+/**
+ * local functions
+ */
+bool deserialize_string(std::istream& str, std::string& value)
+{
+ char c = str.get();
+ if (str.fail())
+ {
+ // No data in stream, bail out
+ return false;
+ }
+
+ bool rv = false;
+ switch(c)
+ {
+ case '\'':
+ case '"':
+ rv = deserialize_string_delim(str, value, c);
+ break;
+ case 's':
+ rv = deserialize_string_raw(str, value);
+ break;
+ default:
+ break;
+ }
+ return rv;
+}
+
+bool deserialize_string_delim(
+ std::istream& str,
+ std::string& value,
+ char delim)
+{
+ std::ostringstream write_buffer;
+ bool found_escape = false;
+ bool found_hex = false;
+ bool found_digit = false;
+ U8 byte = 0;
+
+ while (true)
+ {
+ char next_char = str.get();
+
+ if(str.fail())
+ {
+ // If our stream is empty, break out
+ value = write_buffer.str();
+ return false;
+ }
+
+ if(found_escape)
+ {
+ // next character(s) is a special sequence.
+ if(found_hex)
+ {
+ if(found_digit)
+ {
+ found_digit = false;
+ found_hex = false;
+ found_escape = false;
+ byte = byte << 4;
+ byte |= hex_as_nybble(next_char);
+ write_buffer << byte;
+ byte = 0;
+ }
+ else
+ {
+ // next character is the first nybble of
+ //
+ found_digit = true;
+ byte = hex_as_nybble(next_char);
+ }
+ }
+ else if(next_char == 'x')
+ {
+ found_hex = true;
+ }
+ else
+ {
+ switch(next_char)
+ {
+ case 'a':
+ write_buffer << '\a';
+ break;
+ case 'b':
+ write_buffer << '\b';
+ break;
+ case 'f':
+ write_buffer << '\f';
+ break;
+ case 'n':
+ write_buffer << '\n';
+ break;
+ case 'r':
+ write_buffer << '\r';
+ break;
+ case 't':
+ write_buffer << '\t';
+ break;
+ case 'v':
+ write_buffer << '\v';
+ break;
+ default:
+ write_buffer << next_char;
+ break;
+ }
+ found_escape = false;
+ }
+ }
+ else if(next_char == '\\')
+ {
+ found_escape = true;
+ }
+ else if(next_char == delim)
+ {
+ break;
+ }
+ else
+ {
+ write_buffer << next_char;
+ }
+ }
+
+ value = write_buffer.str();
+ return true;
+}
+
+bool deserialize_string_raw(std::istream& str, std::string& value)
+{
+ bool ok = false;
+ const S32 BUF_LEN = 20;
+ char buf[BUF_LEN]; /* Flawfinder: ignore */
+ str.get(buf, BUF_LEN - 1, ')');
+ char c = str.get();
+ c = str.get();
+ if(((c == '"') || (c == '\'')) && (buf[0] == '('))
+ {
+ // We probably have a valid raw string. determine
+ // the size, and read it.
+ // *FIX: Should we set a maximum size?
+ // *FIX: This is memory inefficient.
+ S32 len = strtol(buf + 1, NULL, 0);
+ std::vector<char> buf;
+ buf.resize(len);
+ str.read(&buf[0], len); /*Flawfinder: ignore*/
+ value.assign(buf.begin(), buf.end());
+ c = str.get();
+ if((c == '"') || (c == '\''))
+ {
+ ok = true;
+ }
+ }
+ return ok;
+}
+
+static const char* NOTATION_STRING_CHARACTERS[256] =
+{
+ "\\x00", // 0
+ "\\x01", // 1
+ "\\x02", // 2
+ "\\x03", // 3
+ "\\x04", // 4
+ "\\x05", // 5
+ "\\x06", // 6
+ "\\a", // 7
+ "\\b", // 8
+ "\\t", // 9
+ "\\n", // 10
+ "\\v", // 11
+ "\\f", // 12
+ "\\r", // 13
+ "\\x0e", // 14
+ "\\x0f", // 15
+ "\\x10", // 16
+ "\\x11", // 17
+ "\\x12", // 18
+ "\\x13", // 19
+ "\\x14", // 20
+ "\\x15", // 21
+ "\\x16", // 22
+ "\\x17", // 23
+ "\\x18", // 24
+ "\\x19", // 25
+ "\\x1a", // 26
+ "\\x1b", // 27
+ "\\x1c", // 28
+ "\\x1d", // 29
+ "\\x1e", // 30
+ "\\x1f", // 31
+ " ", // 32
+ "!", // 33
+ "\"", // 34
+ "#", // 35
+ "$", // 36
+ "%", // 37
+ "&", // 38
+ "\\'", // 39
+ "(", // 40
+ ")", // 41
+ "*", // 42
+ "+", // 43
+ ",", // 44
+ "-", // 45
+ ".", // 46
+ "/", // 47
+ "0", // 48
+ "1", // 49
+ "2", // 50
+ "3", // 51
+ "4", // 52
+ "5", // 53
+ "6", // 54
+ "7", // 55
+ "8", // 56
+ "9", // 57
+ ":", // 58
+ ";", // 59
+ "<", // 60
+ "=", // 61
+ ">", // 62
+ "?", // 63
+ "@", // 64
+ "A", // 65
+ "B", // 66
+ "C", // 67
+ "D", // 68
+ "E", // 69
+ "F", // 70
+ "G", // 71
+ "H", // 72
+ "I", // 73
+ "J", // 74
+ "K", // 75
+ "L", // 76
+ "M", // 77
+ "N", // 78
+ "O", // 79
+ "P", // 80
+ "Q", // 81
+ "R", // 82
+ "S", // 83
+ "T", // 84
+ "U", // 85
+ "V", // 86
+ "W", // 87
+ "X", // 88
+ "Y", // 89
+ "Z", // 90
+ "[", // 91
+ "\\\\", // 92
+ "]", // 93
+ "^", // 94
+ "_", // 95
+ "`", // 96
+ "a", // 97
+ "b", // 98
+ "c", // 99
+ "d", // 100
+ "e", // 101
+ "f", // 102
+ "g", // 103
+ "h", // 104
+ "i", // 105
+ "j", // 106
+ "k", // 107
+ "l", // 108
+ "m", // 109
+ "n", // 110
+ "o", // 111
+ "p", // 112
+ "q", // 113
+ "r", // 114
+ "s", // 115
+ "t", // 116
+ "u", // 117
+ "v", // 118
+ "w", // 119
+ "x", // 120
+ "y", // 121
+ "z", // 122
+ "{", // 123
+ "|", // 124
+ "}", // 125
+ "~", // 126
+ "\\x7f", // 127
+ "\\x80", // 128
+ "\\x81", // 129
+ "\\x82", // 130
+ "\\x83", // 131
+ "\\x84", // 132
+ "\\x85", // 133
+ "\\x86", // 134
+ "\\x87", // 135
+ "\\x88", // 136
+ "\\x89", // 137
+ "\\x8a", // 138
+ "\\x8b", // 139
+ "\\x8c", // 140
+ "\\x8d", // 141
+ "\\x8e", // 142
+ "\\x8f", // 143
+ "\\x90", // 144
+ "\\x91", // 145
+ "\\x92", // 146
+ "\\x93", // 147
+ "\\x94", // 148
+ "\\x95", // 149
+ "\\x96", // 150
+ "\\x97", // 151
+ "\\x98", // 152
+ "\\x99", // 153
+ "\\x9a", // 154
+ "\\x9b", // 155
+ "\\x9c", // 156
+ "\\x9d", // 157
+ "\\x9e", // 158
+ "\\x9f", // 159
+ "\\xa0", // 160
+ "\\xa1", // 161
+ "\\xa2", // 162
+ "\\xa3", // 163
+ "\\xa4", // 164
+ "\\xa5", // 165
+ "\\xa6", // 166
+ "\\xa7", // 167
+ "\\xa8", // 168
+ "\\xa9", // 169
+ "\\xaa", // 170
+ "\\xab", // 171
+ "\\xac", // 172
+ "\\xad", // 173
+ "\\xae", // 174
+ "\\xaf", // 175
+ "\\xb0", // 176
+ "\\xb1", // 177
+ "\\xb2", // 178
+ "\\xb3", // 179
+ "\\xb4", // 180
+ "\\xb5", // 181
+ "\\xb6", // 182
+ "\\xb7", // 183
+ "\\xb8", // 184
+ "\\xb9", // 185
+ "\\xba", // 186
+ "\\xbb", // 187
+ "\\xbc", // 188
+ "\\xbd", // 189
+ "\\xbe", // 190
+ "\\xbf", // 191
+ "\\xc0", // 192
+ "\\xc1", // 193
+ "\\xc2", // 194
+ "\\xc3", // 195
+ "\\xc4", // 196
+ "\\xc5", // 197
+ "\\xc6", // 198
+ "\\xc7", // 199
+ "\\xc8", // 200
+ "\\xc9", // 201
+ "\\xca", // 202
+ "\\xcb", // 203
+ "\\xcc", // 204
+ "\\xcd", // 205
+ "\\xce", // 206
+ "\\xcf", // 207
+ "\\xd0", // 208
+ "\\xd1", // 209
+ "\\xd2", // 210
+ "\\xd3", // 211
+ "\\xd4", // 212
+ "\\xd5", // 213
+ "\\xd6", // 214
+ "\\xd7", // 215
+ "\\xd8", // 216
+ "\\xd9", // 217
+ "\\xda", // 218
+ "\\xdb", // 219
+ "\\xdc", // 220
+ "\\xdd", // 221
+ "\\xde", // 222
+ "\\xdf", // 223
+ "\\xe0", // 224
+ "\\xe1", // 225
+ "\\xe2", // 226
+ "\\xe3", // 227
+ "\\xe4", // 228
+ "\\xe5", // 229
+ "\\xe6", // 230
+ "\\xe7", // 231
+ "\\xe8", // 232
+ "\\xe9", // 233
+ "\\xea", // 234
+ "\\xeb", // 235
+ "\\xec", // 236
+ "\\xed", // 237
+ "\\xee", // 238
+ "\\xef", // 239
+ "\\xf0", // 240
+ "\\xf1", // 241
+ "\\xf2", // 242
+ "\\xf3", // 243
+ "\\xf4", // 244
+ "\\xf5", // 245
+ "\\xf6", // 246
+ "\\xf7", // 247
+ "\\xf8", // 248
+ "\\xf9", // 249
+ "\\xfa", // 250
+ "\\xfb", // 251
+ "\\xfc", // 252
+ "\\xfd", // 253
+ "\\xfe", // 254
+ "\\xff" // 255
+};
+
+void serialize_string(const std::string& value, std::ostream& str)
+{
+ std::string::const_iterator it = value.begin();
+ std::string::const_iterator end = value.end();
+ U8 c;
+ for(; it != end; ++it)
+ {
+ c = (U8)(*it);
+ str << NOTATION_STRING_CHARACTERS[c];
+ }
+}
+
+