|
GiNaC
1.6.2
|
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