PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/ginac/archive.cpp

https://bitbucket.org/titusnicolae/pynac
C++ | 638 lines | 440 code | 83 blank | 115 comment | 105 complexity | 8b76c4922fe9889fbd858873bd5e3351 MD5 | raw file
Possible License(s): GPL-2.0
  1. /** @file archive.cpp
  2. *
  3. * Archiving of GiNaC expressions. */
  4. /*
  5. * GiNaC Copyright (C) 1999-2008 Johannes Gutenberg University Mainz, Germany
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. */
  21. #include "archive.h"
  22. #include "registrar.h"
  23. #include "ex.h"
  24. #include "lst.h"
  25. #include "config.h"
  26. #include "tostring.h"
  27. #include <iostream>
  28. #include <stdexcept>
  29. namespace GiNaC {
  30. void archive::archive_ex(const ex &e, const char *name)
  31. {
  32. // Create root node (which recursively archives the whole expression tree)
  33. // and add it to the archive
  34. archive_node_id id = add_node(archive_node(*this, e));
  35. // Add root node ID to list of archived expressions
  36. archived_ex ae = archived_ex(atomize(name), id);
  37. exprs.push_back(ae);
  38. }
  39. /** Add archive_node to archive if the corresponding expression is
  40. * not already archived.
  41. * @return ID of archived node */
  42. archive_node_id archive::add_node(const archive_node &n)
  43. {
  44. // Look if expression is known to be in some node already.
  45. if (n.has_ex()) {
  46. mapit i = exprtable.find(n.get_ex());
  47. if (i != exprtable.end())
  48. return i->second;
  49. nodes.push_back(n);
  50. exprtable[n.get_ex()] = nodes.size() - 1;
  51. return nodes.size() - 1;
  52. }
  53. // Not found, add archive_node to nodes vector
  54. nodes.push_back(n);
  55. return nodes.size()-1;
  56. }
  57. /** Retrieve archive_node by ID. */
  58. archive_node &archive::get_node(archive_node_id id)
  59. {
  60. if (id >= nodes.size())
  61. throw (std::range_error("archive::get_node(): archive node ID out of range"));
  62. return nodes[id];
  63. }
  64. ex archive::unarchive_ex(const lst &sym_lst, const char *name) const
  65. {
  66. // Find root node
  67. std::string name_string = name;
  68. archive_atom id = atomize(name_string);
  69. std::vector<archived_ex>::const_iterator i = exprs.begin(), iend = exprs.end();
  70. while (i != iend) {
  71. if (i->name == id)
  72. goto found;
  73. i++;
  74. }
  75. throw (std::runtime_error("expression with name '" + name_string + "' not found in archive"));
  76. found:
  77. // Recursively unarchive all nodes, starting at the root node
  78. lst sym_lst_copy = sym_lst;
  79. return nodes[i->root].unarchive(sym_lst_copy);
  80. }
  81. ex archive::unarchive_ex(const lst &sym_lst, unsigned index) const
  82. {
  83. if (index >= exprs.size())
  84. throw (std::range_error("index of archived expression out of range"));
  85. // Recursively unarchive all nodes, starting at the root node
  86. lst sym_lst_copy = sym_lst;
  87. return nodes[exprs[index].root].unarchive(sym_lst_copy);
  88. }
  89. ex archive::unarchive_ex(const lst &sym_lst, std::string &name, unsigned index) const
  90. {
  91. if (index >= exprs.size())
  92. throw (std::range_error("index of archived expression out of range"));
  93. // Return expression name
  94. name = unatomize(exprs[index].name);
  95. // Recursively unarchive all nodes, starting at the root node
  96. lst sym_lst_copy = sym_lst;
  97. return nodes[exprs[index].root].unarchive(sym_lst_copy);
  98. }
  99. unsigned archive::num_expressions() const
  100. {
  101. return exprs.size();
  102. }
  103. const archive_node &archive::get_top_node(unsigned index) const
  104. {
  105. if (index >= exprs.size())
  106. throw (std::range_error("index of archived expression out of range"));
  107. return nodes[exprs[index].root];
  108. }
  109. /*
  110. * Archive file format
  111. *
  112. * - 4 bytes signature 'GARC'
  113. * - unsigned version number
  114. * - unsigned number of atoms
  115. * - atom strings (each zero-terminated)
  116. * - unsigned number of expressions
  117. * - unsigned name atom
  118. * - unsigned root node ID
  119. * - unsigned number of nodes
  120. * - unsigned number of properties
  121. * - unsigned containing type (PTYPE_*) in its lower 3 bits and
  122. * name atom in the upper bits
  123. * - unsigned property value
  124. *
  125. * Unsigned quantities are stored in a compressed format:
  126. * - numbers in the range 0x00..0x7f are stored verbatim (1 byte)
  127. * - numbers larger than 0x7f are stored in 7-bit packets (1 byte per
  128. * packet), starting with the LSBs; all bytes except the last one have
  129. * their upper bit set
  130. *
  131. * Examples:
  132. * 0x00 = 0x00
  133. * .. ..
  134. * 0x7f = 0x7f
  135. * 0x80 0x01 = 0x80
  136. * .. .. ..
  137. * 0xff 0x01 = 0xff
  138. * 0x80 0x02 = 0x100
  139. * .. .. ..
  140. * 0xff 0x02 = 0x17f
  141. * 0x80 0x03 = 0x180
  142. * .. .. ..
  143. * 0xff 0x7f = 0x3fff
  144. * 0x80 0x80 0x01 = 0x4000
  145. * .. .. .. ..
  146. */
  147. /** Write unsigned integer quantity to stream. */
  148. static void write_unsigned(std::ostream &os, unsigned val)
  149. {
  150. while (val >= 0x80) {
  151. os.put((val & 0x7f) | 0x80);
  152. val >>= 7;
  153. }
  154. os.put(val);
  155. }
  156. /** Read unsigned integer quantity from stream. */
  157. static unsigned read_unsigned(std::istream &is)
  158. {
  159. unsigned char b;
  160. unsigned ret = 0;
  161. unsigned shift = 0;
  162. do {
  163. char b2;
  164. is.get(b2);
  165. b = b2;
  166. ret |= (b & 0x7f) << shift;
  167. shift += 7;
  168. } while (b & 0x80);
  169. return ret;
  170. }
  171. /** Write archive_node to binary data stream. */
  172. std::ostream &operator<<(std::ostream &os, const archive_node &n)
  173. {
  174. // Write properties
  175. unsigned num_props = n.props.size();
  176. write_unsigned(os, num_props);
  177. for (unsigned i=0; i<num_props; i++) {
  178. write_unsigned(os, n.props[i].type | (n.props[i].name << 3));
  179. write_unsigned(os, n.props[i].value);
  180. }
  181. return os;
  182. }
  183. /** Write archive to binary data stream. */
  184. std::ostream &operator<<(std::ostream &os, const archive &ar)
  185. {
  186. // Write header
  187. os.put('G'); // Signature
  188. os.put('A');
  189. os.put('R');
  190. os.put('C');
  191. write_unsigned(os, ARCHIVE_VERSION);
  192. // Write atoms
  193. unsigned num_atoms = ar.atoms.size();
  194. write_unsigned(os, num_atoms);
  195. for (unsigned i=0; i<num_atoms; i++)
  196. os << ar.atoms[i] << std::ends;
  197. // Write expressions
  198. unsigned num_exprs = ar.exprs.size();
  199. write_unsigned(os, num_exprs);
  200. for (unsigned i=0; i<num_exprs; i++) {
  201. write_unsigned(os, ar.exprs[i].name);
  202. write_unsigned(os, ar.exprs[i].root);
  203. }
  204. // Write nodes
  205. unsigned num_nodes = ar.nodes.size();
  206. write_unsigned(os, num_nodes);
  207. for (unsigned i=0; i<num_nodes; i++)
  208. os << ar.nodes[i];
  209. return os;
  210. }
  211. /** Read archive_node from binary data stream. */
  212. std::istream &operator>>(std::istream &is, archive_node &n)
  213. {
  214. // Read properties
  215. unsigned num_props = read_unsigned(is);
  216. n.props.resize(num_props);
  217. for (unsigned i=0; i<num_props; i++) {
  218. unsigned name_type = read_unsigned(is);
  219. n.props[i].type = (archive_node::property_type)(name_type & 7);
  220. n.props[i].name = name_type >> 3;
  221. n.props[i].value = read_unsigned(is);
  222. }
  223. return is;
  224. }
  225. /** Read archive from binary data stream. */
  226. std::istream &operator>>(std::istream &is, archive &ar)
  227. {
  228. // Read header
  229. char c1, c2, c3, c4;
  230. is.get(c1); is.get(c2); is.get(c3); is.get(c4);
  231. if (c1 != 'G' || c2 != 'A' || c3 != 'R' || c4 != 'C')
  232. throw (std::runtime_error("not a GiNaC archive (signature not found)"));
  233. unsigned version = read_unsigned(is);
  234. if (version > ARCHIVE_VERSION || version < ARCHIVE_VERSION - ARCHIVE_AGE)
  235. throw (std::runtime_error("archive version " + ToString(version) + " cannot be read by this GiNaC library (which supports versions " + ToString(ARCHIVE_VERSION-ARCHIVE_AGE) + " thru " + ToString(ARCHIVE_VERSION)));
  236. // Read atoms
  237. unsigned num_atoms = read_unsigned(is);
  238. ar.atoms.resize(num_atoms);
  239. for (unsigned i=0; i<num_atoms; i++) {
  240. getline(is, ar.atoms[i], '\0');
  241. ar.inverse_atoms[ar.atoms[i]] = i;
  242. }
  243. // Read expressions
  244. unsigned num_exprs = read_unsigned(is);
  245. ar.exprs.resize(num_exprs);
  246. for (unsigned i=0; i<num_exprs; i++) {
  247. archive_atom name = read_unsigned(is);
  248. archive_node_id root = read_unsigned(is);
  249. ar.exprs[i] = archive::archived_ex(name, root);
  250. }
  251. // Read nodes
  252. unsigned num_nodes = read_unsigned(is);
  253. ar.nodes.resize(num_nodes, ar);
  254. for (unsigned i=0; i<num_nodes; i++)
  255. is >> ar.nodes[i];
  256. return is;
  257. }
  258. /** Atomize a string (i.e. convert it into an ID number that uniquely
  259. * represents the string). */
  260. archive_atom archive::atomize(const std::string &s) const
  261. {
  262. // Search for string in inverse_atoms map.
  263. inv_at_cit i = inverse_atoms.find(s);
  264. if (i!=inverse_atoms.end())
  265. return i->second;
  266. // Not found, add to atoms vector
  267. archive_atom id = atoms.size();
  268. atoms.push_back(s);
  269. inverse_atoms[s] = id;
  270. return id;
  271. }
  272. /** Unatomize a string (i.e. convert the ID number back to the string). */
  273. const std::string &archive::unatomize(archive_atom id) const
  274. {
  275. if (id >= atoms.size())
  276. throw (std::range_error("archive::unatomizee(): atom ID out of range"));
  277. return atoms[id];
  278. }
  279. /** Assignment operator of archive_node. */
  280. const archive_node &archive_node::operator=(const archive_node &other)
  281. {
  282. if (this != &other) {
  283. // archive &a member doesn't get copied
  284. props = other.props;
  285. has_expression = other.has_expression;
  286. e = other.e;
  287. }
  288. return *this;
  289. }
  290. /** Recursively construct archive node from expression. */
  291. archive_node::archive_node(archive &ar, const ex &expr)
  292. : a(ar), has_expression(true), e(expr)
  293. {
  294. expr.bp->archive(*this);
  295. }
  296. /** Check if the archive_node stores the same expression as another
  297. * archive_node.
  298. * @return "true" if expressions are the same */
  299. bool archive_node::has_same_ex_as(const archive_node &other) const
  300. {
  301. if (!has_expression || !other.has_expression)
  302. return false;
  303. return e.bp == other.e.bp;
  304. }
  305. archive_node::archive_node_cit
  306. archive_node::find_first(const std::string &name) const
  307. {
  308. archive_atom name_atom = a.atomize(name);
  309. for (archive_node_cit i=props.begin(); i!=props.end(); ++i)
  310. if (i->name == name_atom)
  311. return i;
  312. return props.end();;
  313. }
  314. archive_node::archive_node_cit
  315. archive_node::find_last(const std::string &name) const
  316. {
  317. archive_atom name_atom = a.atomize(name);
  318. for (archive_node_cit i=props.end(); i!=props.begin();) {
  319. --i;
  320. if (i->name == name_atom)
  321. return i;
  322. }
  323. return props.end();
  324. }
  325. void archive_node::add_bool(const std::string &name, bool value)
  326. {
  327. props.push_back(property(a.atomize(name), PTYPE_BOOL, value));
  328. }
  329. void archive_node::add_unsigned(const std::string &name, unsigned value)
  330. {
  331. props.push_back(property(a.atomize(name), PTYPE_UNSIGNED, value));
  332. }
  333. void archive_node::add_string(const std::string &name, const std::string &value)
  334. {
  335. props.push_back(property(a.atomize(name), PTYPE_STRING, a.atomize(value)));
  336. }
  337. void archive_node::add_ex(const std::string &name, const ex &value)
  338. {
  339. // Recursively create an archive_node and add its ID to the properties of this node
  340. archive_node_id id = a.add_node(archive_node(a, value));
  341. props.push_back(property(a.atomize(name), PTYPE_NODE, id));
  342. }
  343. bool archive_node::find_bool(const std::string &name, bool &ret, unsigned index) const
  344. {
  345. archive_atom name_atom = a.atomize(name);
  346. archive_node_cit i = props.begin(), iend = props.end();
  347. unsigned found_index = 0;
  348. while (i != iend) {
  349. if (i->type == PTYPE_BOOL && i->name == name_atom) {
  350. if (found_index == index) {
  351. ret = i->value;
  352. return true;
  353. }
  354. found_index++;
  355. }
  356. i++;
  357. }
  358. return false;
  359. }
  360. bool archive_node::find_unsigned(const std::string &name, unsigned &ret, unsigned index) const
  361. {
  362. archive_atom name_atom = a.atomize(name);
  363. archive_node_cit i = props.begin(), iend = props.end();
  364. unsigned found_index = 0;
  365. while (i != iend) {
  366. if (i->type == PTYPE_UNSIGNED && i->name == name_atom) {
  367. if (found_index == index) {
  368. ret = i->value;
  369. return true;
  370. }
  371. found_index++;
  372. }
  373. i++;
  374. }
  375. return false;
  376. }
  377. bool archive_node::find_string(const std::string &name, std::string &ret, unsigned index) const
  378. {
  379. archive_atom name_atom = a.atomize(name);
  380. archive_node_cit i = props.begin(), iend = props.end();
  381. unsigned found_index = 0;
  382. while (i != iend) {
  383. if (i->type == PTYPE_STRING && i->name == name_atom) {
  384. if (found_index == index) {
  385. ret = a.unatomize(i->value);
  386. return true;
  387. }
  388. found_index++;
  389. }
  390. i++;
  391. }
  392. return false;
  393. }
  394. void archive_node::find_ex_by_loc(archive_node_cit loc, ex &ret, lst &sym_lst)
  395. const
  396. {
  397. ret = a.get_node(loc->value).unarchive(sym_lst);
  398. }
  399. bool archive_node::find_ex(const std::string &name, ex &ret, lst &sym_lst, unsigned index) const
  400. {
  401. archive_atom name_atom = a.atomize(name);
  402. archive_node_cit i = props.begin(), iend = props.end();
  403. unsigned found_index = 0;
  404. while (i != iend) {
  405. if (i->type == PTYPE_NODE && i->name == name_atom) {
  406. if (found_index == index) {
  407. ret = a.get_node(i->value).unarchive(sym_lst);
  408. return true;
  409. }
  410. found_index++;
  411. }
  412. i++;
  413. }
  414. return false;
  415. }
  416. const archive_node &archive_node::find_ex_node(const std::string &name, unsigned index) const
  417. {
  418. archive_atom name_atom = a.atomize(name);
  419. archive_node_cit i = props.begin(), iend = props.end();
  420. unsigned found_index = 0;
  421. while (i != iend) {
  422. if (i->type == PTYPE_NODE && i->name == name_atom) {
  423. if (found_index == index)
  424. return a.get_node(i->value);
  425. found_index++;
  426. }
  427. i++;
  428. }
  429. throw (std::runtime_error("property with name '" + name + "' not found in archive node"));
  430. }
  431. void archive_node::get_properties(propinfovector &v) const
  432. {
  433. v.clear();
  434. archive_node_cit i = props.begin(), iend = props.end();
  435. while (i != iend) {
  436. property_type type = i->type;
  437. std::string name = a.unatomize(i->name);
  438. propinfovector::iterator a = v.begin(), aend = v.end();
  439. bool found = false;
  440. while (a != aend) {
  441. if (a->type == type && a->name == name) {
  442. a->count++;
  443. found = true;
  444. break;
  445. }
  446. ++a;
  447. }
  448. if (!found)
  449. v.push_back(property_info(type, name));
  450. i++;
  451. }
  452. }
  453. /** Convert archive node to GiNaC expression. */
  454. ex archive_node::unarchive(lst &sym_lst) const
  455. {
  456. // Already unarchived? Then return cached unarchived expression.
  457. if (has_expression)
  458. return e;
  459. // Find instantiation function for class specified in node
  460. std::string class_name;
  461. if (!find_string("class", class_name))
  462. throw (std::runtime_error("archive node contains no class name"));
  463. unarch_func f = find_unarch_func(class_name);
  464. // Call instantiation function
  465. e = f(*this, sym_lst);
  466. has_expression = true;
  467. return e;
  468. }
  469. void archive::clear()
  470. {
  471. atoms.clear();
  472. inverse_atoms.clear();
  473. exprs.clear();
  474. nodes.clear();
  475. exprtable.clear();
  476. }
  477. /** Delete cached unarchived expressions in all archive_nodes (mainly for debugging). */
  478. void archive::forget()
  479. {
  480. for_each(nodes.begin(), nodes.end(), std::mem_fun_ref(&archive_node::forget));
  481. }
  482. /** Delete cached unarchived expressions from node (for debugging). */
  483. void archive_node::forget()
  484. {
  485. has_expression = false;
  486. e = 0;
  487. }
  488. /** Print archive to stream in ugly raw format (for debugging). */
  489. void archive::printraw(std::ostream &os) const
  490. {
  491. // Dump atoms
  492. os << "Atoms:\n";
  493. {
  494. std::vector<std::string>::const_iterator i = atoms.begin(), iend = atoms.end();
  495. archive_atom id = 0;
  496. while (i != iend) {
  497. os << " " << id << " " << *i << std::endl;
  498. i++; id++;
  499. }
  500. }
  501. os << std::endl;
  502. // Dump expressions
  503. os << "Expressions:\n";
  504. {
  505. std::vector<archived_ex>::const_iterator i = exprs.begin(), iend = exprs.end();
  506. unsigned index = 0;
  507. while (i != iend) {
  508. os << " " << index << " \"" << unatomize(i->name) << "\" root node " << i->root << std::endl;
  509. i++; index++;
  510. }
  511. }
  512. os << std::endl;
  513. // Dump nodes
  514. os << "Nodes:\n";
  515. {
  516. std::vector<archive_node>::const_iterator i = nodes.begin(), iend = nodes.end();
  517. archive_node_id id = 0;
  518. while (i != iend) {
  519. os << " " << id << " ";
  520. i->printraw(os);
  521. i++; id++;
  522. }
  523. }
  524. }
  525. /** Output archive_node to stream in ugly raw format (for debugging). */
  526. void archive_node::printraw(std::ostream &os) const
  527. {
  528. // Dump cached unarchived expression
  529. if (has_expression)
  530. os << "(basic * " << e.bp << " = " << e << ")\n";
  531. else
  532. os << "\n";
  533. // Dump properties
  534. archive_node_cit i = props.begin(), iend = props.end();
  535. while (i != iend) {
  536. os << " ";
  537. switch (i->type) {
  538. case PTYPE_BOOL: os << "bool"; break;
  539. case PTYPE_UNSIGNED: os << "unsigned"; break;
  540. case PTYPE_STRING: os << "string"; break;
  541. case PTYPE_NODE: os << "node"; break;
  542. default: os << "<unknown>"; break;
  543. }
  544. os << " \"" << a.unatomize(i->name) << "\" " << i->value << std::endl;
  545. i++;
  546. }
  547. }
  548. /** Create a dummy archive. The intention is to fill archive_node's default
  549. * ctor, which is currently a Cint-requirement. */
  550. archive* archive_node::dummy_ar_creator()
  551. {
  552. static archive* some_ar = new archive;
  553. return some_ar;
  554. }
  555. } // namespace GiNaC