GiNaC  1.6.2
archive.cpp
Go to the documentation of this file.
00001 
00005 /*
00006  *  GiNaC Copyright (C) 1999-2011 Johannes Gutenberg University Mainz, Germany
00007  *
00008  *  This program is free software; you can redistribute it and/or modify
00009  *  it under the terms of the GNU General Public License as published by
00010  *  the Free Software Foundation; either version 2 of the License, or
00011  *  (at your option) any later version.
00012  *
00013  *  This program is distributed in the hope that it will be useful,
00014  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  *  GNU General Public License for more details.
00017  *
00018  *  You should have received a copy of the GNU General Public License
00019  *  along with this program; if not, write to the Free Software
00020  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00021  */
00022 
00023 #include "archive.h"
00024 #include "registrar.h"
00025 #include "ex.h"
00026 #include "lst.h"
00027 #include "config.h"
00028 #include "tostring.h"
00029 
00030 #include <iostream>
00031 #include <stdexcept>
00032 
00033 namespace GiNaC {
00034 
00035 
00036 void archive::archive_ex(const ex &e, const char *name)
00037 {
00038     // Create root node (which recursively archives the whole expression tree)
00039     // and add it to the archive
00040     archive_node_id id = add_node(archive_node(*this, e));
00041 
00042     // Add root node ID to list of archived expressions
00043     archived_ex ae = archived_ex(atomize(name), id);
00044     exprs.push_back(ae);
00045 }
00046 
00047 
00051 archive_node_id archive::add_node(const archive_node &n)
00052 {
00053     // Look if expression is known to be in some node already.
00054     if (n.has_ex()) {
00055         mapit i = exprtable.find(n.get_ex());
00056         if (i != exprtable.end())
00057             return i->second;
00058         nodes.push_back(n);
00059         exprtable[n.get_ex()] = nodes.size() - 1;
00060         return nodes.size() - 1;
00061     }
00062 
00063     // Not found, add archive_node to nodes vector
00064     nodes.push_back(n);
00065     return nodes.size()-1;
00066 }
00067 
00068 
00070 archive_node &archive::get_node(archive_node_id id)
00071 {
00072     if (id >= nodes.size())
00073         throw (std::range_error("archive::get_node(): archive node ID out of range"));
00074 
00075     return nodes[id];
00076 }
00077 
00078 
00079 ex archive::unarchive_ex(const lst &sym_lst, const char *name) const
00080 {
00081     // Find root node
00082     std::string name_string = name;
00083     archive_atom id = atomize(name_string);
00084     std::vector<archived_ex>::const_iterator i = exprs.begin(), iend = exprs.end();
00085     while (i != iend) {
00086         if (i->name == id)
00087             goto found;
00088         i++;
00089     }
00090     throw (std::runtime_error("expression with name '" + name_string + "' not found in archive"));
00091 
00092 found:
00093     // Recursively unarchive all nodes, starting at the root node
00094     lst sym_lst_copy = sym_lst;
00095     return nodes[i->root].unarchive(sym_lst_copy);
00096 }
00097 
00098 ex archive::unarchive_ex(const lst &sym_lst, unsigned index) const
00099 {
00100     if (index >= exprs.size())
00101         throw (std::range_error("index of archived expression out of range"));
00102 
00103     // Recursively unarchive all nodes, starting at the root node
00104     lst sym_lst_copy = sym_lst;
00105     return nodes[exprs[index].root].unarchive(sym_lst_copy);
00106 }
00107 
00108 ex archive::unarchive_ex(const lst &sym_lst, std::string &name, unsigned index) const
00109 {
00110     if (index >= exprs.size())
00111         throw (std::range_error("index of archived expression out of range"));
00112 
00113     // Return expression name
00114     name = unatomize(exprs[index].name);
00115 
00116     // Recursively unarchive all nodes, starting at the root node
00117     lst sym_lst_copy = sym_lst;
00118     return nodes[exprs[index].root].unarchive(sym_lst_copy);
00119 }
00120 
00121 unsigned archive::num_expressions() const
00122 {
00123     return exprs.size();
00124 }
00125 
00126 const archive_node &archive::get_top_node(unsigned index) const
00127 {
00128     if (index >= exprs.size())
00129         throw (std::range_error("index of archived expression out of range"));
00130 
00131     return nodes[exprs[index].root];
00132 }
00133 
00134 
00135 /*
00136  *  Archive file format
00137  *
00138  *   - 4 bytes signature 'GARC'
00139  *   - unsigned version number
00140  *   - unsigned number of atoms
00141  *      - atom strings (each zero-terminated)
00142  *   - unsigned number of expressions
00143  *      - unsigned name atom
00144  *      - unsigned root node ID
00145  *   - unsigned number of nodes
00146  *      - unsigned number of properties
00147  *        - unsigned containing type (PTYPE_*) in its lower 3 bits and
00148  *          name atom in the upper bits
00149  *        - unsigned property value
00150  *
00151  *  Unsigned quantities are stored in a compressed format:
00152  *   - numbers in the range 0x00..0x7f are stored verbatim (1 byte)
00153  *   - numbers larger than 0x7f are stored in 7-bit packets (1 byte per
00154  *     packet), starting with the LSBs; all bytes except the last one have
00155  *     their upper bit set
00156  *
00157  *  Examples:
00158  *   0x00           =   0x00
00159  *    ..                 ..
00160  *   0x7f           =   0x7f
00161  *   0x80 0x01      =   0x80
00162  *    ..   ..            ..
00163  *   0xff 0x01      =   0xff
00164  *   0x80 0x02      =  0x100
00165  *    ..   ..            ..
00166  *   0xff 0x02      =  0x17f
00167  *   0x80 0x03      =  0x180
00168  *    ..   ..            ..
00169  *   0xff 0x7f      = 0x3fff
00170  *   0x80 0x80 0x01 = 0x4000
00171  *    ..   ..   ..       ..
00172  */
00173 
00175 static void write_unsigned(std::ostream &os, unsigned val)
00176 {
00177     while (val >= 0x80) {
00178         os.put((val & 0x7f) | 0x80);
00179         val >>= 7;
00180     }
00181     os.put(val);
00182 }
00183 
00185 static unsigned read_unsigned(std::istream &is)
00186 {
00187     unsigned char b;
00188     unsigned ret = 0;
00189     unsigned shift = 0;
00190     do {
00191         char b2;
00192         is.get(b2);
00193         b = b2;
00194         ret |= (b & 0x7f) << shift;
00195         shift += 7;
00196     } while (b & 0x80);
00197     return ret;
00198 }
00199 
00201 std::ostream &operator<<(std::ostream &os, const archive_node &n)
00202 {
00203     // Write properties
00204     unsigned num_props = n.props.size();
00205     write_unsigned(os, num_props);
00206     for (unsigned i=0; i<num_props; i++) {
00207         write_unsigned(os, n.props[i].type | (n.props[i].name << 3));
00208         write_unsigned(os, n.props[i].value);
00209     }
00210     return os;
00211 }
00212 
00214 std::ostream &operator<<(std::ostream &os, const archive &ar)
00215 {
00216     // Write header
00217     os.put('G');    // Signature
00218     os.put('A');
00219     os.put('R');
00220     os.put('C');
00221     write_unsigned(os, ARCHIVE_VERSION);
00222 
00223     // Write atoms
00224     unsigned num_atoms = ar.atoms.size();
00225     write_unsigned(os, num_atoms);
00226     for (unsigned i=0; i<num_atoms; i++)
00227         os << ar.atoms[i] << std::ends;
00228 
00229     // Write expressions
00230     unsigned num_exprs = ar.exprs.size();
00231     write_unsigned(os, num_exprs);
00232     for (unsigned i=0; i<num_exprs; i++) {
00233         write_unsigned(os, ar.exprs[i].name);
00234         write_unsigned(os, ar.exprs[i].root);
00235     }
00236 
00237     // Write nodes
00238     unsigned num_nodes = ar.nodes.size();
00239     write_unsigned(os, num_nodes);
00240     for (unsigned i=0; i<num_nodes; i++)
00241         os << ar.nodes[i];
00242     return os;
00243 }
00244 
00246 std::istream &operator>>(std::istream &is, archive_node &n)
00247 {
00248     // Read properties
00249     unsigned num_props = read_unsigned(is);
00250     n.props.resize(num_props);
00251     for (unsigned i=0; i<num_props; i++) {
00252         unsigned name_type = read_unsigned(is);
00253         n.props[i].type = (archive_node::property_type)(name_type & 7);
00254         n.props[i].name = name_type >> 3;
00255         n.props[i].value = read_unsigned(is);
00256     }
00257     return is;
00258 }
00259 
00261 std::istream &operator>>(std::istream &is, archive &ar)
00262 {
00263     // Read header
00264     char c1, c2, c3, c4;
00265     is.get(c1); is.get(c2); is.get(c3); is.get(c4);
00266     if (c1 != 'G' || c2 != 'A' || c3 != 'R' || c4 != 'C')
00267         throw (std::runtime_error("not a GiNaC archive (signature not found)"));
00268     unsigned version = read_unsigned(is);
00269     if (version > ARCHIVE_VERSION || version < ARCHIVE_VERSION - ARCHIVE_AGE)
00270         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)));
00271 
00272     // Read atoms
00273     unsigned num_atoms = read_unsigned(is);
00274     ar.atoms.resize(num_atoms);
00275     for (unsigned i=0; i<num_atoms; i++) {
00276         getline(is, ar.atoms[i], '\0');
00277         ar.inverse_atoms[ar.atoms[i]] = i;
00278     }
00279 
00280     // Read expressions
00281     unsigned num_exprs = read_unsigned(is);
00282     ar.exprs.resize(num_exprs);
00283     for (unsigned i=0; i<num_exprs; i++) {
00284         archive_atom name = read_unsigned(is);
00285         archive_node_id root = read_unsigned(is);
00286         ar.exprs[i] = archive::archived_ex(name, root);
00287     }
00288 
00289     // Read nodes
00290     unsigned num_nodes = read_unsigned(is);
00291     ar.nodes.resize(num_nodes, ar);
00292     for (unsigned i=0; i<num_nodes; i++)
00293         is >> ar.nodes[i];
00294     return is;
00295 }
00296 
00297 
00300 archive_atom archive::atomize(const std::string &s) const
00301 {
00302     // Search for string in inverse_atoms map.
00303     inv_at_cit i = inverse_atoms.find(s);
00304     if (i!=inverse_atoms.end())
00305         return i->second;
00306 
00307     // Not found, add to atoms vector
00308     archive_atom id = atoms.size();
00309     atoms.push_back(s);
00310     inverse_atoms[s] = id;
00311     return id;
00312 }
00313 
00315 const std::string &archive::unatomize(archive_atom id) const
00316 {
00317     if (id >= atoms.size())
00318         throw (std::range_error("archive::unatomizee(): atom ID out of range"));
00319 
00320     return atoms[id];
00321 }
00322 
00323 
00325 const archive_node &archive_node::operator=(const archive_node &other)
00326 {
00327     if (this != &other) {
00328         // archive &a member doesn't get copied
00329         props = other.props;
00330         has_expression = other.has_expression;
00331         e = other.e;
00332     }
00333     return *this;
00334 }
00335 
00336 
00338 archive_node::archive_node(archive &ar, const ex &expr)
00339   : a(ar), has_expression(true), e(expr)
00340 {
00341     expr.bp->archive(*this);
00342 }
00343 
00344 
00348 bool archive_node::has_same_ex_as(const archive_node &other) const
00349 {
00350     if (!has_expression || !other.has_expression)
00351         return false;
00352     return e.bp == other.e.bp;
00353 }
00354 
00355 archive_node::archive_node_cit
00356         archive_node::find_first(const std::string &name) const
00357 {   
00358     archive_atom name_atom = a.atomize(name);
00359     for (archive_node_cit i=props.begin(); i!=props.end(); ++i)
00360         if (i->name == name_atom)
00361             return i;
00362     return props.end();;
00363 }
00364 
00365 archive_node::archive_node_cit
00366         archive_node::find_last(const std::string &name) const
00367 {
00368     archive_atom name_atom = a.atomize(name);
00369     for (archive_node_cit i=props.end(); i!=props.begin();) {
00370         --i;
00371         if (i->name == name_atom)
00372             return i;
00373     }
00374     return props.end();
00375 }
00376 
00377 void archive_node::add_bool(const std::string &name, bool value)
00378 {
00379     props.push_back(property(a.atomize(name), PTYPE_BOOL, value));
00380 }
00381 
00382 void archive_node::add_unsigned(const std::string &name, unsigned value)
00383 {
00384     props.push_back(property(a.atomize(name), PTYPE_UNSIGNED, value));
00385 }
00386 
00387 void archive_node::add_string(const std::string &name, const std::string &value)
00388 {
00389     props.push_back(property(a.atomize(name), PTYPE_STRING, a.atomize(value)));
00390 }
00391 
00392 void archive_node::add_ex(const std::string &name, const ex &value)
00393 {
00394     // Recursively create an archive_node and add its ID to the properties of this node
00395     archive_node_id id = a.add_node(archive_node(a, value));
00396     props.push_back(property(a.atomize(name), PTYPE_NODE, id));
00397 }
00398 
00399 
00400 bool archive_node::find_bool(const std::string &name, bool &ret, unsigned index) const
00401 {
00402     archive_atom name_atom = a.atomize(name);
00403     archive_node_cit i = props.begin(), iend = props.end();
00404     unsigned found_index = 0;
00405     while (i != iend) {
00406         if (i->type == PTYPE_BOOL && i->name == name_atom) {
00407             if (found_index == index) {
00408                 ret = i->value;
00409                 return true;
00410             }
00411             found_index++;
00412         }
00413         i++;
00414     }
00415     return false;
00416 }
00417 
00418 bool archive_node::find_unsigned(const std::string &name, unsigned &ret, unsigned index) const
00419 {
00420     archive_atom name_atom = a.atomize(name);
00421     archive_node_cit i = props.begin(), iend = props.end();
00422     unsigned found_index = 0;
00423     while (i != iend) {
00424         if (i->type == PTYPE_UNSIGNED && i->name == name_atom) {
00425             if (found_index == index) {
00426                 ret = i->value;
00427                 return true;
00428             }
00429             found_index++;
00430         }
00431         i++;
00432     }
00433     return false;
00434 }
00435 
00436 bool archive_node::find_string(const std::string &name, std::string &ret, unsigned index) const
00437 {
00438     archive_atom name_atom = a.atomize(name);
00439     archive_node_cit i = props.begin(), iend = props.end();
00440     unsigned found_index = 0;
00441     while (i != iend) {
00442         if (i->type == PTYPE_STRING && i->name == name_atom) {
00443             if (found_index == index) {
00444                 ret = a.unatomize(i->value);
00445                 return true;
00446             }
00447             found_index++;
00448         }
00449         i++;
00450     }
00451     return false;
00452 }
00453 
00454 void archive_node::find_ex_by_loc(archive_node_cit loc, ex &ret, lst &sym_lst)
00455         const
00456 {
00457     ret = a.get_node(loc->value).unarchive(sym_lst);
00458 }
00459 
00460 bool archive_node::find_ex(const std::string &name, ex &ret, lst &sym_lst, unsigned index) const
00461 {
00462     archive_atom name_atom = a.atomize(name);
00463     archive_node_cit i = props.begin(), iend = props.end();
00464     unsigned found_index = 0;
00465     while (i != iend) {
00466         if (i->type == PTYPE_NODE && i->name == name_atom) {
00467             if (found_index == index) {
00468                 ret = a.get_node(i->value).unarchive(sym_lst);
00469                 return true;
00470             }
00471             found_index++;
00472         }
00473         i++;
00474     }
00475     return false;
00476 }
00477 
00478 const archive_node &archive_node::find_ex_node(const std::string &name, unsigned index) const
00479 {
00480     archive_atom name_atom = a.atomize(name);
00481     archive_node_cit i = props.begin(), iend = props.end();
00482     unsigned found_index = 0;
00483     while (i != iend) {
00484         if (i->type == PTYPE_NODE && i->name == name_atom) {
00485             if (found_index == index)
00486                 return a.get_node(i->value);
00487             found_index++;
00488         }
00489         i++;
00490     }
00491     throw (std::runtime_error("property with name '" + name + "' not found in archive node"));
00492 }
00493 
00494 
00495 void archive_node::get_properties(propinfovector &v) const
00496 {
00497     v.clear();
00498     archive_node_cit i = props.begin(), iend = props.end();
00499     while (i != iend) {
00500         property_type type = i->type;
00501         std::string name = a.unatomize(i->name);
00502 
00503         propinfovector::iterator a = v.begin(), aend = v.end();
00504         bool found = false;
00505         while (a != aend) {
00506             if (a->type == type && a->name == name) {
00507                 a->count++;
00508                 found = true;
00509                 break;
00510             }
00511             ++a;
00512         }
00513         if (!found)
00514             v.push_back(property_info(type, name));
00515         i++;
00516     }   
00517 }
00518 
00519 static synthesize_func find_factory_fcn(const std::string& name)
00520 {
00521     static unarchive_table_t the_table;
00522     synthesize_func ret = the_table.find(name);
00523     return ret;
00524 }
00525 
00527 ex archive_node::unarchive(lst &sym_lst) const
00528 {
00529     // Already unarchived? Then return cached unarchived expression.
00530     if (has_expression)
00531         return e;
00532 
00533     // Find instantiation function for class specified in node
00534     std::string class_name;
00535     if (!find_string("class", class_name))
00536         throw (std::runtime_error("archive node contains no class name"));
00537 
00538     // Call instantiation function
00539     synthesize_func factory_fcn = find_factory_fcn(class_name);
00540     ptr<basic> obj(factory_fcn());
00541     obj->setflag(status_flags::dynallocated);
00542     obj->read_archive(*this, sym_lst);
00543     e = ex(*obj);
00544     has_expression = true;
00545     return e;
00546 }
00547 
00548 int unarchive_table_t::usecount = 0;
00549 unarchive_map_t* unarchive_table_t::unarch_map = 0;
00550 
00551 unarchive_table_t::unarchive_table_t()
00552 {
00553     if (usecount == 0)
00554         unarch_map = new unarchive_map_t();
00555     ++usecount;
00556 }
00557 
00558 synthesize_func unarchive_table_t::find(const std::string& classname) const
00559 {
00560     unarchive_map_t::const_iterator i = unarch_map->find(classname);
00561     if (i != unarch_map->end())
00562         return i->second;
00563     throw std::runtime_error(std::string("no unarchiving function for \"")
00564             + classname + "\" class");
00565 }
00566 
00567 void unarchive_table_t::insert(const std::string& classname, synthesize_func f)
00568 {
00569     if (unarch_map->find(classname) != unarch_map->end())
00570         throw std::runtime_error(std::string("Class \"" + classname
00571                     + "\" is already registered"));
00572     unarch_map->operator[](classname) = f;
00573 }
00574 
00575 unarchive_table_t::~unarchive_table_t()
00576 {
00577     if (--usecount == 0)
00578         delete unarch_map;
00579 }
00580 
00581 
00582 void archive::clear()
00583 {
00584     atoms.clear();
00585     inverse_atoms.clear();
00586     exprs.clear();
00587     nodes.clear();
00588     exprtable.clear();
00589 }
00590 
00591 
00593 void archive::forget()
00594 {
00595     for_each(nodes.begin(), nodes.end(), std::mem_fun_ref(&archive_node::forget));
00596 }
00597 
00599 void archive_node::forget()
00600 {
00601     has_expression = false;
00602     e = 0;
00603 }
00604 
00605 
00607 void archive::printraw(std::ostream &os) const
00608 {
00609     // Dump atoms
00610     os << "Atoms:\n";
00611     {
00612         std::vector<std::string>::const_iterator i = atoms.begin(), iend = atoms.end();
00613         archive_atom id = 0;
00614         while (i != iend) {
00615             os << " " << id << " " << *i << std::endl;
00616             i++; id++;
00617         }
00618     }
00619     os << std::endl;
00620 
00621     // Dump expressions
00622     os << "Expressions:\n";
00623     {
00624         std::vector<archived_ex>::const_iterator i = exprs.begin(), iend = exprs.end();
00625         unsigned index = 0;
00626         while (i != iend) {
00627             os << " " << index << " \"" << unatomize(i->name) << "\" root node " << i->root << std::endl;
00628             i++; index++;
00629         }
00630     }
00631     os << std::endl;
00632 
00633     // Dump nodes
00634     os << "Nodes:\n";
00635     {
00636         std::vector<archive_node>::const_iterator i = nodes.begin(), iend = nodes.end();
00637         archive_node_id id = 0;
00638         while (i != iend) {
00639             os << " " << id << " ";
00640             i->printraw(os);
00641             i++; id++;
00642         }
00643     }
00644 }
00645 
00647 void archive_node::printraw(std::ostream &os) const
00648 {
00649     // Dump cached unarchived expression
00650     if (has_expression)
00651         os << "(basic * " << e.bp << " = " << e << ")\n";
00652     else
00653         os << "\n";
00654 
00655     // Dump properties
00656     archive_node_cit i = props.begin(), iend = props.end();
00657     while (i != iend) {
00658         os << "  ";
00659         switch (i->type) {
00660             case PTYPE_BOOL: os << "bool"; break;
00661             case PTYPE_UNSIGNED: os << "unsigned"; break;
00662             case PTYPE_STRING: os << "string"; break;
00663             case PTYPE_NODE: os << "node"; break;
00664             default: os << "<unknown>"; break;
00665         }
00666         os << " \"" << a.unatomize(i->name) << "\" " << i->value << std::endl;
00667         i++;
00668     }
00669 }
00670 
00673 archive* archive_node::dummy_ar_creator()
00674 {
00675     static archive* some_ar = new archive;
00676     return some_ar;
00677 }
00678 
00679 
00680 } // namespace GiNaC

This page is part of the GiNaC developer's reference. It was generated automatically by doxygen. For an introduction, see the tutorial.