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