123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209 |
- //
- // PlistCpp Property List (plist) serialization and parsing library.
- //
- // https://github.com/animetrics/PlistCpp
- //
- // Copyright (c) 2011 Animetrics Inc. (marc@animetrics.com)
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- #include "plisthpp/Plist.hpp"
- #include <boost/locale/encoding_utf.hpp>
- #include <list>
- #include <sstream>
- #include "plisthpp/base64.hpp"
- #include "plisthpp/pugixml.hpp"
- namespace Plist {
- struct PlistHelperData
- {
- public:
- // binary helper data
- std::vector<int32_t> _offsetTable;
- std::vector<unsigned char> _objectTable;
- int32_t _offsetByteSize;
- int64_t _offsetTableOffset;
- int32_t _objRefSize;
- int32_t _refCount;
- };
- void writePlistBinary(
- PlistHelperData& d,
- const boost::any& message);
- void writePlistXML(
- pugi::xml_document& doc,
- const boost::any& message);
- // helper functions
- // msvc <= 2005 doesn't have std::vector::data() method
- template<typename T>
- T* vecData(std::vector<T>& vec)
- {
- return (vec.size() > 0) ? &vec[0] : 0;
- // if(vec.size() > 0)
- // return &vec[0];
- // else
- // throw Error("vecData trying to get pointer to empty std::vector");
- }
- template<typename T>
- const T* vecData(const std::vector<T>& vec)
- {
- return (vec.size() > 0) ? &vec[0] : 0;
- // if(vec.size() > 0)
- // return &vec[0];
- // else
- // throw Error("vecData trying to get pointer to empty std::vector");
- }
- // xml helper functions
- template<typename T>
- std::string stringFromValue(const T& value);
- template<typename T>
- void writeXMLSimpleNode(pugi::xml_node& node, const char* name, const boost::any& obj);
- // xml parsing
- dictionary_type parseDictionary(pugi::xml_node& node);
- array_type parseArray(pugi::xml_node& node);
- std::vector<char> base64Decode(const char* data);
- void base64Encode(std::string& dataEncoded, const std::vector<char>& data);
- Date parseDate(pugi::xml_node& node);
- boost::any parse(pugi::xml_node& doc);
- // xml writing
- void writeXMLArray(pugi::xml_node& node, const array_type& array);
- void writeXMLDictionary(pugi::xml_node& node, const dictionary_type& message);
- void writeXMLNode(pugi::xml_node& node, const boost::any& obj);
- // binary helper functions
- template <typename IntegerType>
- IntegerType bytesToInt(const unsigned char* bytes, bool littleEndian);
- double bytesToDouble(const unsigned char* bytes, bool littleEndian);
- std::vector<unsigned char> doubleToBytes(double val, bool littleEndian);
- template<typename IntegerType>
- std::vector<unsigned char> intToBytes(IntegerType val, bool littleEndian);
- std::vector<unsigned char> getRange(const unsigned char* origBytes, int64_t index, int64_t size);
- std::vector<unsigned char> getRange(const std::vector<unsigned char>& origBytes, int64_t index, int64_t size);
- std::vector<char> getRange(const char* origBytes, int64_t index, int64_t size);
- // binary parsing
- boost::any parseBinary(const PlistHelperData& d, int objRef);
- dictionary_type parseBinaryDictionary(const PlistHelperData& d, int objRef);
- array_type parseBinaryArray(const PlistHelperData& d, int objRef);
- std::vector<int32_t> getRefsForContainers(const PlistHelperData& d, int objRef);
- int64_t parseBinaryInt(const PlistHelperData& d, int headerPosition, int& intByteCount);
- double parseBinaryReal(const PlistHelperData& d, int headerPosition);
- Date parseBinaryDate(const PlistHelperData& d, int headerPosition);
- bool parseBinaryBool(const PlistHelperData& d, int headerPosition);
- std::string parseBinaryString(const PlistHelperData& d, int objRef);
- std::string parseBinaryUnicode(const PlistHelperData& d, int headerPosition);
- data_type parseBinaryByteArray(const PlistHelperData& d, int headerPosition);
- std::vector<unsigned char> regulateNullBytes(const std::vector<unsigned char>& origBytes, unsigned int minBytes);
- void parseTrailer(PlistHelperData& d, const std::vector<unsigned char>& trailer);
- void parseOffsetTable(PlistHelperData& d, const std::vector<unsigned char>& offsetTableBytes);
- int32_t getCount(const PlistHelperData& d, int bytePosition, unsigned char headerByte, int& startOffset);
- // binary writing
- int countAny(const boost::any& object);
- int countDictionary(const dictionary_type& dictionary);
- int countArray(const array_type& array);
- std::vector<unsigned char> writeBinaryDictionary(PlistHelperData& d, const dictionary_type& dictionary);
- std::vector<unsigned char> writeBinaryArray(PlistHelperData& d, const array_type& array);
- std::vector<unsigned char> writeBinaryByteArray(PlistHelperData& d, const data_type& byteArray);
- std::vector<unsigned char> writeBinaryInteger(PlistHelperData& d, int64_t value, bool write);
- std::vector<unsigned char> writeBinaryBool(PlistHelperData& d, bool value);
- std::vector<unsigned char> writeBinaryDate(PlistHelperData& d, const Date& date);
- std::vector<unsigned char> writeBinaryDouble(PlistHelperData& d, double value);
- std::vector<unsigned char> writeBinary(PlistHelperData& d, const boost::any& obj);
- std::vector<unsigned char> writeBinaryString(PlistHelperData& d, const std::string& value, bool head);
- inline bool hostLittleEndian()
- {
- union { uint32_t x; uint8_t c[4]; } u;
- u.x = 0xab0000cd;
- return u.c[0] == 0xcd;
- }
- } // namespace Plist
- namespace Plist {
- template<typename T>
- void writeXMLSimpleNode(pugi::xml_node& node, const char* name, const boost::any& obj)
- {
- pugi::xml_node newNode;
- newNode = node.append_child(name);
- newNode.append_child(pugi::node_pcdata).set_value(stringFromValue(boost::any_cast<const T&>(obj)).c_str());
- }
- void writeXMLNode(pugi::xml_node& node, const boost::any& obj)
- {
- using namespace std;
- const std::type_info &objType = obj.type();
- if(objType == typeid(int32_t))
- writeXMLSimpleNode<int32_t>(node, "integer", obj);
- else if(objType == typeid(int64_t))
- writeXMLSimpleNode<int64_t>(node, "integer", obj);
- else if(objType == typeid(long))
- writeXMLSimpleNode<long>(node, "integer", obj);
- else if(objType == typeid(short))
- writeXMLSimpleNode<short>(node, "integer", obj);
- else if(objType == typeid(dictionary_type))
- writeXMLDictionary(node, boost::any_cast<const dictionary_type&>(obj));
- else if(objType == typeid(string_type))
- writeXMLSimpleNode<string_type>(node, "string", obj);
- else if(objType == typeid(array_type))
- writeXMLArray(node, boost::any_cast<const array_type&>(obj));
- else if(objType == typeid(data_type))
- {
- string dataEncoded;
- base64Encode(dataEncoded, boost::any_cast<const data_type&>(obj));
- writeXMLSimpleNode<string>(node, "data", dataEncoded);
- }
- else if(objType == typeid(double))
- writeXMLSimpleNode<double>(node, "real", obj);
- else if(objType == typeid(float))
- writeXMLSimpleNode<float>(node, "real", obj);
- else if(objType == typeid(Date))
- writeXMLSimpleNode<string>(node, "date", boost::any_cast<const Date&>(obj).timeAsXMLConvention());
- else if(objType == typeid(bool))
- {
- bool value = boost::any_cast<const bool&>(obj);
- node.append_child(value ? "true" : "false");
- }
- else
- throw Error((string("Plist Error: Can't serialize type ") + objType.name()).c_str());
- }
- void writeXMLArray(
- pugi::xml_node& node,
- const array_type& array)
- {
- using namespace std;
- pugi::xml_node newNode = node.append_child("array");
- for(array_type::const_iterator it = array.begin();
- it != array.end();
- ++it)
- writeXMLNode(newNode, *it);
- }
- void writeXMLDictionary(
- pugi::xml_node& node,
- const dictionary_type& message)
- {
- using namespace std;
- pugi::xml_node newNode = node.append_child("dict");
- for(dictionary_type::const_iterator it = message.begin();
- it != message.end();
- ++it)
- {
- pugi::xml_node keyNode = newNode.append_child("key");
- keyNode.append_child(pugi::node_pcdata).set_value(it->first.c_str());
- writeXMLNode(newNode, it->second);
- }
- }
- void writePlistXML(
- pugi::xml_document& doc,
- const boost::any& message)
- // const dictionary_type& message)
- {
- // declaration node
- pugi::xml_node decNode = doc.append_child(pugi::node_declaration);
- decNode.append_attribute("version") = "1.0";
- decNode.append_attribute("encoding") = "UTF-8";
- // doctype node
- doc.append_child(pugi::node_doctype).set_value("plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"");
- // root node
- pugi::xml_node plistNode = doc.append_child("plist");
- plistNode.append_attribute("version") = "1.0";
- writeXMLNode(plistNode, message);
- }
- void writePlistBinary(
- PlistHelperData& d,
- const boost::any& message)
- {
- using namespace std;
- //int totalRefs = countDictionary(message);
- int totalRefs = countAny(message) - 1;
- d._refCount = totalRefs;
- d._objRefSize = regulateNullBytes(intToBytes<int32_t>(d._refCount, hostLittleEndian()), 1).size();
- //writeBinaryDictionary(d, message);
- writeBinary(d, message);
- writeBinaryString(d, "bplist00", false);
- d._offsetTableOffset = (int64_t) d._objectTable.size();
- d._offsetTable.push_back(d._objectTable.size() - 8);
- d._offsetByteSize = regulateNullBytes(intToBytes<int>(d._offsetTable.back(), hostLittleEndian()), 1).size();
- vector<unsigned char> offsetBytes;
- reverse(d._offsetTable.begin(), d._offsetTable.end());
- for(unsigned int i = 0; i < d._offsetTable.size(); ++i)
- {
- d._offsetTable[i] = d._objectTable.size() - d._offsetTable[i];
- vector<unsigned char> buffer = regulateNullBytes(intToBytes<int>(d._offsetTable[i], hostLittleEndian()), d._offsetByteSize);
- //reverse(buffer.begin(), buffer.end());
- offsetBytes.insert(offsetBytes.end(), buffer.rbegin(), buffer.rend());
- }
- d._objectTable.insert(d._objectTable.end(), offsetBytes.begin(), offsetBytes.end());
- vector<unsigned char> dummy(6, 0);
- d._objectTable.insert(d._objectTable.end(), dummy.begin(), dummy.end());
- d._objectTable.push_back((unsigned char) (d._offsetByteSize));
- d._objectTable.push_back((unsigned char) (d._objRefSize));
- vector<unsigned char> temp = intToBytes<int64_t>((int64_t) totalRefs + 1, hostLittleEndian());
- d._objectTable.insert(d._objectTable.end(), temp.rbegin(), temp.rend());
- temp = intToBytes<int64_t>(0, hostLittleEndian());
- d._objectTable.insert(d._objectTable.end(), temp.begin(), temp.end());
- temp = intToBytes<int64_t>(d._offsetTableOffset, hostLittleEndian());
- d._objectTable.insert(d._objectTable.end(), temp.rbegin(), temp.rend());
- }
- void writePlistBinary(std::vector<char>& plist, const boost::any& message)
- {
- PlistHelperData d;
- writePlistBinary(d, message);
- plist.resize(d._objectTable.size());
- std::copy((const char*) vecData(d._objectTable), (const char*) vecData(d._objectTable) + d._objectTable.size(), plist.begin());
- }
- void writePlistBinary(
- std::ostream& stream,
- const boost::any& message)
- {
- PlistHelperData d;
- writePlistBinary(d, message);
- stream.write((const char*) vecData(d._objectTable), d._objectTable.size());
- }
- void writePlistBinary(
- const char* filename,
- const boost::any& message)
- {
- std::ofstream stream(filename, std::ios::binary);
- writePlistBinary(stream, message);
- stream.close();
- }
- #if defined(_MSC_VER)
- void writePlistBinary(
- const wchar_t* filename,
- const boost::any& message)
- {
- std::ofstream stream(filename, std::ios::binary);
- writePlistBinary(stream, message);
- stream.close();
- }
- #endif
- void writePlistXML(std::vector<char>& plist, const boost::any& message)
- {
- std::stringstream ss;
- writePlistXML(ss, message);
- std::istreambuf_iterator<char> beg(ss);
- std::istreambuf_iterator<char> end;
- plist.clear();
- plist.insert(plist.begin(), beg, end);
- }
- void writePlistXML(
- std::ostream& stream,
- const boost::any& message)
- {
- pugi::xml_document doc;
- writePlistXML(doc, message);
- doc.save(stream);
- }
- void writePlistXML(
- const char* filename,
- const boost::any& message)
- {
- std::ofstream stream(filename, std::ios::binary);
- writePlistXML(stream, message);
- stream.close();
- }
- #if defined(_MSC_VER)
- void writePlistXML(
- const wchar_t* filename,
- const boost::any& message)
- {
- std::ofstream stream(filename, std::ios::binary);
- writePlistXML(stream, message);
- stream.close();
- }
- #endif
- int countAny(const boost::any& object)
- {
- using namespace std;
- static boost::any dict = dictionary_type();
- static boost::any array = array_type();
- int count = 0;
- if(object.type() == dict.type())
- count += countDictionary(boost::any_cast<dictionary_type >(object)) + 1;
- else if (object.type() == array.type())
- count += countArray(boost::any_cast<array_type >(object)) + 1;
- else
- ++count;
- return count;
- }
- std::vector<unsigned char> writeBinary(PlistHelperData& d, const boost::any& obj)
- {
- using namespace std;
- const std::type_info &objType = obj.type();
- std::vector<unsigned char> value;
- if(objType == typeid(int32_t))
- value = writeBinaryInteger(d, boost::any_cast<const int32_t&>(obj), true);
- else if(objType == typeid(int64_t))
- value = writeBinaryInteger(d, boost::any_cast<const int64_t&>(obj), true);
- else if(objType == typeid(long))
- value = writeBinaryInteger(d, boost::any_cast<const long&>(obj), true);
- else if(objType == typeid(short))
- value = writeBinaryInteger(d, boost::any_cast<const short&>(obj), true);
- else if(objType == typeid(dictionary_type))
- value = writeBinaryDictionary(d, boost::any_cast<const dictionary_type& >(obj));
- else if(objType == typeid(string))
- value = writeBinaryString(d, boost::any_cast<const string&>(obj), true);
- else if(objType == typeid(array_type))
- value = writeBinaryArray(d, boost::any_cast<const array_type& >(obj));
- else if(objType == typeid(data_type))
- value = writeBinaryByteArray(d, boost::any_cast<const data_type& >(obj));
- else if(objType == typeid(double))
- value = writeBinaryDouble(d, boost::any_cast<const double&>(obj));
- else if(objType == typeid(float))
- value = writeBinaryDouble(d, boost::any_cast<const float&>(obj));
- else if(objType == typeid(Date))
- value = writeBinaryDate(d, boost::any_cast<const Date&>(obj));
- else if(objType == typeid(bool))
- value = writeBinaryBool(d, boost::any_cast<const bool&>(obj));
- else
- throw Error((string("Plist Error: Can't serialize type ") + objType.name()).c_str());
- return value;
- }
- static uint32_t nextpow2(uint32_t x)
- {
- --x;
- x |= x >> 1;
- x |= x >> 2;
- x |= x >> 4;
- x |= x >> 8;
- x |= x >> 16;
- return x + 1;
- }
- static uint32_t ilog2(uint32_t x)
- {
- uint32_t r = 0;
- while (x >>= 1)
- ++r;
- return r;
- }
- std::vector<unsigned char> writeBinaryByteArray(PlistHelperData& d, const data_type& byteArray)
- {
- using namespace std;
- vector<unsigned char> header;
- if(byteArray.size() < 15)
- header.push_back(0x40 | ((unsigned char) byteArray.size()));
- else
- {
- header.push_back(0x40 | 0xf);
- vector<unsigned char> theSize = writeBinaryInteger(d, byteArray.size(), false);
- header.insert(header.end(), theSize.begin(), theSize.end());
- }
- vector<unsigned char> buffer(header);
- buffer.insert(buffer.end(), (unsigned char*) vecData(byteArray), (unsigned char*) vecData(byteArray) + byteArray.size());
- d._objectTable.insert(d._objectTable.begin(), buffer.begin(), buffer.end());
- return buffer;
- }
- std::vector<unsigned char> writeBinaryArray(PlistHelperData& d, const array_type& array)
- {
- using namespace std;
- vector<int32_t> refs;
- for(array_type::const_reverse_iterator it = array.rbegin();
- it != array.rend();
- ++it)
- {
- writeBinary(d, *it);
- d._offsetTable.push_back(d._objectTable.size());
- refs.push_back(d._refCount);
- d._refCount--;
- }
- vector<unsigned char> header;
- if (array.size() < 15)
- {
- header.push_back(0xA0 | ((unsigned char) array.size()));
- }
- else
- {
- header.push_back(0xA0 | 0xf);
- vector<unsigned char> theSize = writeBinaryInteger(d, array.size(), false);
- header.insert(header.end(), theSize.begin(), theSize.end());
- }
- // try to do this more efficiently. Not good to insert at the begining of buffer.
- vector<unsigned char> buffer;
- for(vector<int32_t>::const_iterator it = refs.begin();
- it != refs.end();
- ++it)
- {
- vector<unsigned char> refBuffer = regulateNullBytes(intToBytes<int32_t>(*it, hostLittleEndian()), d._objRefSize);
- // reverse(refBuffer.begin(), refBuffer.end());
- buffer.insert(buffer.begin(), refBuffer.rbegin(), refBuffer.rend());
- }
- buffer.insert(buffer.begin(), header.begin(), header.end());
- d._objectTable.insert(d._objectTable.begin(), buffer.begin(), buffer.end());
- return buffer;
- }
- std::vector<unsigned char> writeBinaryDictionary(PlistHelperData& d, const dictionary_type& dictionary)
- {
- using namespace std;
- vector<int32_t> refs;
- for(dictionary_type::const_reverse_iterator it = dictionary.rbegin();
- it != dictionary.rend();
- ++it)
- {
- writeBinary(d, it->second);
- d._offsetTable.push_back(d._objectTable.size());
- refs.push_back(d._refCount);
- d._refCount--;
- }
- for(dictionary_type::const_reverse_iterator it = dictionary.rbegin();
- it != dictionary.rend();
- ++it)
- {
- writeBinary(d, it->first);
- d._offsetTable.push_back(d._objectTable.size());
- refs.push_back(d._refCount);
- d._refCount--;
- }
- vector<unsigned char> header;
- if (dictionary.size() < 15)
- {
- header.push_back(0xD0 | ((unsigned char) dictionary.size()));
- }
- else
- {
- header.push_back(0xD0 | 0xf);
- vector<unsigned char> theSize = writeBinaryInteger(d, dictionary.size(), false);
- header.insert(header.end(), theSize.begin(), theSize.end());
- }
- // try to do this more efficiently. Not good to insert at the begining of buffer.
- vector<unsigned char> buffer;
- for(vector<int32_t>::const_iterator it = refs.begin();
- it != refs.end();
- ++it)
- {
- vector<unsigned char> refBuffer = regulateNullBytes(intToBytes<int32_t>(*it, hostLittleEndian()), d._objRefSize);
- // reverse(refBuffer.begin(), refBuffer.end());
- buffer.insert(buffer.begin(), refBuffer.rbegin(), refBuffer.rend());
- }
- buffer.insert(buffer.begin(), header.begin(), header.end());
- d._objectTable.insert(d._objectTable.begin(), buffer.begin(), buffer.end());
- return buffer;
- }
- std::vector<unsigned char> writeBinaryDouble(PlistHelperData& d, double value)
- {
- using namespace std;
- vector<unsigned char> buffer = regulateNullBytes(doubleToBytes(value, hostLittleEndian()), 4);
- buffer.resize(nextpow2(buffer.size()), 0);
- unsigned char header = 0x20 | ilog2(buffer.size());
- buffer.push_back(header);
- reverse(buffer.begin(), buffer.end());
- d._objectTable.insert(d._objectTable.begin(), buffer.begin(), buffer.end());
- return buffer;
- }
- std::vector<unsigned char> writeBinaryBool(PlistHelperData& d, bool value)
- {
- std::vector<unsigned char> buffer;
- if(value)
- buffer.push_back(0x09);
- else
- buffer.push_back(0x08);
- d._objectTable.insert(d._objectTable.begin(), buffer.begin(), buffer.end());
- return buffer;
- }
- std::vector<unsigned char> writeBinaryDate(PlistHelperData& d, const Date& date)
- {
- std::vector<unsigned char> buffer;
- // need to serialize as Apple epoch.
- double macTime = date.timeAsAppleEpoch();
- buffer = doubleToBytes(macTime, false);
- buffer.insert(buffer.begin(), 0x33);
- d._objectTable.insert(d._objectTable.begin(), buffer.begin(), buffer.end());
- return buffer;
- }
- std::vector<unsigned char> writeBinaryInteger(PlistHelperData& d, int64_t value, bool write)
- {
- using namespace std;
- // The integer is initially forced to be 64 bit because it must be serialized
- // as 8 bytes if it is negative. If it is not negative, the
- // regulateNullBytes step will reduce the representation down to the min
- // power base 2 bytes needed to store it.
- vector<unsigned char> buffer = intToBytes<int64_t>(value, hostLittleEndian());
- buffer = regulateNullBytes(intToBytes<int64_t>(value, hostLittleEndian()), 1);
- buffer.resize(nextpow2(buffer.size()), 0);
- unsigned char header = 0x10 | ilog2(buffer.size());
- buffer.push_back(header);
- reverse(buffer.begin(), buffer.end());
- if(write)
- d._objectTable.insert(d._objectTable.begin(), buffer.begin(), buffer.end());
- return buffer;
- }
- std::vector<unsigned char> writeBinaryString(PlistHelperData& d, const std::string& value, bool head)
- {
- using namespace std;
- vector<unsigned char> buffer;
- buffer.reserve(value.size());
- for(std::string::const_iterator it = value.begin();
- it != value.end();
- ++it)
- buffer.push_back((unsigned char) *it);
- if(head)
- {
- vector<unsigned char> header;
- if (value.length() < 15)
- header.push_back(0x50 | ((unsigned char) value.length()));
- else
- {
- header.push_back(0x50 | 0xf);
- vector<unsigned char> theSize = writeBinaryInteger(d, buffer.size(), false);
- header.insert(header.end(), theSize.begin(), theSize.end());
- }
- buffer.insert(buffer.begin(), header.begin(), header.end());
- }
- d._objectTable.insert(d._objectTable.begin(), buffer.begin(), buffer.end());
- return buffer;
- }
- int countDictionary(const dictionary_type& dictionary)
- {
- using namespace std;
- int count = 0;
- for(dictionary_type::const_iterator it = dictionary.begin();
- it != dictionary.end();
- ++it)
- {
- ++count;
- count += countAny(it->second);
- }
- return count;
- }
- int countArray(const array_type& array)
- {
- using namespace std;
- int count = 0;
- for(array_type::const_iterator it = array.begin();
- it != array.end();
- ++it)
- count += countAny(*it);
- return count;
- }
- void readPlist(std::istream& stream, boost::any& message)
- {
- int start = stream.tellg();
- stream.seekg(0, std::ifstream::end);
- int size = ((int) stream.tellg()) - start;
- if(size > 0)
- {
- stream.seekg(0, std::ifstream::beg);
- std::vector<char> buffer(size);
- stream.read( (char *)&buffer[0], size );
- readPlist(&buffer[0], size, message);
- }
- else
- {
- throw Error("Can't read zero length data");
- }
- }
- void readPlist(const char* byteArrayTemp, int64_t size, boost::any& message)
- {
- using namespace std;
- const unsigned char* byteArray = (const unsigned char*) byteArrayTemp;
- if (!byteArray || (size == 0))
- throw Error("Plist: Empty plist data");
- // infer plist type from header. If it has the bplist00 header as first 8
- // bytes, then it's a binary plist. Otherwise, assume it's XML
- std::string magicHeader((const char*) byteArray, 8);
- if(magicHeader == "bplist00")
- {
- PlistHelperData d;
- parseTrailer(d, getRange(byteArray, size - 32, 32));
- d._objectTable = getRange(byteArray, 0, d._offsetTableOffset);
- std::vector<unsigned char> offsetTableBytes = getRange(byteArray, d._offsetTableOffset, size - d._offsetTableOffset - 32);
- parseOffsetTable(d, offsetTableBytes);
- message = parseBinary(d, 0);
- }
- else
- {
- pugi::xml_document doc;
- pugi::xml_parse_result result = doc.load_buffer(byteArray, (size_t)size);
- if(!result)
- throw Error((string("Plist: XML parsed with error ") + result.description()).c_str());
- pugi::xml_node rootNode = doc.child("plist").first_child();
- message = parse(rootNode);
- }
- }
- dictionary_type parseDictionary(pugi::xml_node& node)
- {
- using namespace std;
- dictionary_type dict;
- for(pugi::xml_node_iterator it = node.begin(); it != node.end(); ++it)
- {
- if(string("key") != it->name())
- throw Error("Plist: XML dictionary key expected but not found");
- string key(it->first_child().value());
- ++it;
- if(it == node.end())
- throw Error("Plist: XML dictionary value expected for key " + key + "but not found");
- else if(string("key") == it->name())
- throw Error("Plist: XML dictionary value expected for key " + key + "but found another key node");
- dict[key] = parse(*it);
- }
- return dict;
- }
- array_type parseArray(pugi::xml_node& node)
- {
- using namespace std;
- array_type array;
- for(pugi::xml_node_iterator it = node.begin(); it != node.end(); ++it)
- array.push_back(parse(*it));
- return array;
- }
- Date parseDate(pugi::xml_node& node)
- {
- Date date;
- date.setTimeFromXMLConvention(node.first_child().value());
- return date;
- }
- std::vector<char> base64Decode(const char* encodedData)
- {
- using namespace std;
- vector<char> data;
- insert_iterator<vector<char> > ii(data, data.begin());
- base64<char> b64;
- int state = 0;
- b64.get(encodedData, encodedData + strlen(encodedData), ii, state);
- return data;
- }
- void base64Encode(std::string& dataEncoded, const std::vector<char>& data)
- {
- using namespace std;
- dataEncoded.clear();
- insert_iterator<string> ii(dataEncoded, dataEncoded.begin());
- base64<char> b64;
- int state = 0;
- #if defined(_WIN32) || defined(_WIN64)
- b64.put(data.begin(), data.end(), ii, state , base64<>::crlf());
- #else
- b64.put(data.begin(), data.end(), ii, state , base64<>::lf());
- #endif
- }
- boost::any parse(pugi::xml_node& node)
- {
- using namespace std;
- string nodeName = node.name();
- boost::any result;
- if("dict" == nodeName)
- result = parseDictionary(node);
- else if("array" == nodeName)
- result = parseArray(node);
- else if("string" == nodeName)
- result = string(node.first_child().value());
- else if("integer" == nodeName)
- result = (int64_t) atoll(node.first_child().value());
- else if("real" == nodeName)
- result = atof(node.first_child().value());
- else if("false" == nodeName)
- result = bool(false);
- else if("true" == nodeName)
- result = bool(true);
- else if("data" == nodeName)
- result = base64Decode(node.first_child().value());
- else if("date" == nodeName)
- result = parseDate(node);
- else
- throw Error(string("Plist: XML unknown node type " + nodeName));
- return result;
- }
- void parseOffsetTable(PlistHelperData& d, const std::vector<unsigned char>& offsetTableBytes)
- {
- for (unsigned int i = 0; i < offsetTableBytes.size(); i += d._offsetByteSize)
- {
- std::vector<unsigned char> temp = getRange(offsetTableBytes, i, d._offsetByteSize);
- std::reverse(temp.begin(), temp.end());
- d._offsetTable.push_back(
- bytesToInt<int32_t>(
- vecData(regulateNullBytes(temp, 4)), hostLittleEndian()));
- }
- }
- void parseTrailer(PlistHelperData& d, const std::vector<unsigned char>& trailer)
- {
- d._offsetByteSize = bytesToInt<int32_t>(vecData(regulateNullBytes(getRange(trailer, 6, 1), 4)), hostLittleEndian());
- d._objRefSize = bytesToInt<int32_t>(vecData(regulateNullBytes(getRange(trailer, 7, 1), 4)), hostLittleEndian());
- std::vector<unsigned char> refCountBytes = getRange(trailer, 12, 4);
- // std::reverse(refCountBytes.begin(), refCountBytes.end());
- d._refCount = bytesToInt<int32_t>(vecData(refCountBytes), false);
- std::vector<unsigned char> offsetTableOffsetBytes = getRange(trailer, 24, 8);
- // std::reverse(offsetTableOffsetBytes.begin(), offsetTableOffsetBytes.end());
- d._offsetTableOffset = bytesToInt<int64_t>(vecData(offsetTableOffsetBytes), false);
- }
- std::vector<unsigned char> regulateNullBytes(const std::vector<unsigned char>& origBytes, unsigned int minBytes)
- {
- std::vector<unsigned char> bytes(origBytes);
- while((bytes.back() == 0) && (bytes.size() > minBytes))
- bytes.pop_back();
- while(bytes.size() < minBytes)
- bytes.push_back(0);
- return bytes;
- }
- boost::any parseBinary(const PlistHelperData& d, int objRef)
- {
- unsigned char header = d._objectTable[d._offsetTable[objRef]];
- switch (header & 0xF0)
- {
- case 0x00:
- {
- return parseBinaryBool(d, d._offsetTable[objRef]);
- }
- case 0x10:
- {
- int intByteCount;
- return parseBinaryInt(d, d._offsetTable[objRef], intByteCount);
- }
- case 0x20:
- {
- return parseBinaryReal(d, d._offsetTable[objRef]);
- }
- case 0x30:
- {
- return parseBinaryDate(d, d._offsetTable[objRef]);
- }
- case 0x40:
- {
- return parseBinaryByteArray(d, d._offsetTable[objRef]);
- }
- case 0x50:
- {
- return parseBinaryString(d, d._offsetTable[objRef]);
- }
- case 0x60:
- {
- return parseBinaryUnicode(d, d._offsetTable[objRef]);
- }
- case 0xD0:
- {
- return parseBinaryDictionary(d, objRef);
- }
- case 0xA0:
- {
- return parseBinaryArray(d, objRef);
- }
- }
- throw Error("This type is not supported");
- }
- std::vector<int32_t> getRefsForContainers(const PlistHelperData& d, int objRef)
- {
- using namespace std;
- int32_t refCount = 0;
- int refStartPosition;
- refCount = getCount(d, d._offsetTable[objRef], d._objectTable[d._offsetTable[objRef]], refStartPosition);
- refStartPosition += d._offsetTable[objRef];
- vector<int32_t> refs;
- int mult = 1;
- if((((unsigned char) d._objectTable[d._offsetTable[objRef]]) & 0xF0) == 0xD0)
- mult = 2;
- for (int i = refStartPosition; i < refStartPosition + refCount * mult * d._objRefSize; i += d._objRefSize)
- {
- std::vector<unsigned char> refBuffer = getRange(d._objectTable, i, d._objRefSize);
- reverse(refBuffer.begin(), refBuffer.end());
- refs.push_back(bytesToInt<int32_t>(vecData(regulateNullBytes(refBuffer, 4)), hostLittleEndian()));
- }
- return refs;
- }
- array_type parseBinaryArray(const PlistHelperData& d, int objRef)
- {
- using namespace std;
- vector<int32_t> refs = getRefsForContainers(d, objRef);
- int32_t refCount = refs.size();
- array_type array;
- for(int i = 0; i < refCount; ++i)
- array.push_back(parseBinary(d, refs[i]));
- return array;
- }
- dictionary_type parseBinaryDictionary(const PlistHelperData& d, int objRef)
- {
- using namespace std;
- vector<int32_t> refs = getRefsForContainers(d, objRef);
- int32_t refCount = refs.size() / 2;
- dictionary_type dict;
- for (int i = 0; i < refCount; i++)
- {
- boost::any keyAny = parseBinary(d, refs[i]);
- try
- {
- std::string& key = boost::any_cast<std::string&>(keyAny);
- dict[key] = parseBinary(d, refs[i + refCount]);
- }
- catch(boost::bad_any_cast& )
- {
- throw Error("Error parsing dictionary. Key can't be parsed as a string");
- }
- }
- return dict;
- }
- std::string parseBinaryString(const PlistHelperData& d, int headerPosition)
- {
- unsigned char headerByte = d._objectTable[headerPosition];
- int charStartPosition;
- int32_t charCount = getCount(d, headerPosition, headerByte, charStartPosition);
- charStartPosition += headerPosition;
- std::vector<unsigned char> characterBytes = getRange(d._objectTable, charStartPosition, charCount);
- std::string buffer = std::string((char*) vecData(characterBytes), characterBytes.size());
- return buffer;
- }
- std::string parseBinaryUnicode(const PlistHelperData& d, int headerPosition)
- {
- unsigned char headerByte = d._objectTable[headerPosition];
- int charStartPosition;
- int32_t charCount = getCount(d, headerPosition, headerByte, charStartPosition);
- charStartPosition += headerPosition;
- std::vector<unsigned char> characterBytes = getRange(d._objectTable, charStartPosition, charCount * 2);
- if (hostLittleEndian()) {
- if (! characterBytes.empty()) {
- for (std::size_t i = 0, n = characterBytes.size(); i < n - 1; i += 2)
- std::swap(characterBytes[i], characterBytes[i + 1]);
- }
- }
- int16_t *u16chars = (int16_t*) vecData(characterBytes);
- std::size_t u16len = characterBytes.size() / 2;
- std::string result = boost::locale::conv::utf_to_utf<char, int16_t>(u16chars, u16chars + u16len, boost::locale::conv::stop);
- return result;
- }
- int64_t parseBinaryInt(const PlistHelperData& d, int headerPosition, int& intByteCount)
- {
- unsigned char header = d._objectTable[headerPosition];
- intByteCount = 1 << (header & 0xf);
- std::vector<unsigned char> buffer = getRange(d._objectTable, headerPosition + 1, intByteCount);
- reverse(buffer.begin(), buffer.end());
- return bytesToInt<int64_t>(vecData(regulateNullBytes(buffer, 8)), hostLittleEndian());
- }
- double parseBinaryReal(const PlistHelperData& d, int headerPosition)
- {
- unsigned char header = d._objectTable[headerPosition];
- int byteCount = 1 << (header & 0xf);
- std::vector<unsigned char> buffer = getRange(d._objectTable, headerPosition + 1, byteCount);
- reverse(buffer.begin(), buffer.end());
- return bytesToDouble(vecData(regulateNullBytes(buffer, 8)), hostLittleEndian());
- }
- bool parseBinaryBool(const PlistHelperData& d, int headerPosition)
- {
- unsigned char header = d._objectTable[headerPosition];
- bool value;
- if(header == 0x09)
- value = true;
- else if (header == 0x08)
- value = false;
- else if (header == 0x00)
- {
- // null byte, not sure yet what to do with this. It's in the spec but we
- // have never encountered it.
- throw Error("Plist: null byte encountered, unsure how to parse");
- }
- else if (header == 0x0F)
- {
- // fill byte, not sure yet what to do with this. It's in the spec but we
- // have never encountered it.
- throw Error("Plist: fill byte encountered, unsure how to parse");
- }
- else
- {
- std::stringstream ss;
- ss<<"Plist: unknown header "<<header;
- throw Error(ss.str().c_str());
- }
- return value;
- }
- Date parseBinaryDate(const PlistHelperData& d, int headerPosition)
- {
- // date always an 8 byte float starting after full byte header
- std::vector<unsigned char> buffer = getRange(d._objectTable, headerPosition + 1, 8);
- Date date;
- // Date is stored as Apple Epoch and big endian.
- date.setTimeFromAppleEpoch(bytesToDouble(vecData(buffer), false));
- return date;
- }
- data_type parseBinaryByteArray(const PlistHelperData& d, int headerPosition)
- {
- unsigned char headerByte = d._objectTable[headerPosition];
- int byteStartPosition;
- int32_t byteCount = getCount(d, headerPosition, headerByte, byteStartPosition);
- byteStartPosition += headerPosition;
- return getRange((const char*) vecData(d._objectTable), byteStartPosition, byteCount);
- }
- int32_t getCount(const PlistHelperData& d, int bytePosition, unsigned char headerByte, int& startOffset)
- {
- unsigned char headerByteTrail = headerByte & 0xf;
- if (headerByteTrail < 15)
- {
- startOffset = 1;
- return headerByteTrail;
- }
- else
- {
- int32_t count = (int32_t)parseBinaryInt(d, bytePosition + 1, startOffset);
- startOffset += 2;
- return count;
- }
- }
- template<typename T>
- std::string stringFromValue(const T& value)
- {
- std::stringstream ss;
- ss<<value;
- return ss.str();
- }
- template <typename IntegerType>
- IntegerType bytesToInt(const unsigned char* bytes, bool littleEndian)
- {
- IntegerType result = 0;
- if (littleEndian)
- for (int n = sizeof( result ) - 1; n >= 0; n--)
- result = (result << 8) + bytes[n];
- else
- for (unsigned n = 0; n < sizeof( result ); n++)
- result = (result << 8) + bytes[n];
- return result;
- }
- double bytesToDouble(const unsigned char* bytes, bool littleEndian)
- {
- double result;
- int numBytes = sizeof(double);
- if(littleEndian)
- memcpy( &result, bytes, numBytes);
- else
- {
- std::vector<unsigned char> bytesReverse(numBytes);
- std::reverse_copy(bytes, bytes + numBytes, bytesReverse.begin());
- memcpy( &result, vecData(bytesReverse), numBytes);
- }
- return result;
- }
- std::vector<unsigned char> doubleToBytes(double val, bool littleEndian)
- {
- std::vector<unsigned char> result(sizeof(double));
- memcpy(vecData(result), &val, sizeof(double));
- if(!littleEndian)
- std::reverse(result.begin(), result.end());
- return result;
- }
- template<typename IntegerType>
- std::vector<unsigned char> intToBytes(IntegerType val, bool littleEndian)
- {
- unsigned int numBytes = sizeof(val);
- std::vector<unsigned char> bytes(numBytes);
- for(unsigned n = 0; n < numBytes; ++n)
- if(littleEndian)
- bytes[n] = (val >> 8 * n) & 0xff;
- else
- bytes[numBytes - 1 - n] = (val >> 8 * n) & 0xff;
- return bytes;
- }
- std::vector<unsigned char> getRange(const unsigned char* origBytes, int64_t index, int64_t size)
- {
- std::vector<unsigned char> result((std::vector<unsigned char>::size_type)size);
- std::copy(origBytes + index, origBytes + index + size, result.begin());
- return result;
- }
- std::vector<char> getRange(const char* origBytes, int64_t index, int64_t size)
- {
- std::vector<char> result((std::vector<char>::size_type)size);
- std::copy(origBytes + index, origBytes + index + size, result.begin());
- return result;
- }
- std::vector<unsigned char> getRange(const std::vector<unsigned char>& origBytes, int64_t index, int64_t size)
- {
- if((index + size) > (int64_t) origBytes.size())
- throw Error("Out of bounds getRange");
- return getRange(vecData(origBytes), index, size);
- }
- } // namespace Plist
|