binaryXml.cxx 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. // Filename: binaryXml.cxx
  2. // Created by: drose (13Jul09)
  3. //
  4. ////////////////////////////////////////////////////////////////////
  5. //
  6. // PANDA 3D SOFTWARE
  7. // Copyright (c) Carnegie Mellon University. All rights reserved.
  8. //
  9. // All use of this software is subject to the terms of the revised BSD
  10. // license. You should have received a copy of this license along
  11. // with this source code in a file named "LICENSE."
  12. //
  13. ////////////////////////////////////////////////////////////////////
  14. #include "binaryXml.h"
  15. #include <sstream>
  16. static const bool debug_xml_output = false;
  17. #define DO_BINARY_XML 1
  18. enum NodeType {
  19. NT_unknown,
  20. NT_document,
  21. NT_element,
  22. NT_text,
  23. };
  24. ////////////////////////////////////////////////////////////////////
  25. // Function: write_xml_node
  26. // Description: Recursively writes a node and all of its children to
  27. // the given stream.
  28. ////////////////////////////////////////////////////////////////////
  29. static void
  30. write_xml_node(ostream &out, TiXmlNode *xnode) {
  31. NodeType type = NT_element;
  32. if (xnode->ToDocument() != NULL) {
  33. type = NT_document;
  34. } else if (xnode->ToElement() != NULL) {
  35. type = NT_element;
  36. } else if (xnode->ToText() != NULL) {
  37. type = NT_text;
  38. } else {
  39. type = NT_unknown;
  40. }
  41. out.put((char)type);
  42. // We don't bother to write any data for the unknown types.
  43. if (type == NT_unknown) {
  44. return;
  45. }
  46. const string &value = xnode->ValueStr();
  47. size_t value_length = value.length();
  48. out.write((char *)&value_length, sizeof(value_length));
  49. out.write(value.data(), value_length);
  50. if (type == NT_element) {
  51. // Write the element attributes.
  52. TiXmlElement *xelement = xnode->ToElement();
  53. assert(xelement != NULL);
  54. const TiXmlAttribute *xattrib = xelement->FirstAttribute();
  55. while (xattrib != NULL) {
  56. // We have an attribute.
  57. out.put((char)true);
  58. string name = xattrib->Name();
  59. size_t name_length = name.length();
  60. out.write((char *)&name_length, sizeof(name_length));
  61. out.write(name.data(), name_length);
  62. const string &value = xattrib->ValueStr();
  63. size_t value_length = value.length();
  64. out.write((char *)&value_length, sizeof(value_length));
  65. out.write(value.data(), value_length);
  66. xattrib = xattrib->Next();
  67. }
  68. // The end of the attributes list.
  69. out.put((char)false);
  70. }
  71. // Now write all of the children.
  72. TiXmlNode *xchild = xnode->FirstChild();
  73. while (xchild != NULL) {
  74. // We have a child.
  75. out.put((char)true);
  76. write_xml_node(out, xchild);
  77. xchild = xchild->NextSibling();
  78. }
  79. // The end of the children list.
  80. out.put((char)false);
  81. }
  82. ////////////////////////////////////////////////////////////////////
  83. // Function: read_xml_node
  84. // Description: Recursively reads a node and all of its children to
  85. // the given stream. Returns the newly-allocated node.
  86. // The caller is responsible for eventually deleting the
  87. // return value. Returns NULL on error.
  88. ////////////////////////////////////////////////////////////////////
  89. static TiXmlNode *
  90. read_xml_node(istream &in, char *&buffer, size_t &buffer_length) {
  91. NodeType type = (NodeType)in.get();
  92. if (type == NT_unknown) {
  93. return NULL;
  94. }
  95. size_t value_length;
  96. in.read((char *)&value_length, sizeof(value_length));
  97. if (in.gcount() != sizeof(value_length)) {
  98. return NULL;
  99. }
  100. if (value_length > buffer_length) {
  101. delete[] buffer;
  102. buffer_length = value_length;
  103. buffer = new char[buffer_length];
  104. }
  105. in.read(buffer, value_length);
  106. string value(buffer, value_length);
  107. TiXmlNode *xnode = NULL;
  108. if (type == NT_element) {
  109. xnode = new TiXmlElement(value);
  110. } else if (type == NT_document) {
  111. xnode = new TiXmlDocument;
  112. } else if (type == NT_text) {
  113. xnode = new TiXmlText(value);
  114. } else {
  115. assert(false);
  116. }
  117. if (type == NT_element) {
  118. // Read the element attributes.
  119. TiXmlElement *xelement = xnode->ToElement();
  120. assert(xelement != NULL);
  121. bool got_attrib = (bool)(in.get() != 0);
  122. while (got_attrib && in && !in.eof()) {
  123. // We have an attribute.
  124. size_t name_length;
  125. in.read((char *)&name_length, sizeof(name_length));
  126. if (in.gcount() != sizeof(name_length)) {
  127. delete xnode;
  128. return NULL;
  129. }
  130. if (name_length > buffer_length) {
  131. delete[] buffer;
  132. buffer_length = name_length;
  133. buffer = new char[buffer_length];
  134. }
  135. in.read(buffer, name_length);
  136. string name(buffer, name_length);
  137. size_t value_length;
  138. in.read((char *)&value_length, sizeof(value_length));
  139. if (in.gcount() != sizeof(value_length)) {
  140. delete xnode;
  141. return NULL;
  142. }
  143. if (value_length > buffer_length) {
  144. delete[] buffer;
  145. buffer_length = value_length;
  146. buffer = new char[buffer_length];
  147. }
  148. in.read(buffer, value_length);
  149. string value(buffer, value_length);
  150. xelement->SetAttribute(name, value);
  151. got_attrib = (bool)(in.get() != 0);
  152. }
  153. }
  154. // Now read all of the children.
  155. bool got_child = (bool)(in.get() != 0);
  156. while (got_child && in && !in.eof()) {
  157. // We have a child.
  158. TiXmlNode *xchild = read_xml_node(in, buffer, buffer_length);
  159. if (xchild != NULL) {
  160. xnode->LinkEndChild(xchild);
  161. }
  162. got_child = (bool)(in.get() != 0);
  163. }
  164. return xnode;
  165. }
  166. ////////////////////////////////////////////////////////////////////
  167. // Function: write_xml
  168. // Description: Writes the indicated TinyXml document to the given
  169. // stream.
  170. ////////////////////////////////////////////////////////////////////
  171. void
  172. write_xml(ostream &out, TiXmlDocument *doc, ostream &logfile) {
  173. #ifdef DO_BINARY_XML
  174. // Binary write.
  175. write_xml_node(out, doc);
  176. #else
  177. // Formatted ASCII write.
  178. // We need a declaration to write it safely.
  179. TiXmlDeclaration decl("1.0", "utf-8", "");
  180. doc->InsertBeforeChild(doc->FirstChild(), decl);
  181. out << *doc;
  182. #endif
  183. out << flush;
  184. if (debug_xml_output) {
  185. // Write via ostringstream, so it all goes in one operation, to
  186. // help out the interleaving from multiple threads.
  187. ostringstream logout;
  188. logout << "sent: " << *doc << "\n";
  189. logfile << logout.str() << flush;
  190. }
  191. }
  192. ////////////////////////////////////////////////////////////////////
  193. // Function: read_xml
  194. // Description: Reads a TinyXml document from the given stream, and
  195. // returns it. If the document is not yet available,
  196. // blocks until it is, or until there is an error
  197. // condition on the input.
  198. //
  199. // The return value is NULL if there is an error, or the
  200. // newly-allocated document if it is successfully read.
  201. // If not NULL, the document has been allocated with
  202. // new, and should be eventually freed by the caller
  203. // with delete.
  204. ////////////////////////////////////////////////////////////////////
  205. TiXmlDocument *
  206. read_xml(istream &in, ostream &logfile) {
  207. #if DO_BINARY_XML
  208. // binary read.
  209. size_t buffer_length = 128;
  210. char *buffer = new char[buffer_length];
  211. TiXmlNode *xnode = read_xml_node(in, buffer, buffer_length);
  212. delete[] buffer;
  213. if (xnode == NULL) {
  214. return NULL;
  215. }
  216. TiXmlDocument *doc = xnode->ToDocument();
  217. assert(doc != NULL);
  218. #else
  219. // standard ASCII read.
  220. TiXmlDocument *doc = new TiXmlDocument;
  221. in >> *doc;
  222. #endif
  223. if (debug_xml_output) {
  224. // Write via ostringstream, so it all goes in one operation, to
  225. // help out the interleaving from multiple threads.
  226. ostringstream logout;
  227. logout << "received: " << *doc << "\n";
  228. logfile << logout.str() << flush;
  229. }
  230. return doc;
  231. }