// --------------------------------------------------------------------------- // - Librarian.cpp - // - afnix engine - librarian class implementation - // --------------------------------------------------------------------------- // - This program is free software; you can redistribute it and/or modify - // - it provided that this copyright notice is kept intact. - // - - // - This program is distributed in the hope that it will be useful, but - // - without any warranty; without even the implied warranty of - // - merchantability or fitness for a particular purpose. In no event shall - // - the copyright holder be liable for any direct, indirect, incidental or - // - special damages arising in any way out of the use of this software. - // --------------------------------------------------------------------------- // - copyright (c) 1999-2007 amaury darsch - // --------------------------------------------------------------------------- #include "Byte.hpp" #include "System.hpp" #include "Vector.hpp" #include "Integer.hpp" #include "Boolean.hpp" #include "Runnable.hpp" #include "Librarian.hpp" #include "QuarkZone.hpp" #include "Exception.hpp" #include "InputFile.hpp" #include "OutputFile.hpp" #include "InputMapped.hpp" namespace afnix { // ------------------------------------------------------------------------- // - private section - // ------------------------------------------------------------------------- // librarian constants const long AXL_MSIZE = 4; const t_byte AXL_MAGIC[4] = {'\377', 'A', 'X', 'L'}; const t_byte AXL_MAJOR = AFNIX_VERSION_MAJOR; const t_byte AXL_MINOR = AFNIX_VERSION_MINOR; const t_byte AXL_FLAGS = nilc; // librarian flags marking const t_byte AXL_DEF_MRK = '-'; const t_byte AXL_UNK_MRK = 'u'; // the librarian header struct s_lhead { t_byte d_magic[AXL_MSIZE]; t_byte d_major; t_byte d_minor; t_byte d_flags; t_octa d_hsize; // create an empty header for reading s_lhead (void) { for (long i = 0; i < AXL_MSIZE; i++) d_magic[i] = nilc; d_major = 0; d_minor = 0; d_flags = 0; d_hsize = 0; } // create a new header with a size for writing s_lhead (const long size) { for (long i = 0; i < AXL_MSIZE; i++) d_magic[i] = AXL_MAGIC[i]; d_major = AXL_MAJOR; d_minor = AXL_MINOR; d_flags = AXL_FLAGS; d_hsize = System::oswap (size); } // check this header bool check (void) { // check magic for (long i = 0; i < AXL_MSIZE; i++) if (d_magic[i] != AXL_MAGIC[i]) return false; // check major if (d_major != AXL_MAJOR) return false; // check minor if (d_minor > AXL_MINOR) return false; // look's ok return true; } }; // the file descriptor struct s_desc { // file path String d_fpath; // file name String d_fname; // the file size t_long d_fsize; // the coding size t_long d_csize; // the librarian offset t_long d_lfoff; // file flags t_byte d_flags; // next file in list s_desc* p_next; // create an empty descriptor s_desc (void) { d_fsize = 0; d_lfoff = 0; d_flags = 0; p_next = nilp; } // create a new descriptor by name, file size and computed size s_desc (const String& fpath, const t_long fsize, const t_long csize) { d_fpath = fpath; d_fname = System::xname (d_fpath); d_fsize = fsize; d_csize = csize; d_lfoff = 0; d_flags = nilc; p_next = nilp; } // delete a chain of descriptors ~s_desc (void) { delete p_next; } // append a descriptor at the end void append (s_desc* desc) { if (desc == nilp) return; s_desc* last = this; while (last->p_next != nilp) last = last->p_next; last->p_next = desc; } // return the serialized length long length (void) { long result = d_fname.length () + 1; result += 16 + 1; return result; } // serialize this descriptor void wrstream (Output& os) { Integer fsize = d_fsize; Integer csize = d_csize; Byte flags = d_flags; d_fname.wrstream (os); fsize.wrstream (os); csize.wrstream (os); flags.wrstream (os); } // deserialize this descriptor void rdstream (Input& is) { Integer fsize; Integer csize; Byte flags; d_fname.rdstream (is); fsize.rdstream (is); csize.rdstream (is); flags.rdstream (is); d_fpath = d_fname; d_fsize = fsize.tointeger (); d_csize = csize.tointeger (); d_flags = flags.tobyte (); } // return true if a flag is set bool chkflg (const t_byte flag) const { return (d_flags & flag) == flag; } // format the flags into a string String fmtflg (const t_byte ffmt[8]) const { String result; // format the string for (long i = 0; i < 8; i++) { if (chkflg (0x01 << i) == true) result = result + (char) ffmt[i]; else result = result + (char) AXL_DEF_MRK; } return result; } // format the file size into a string String fmtsiz (void) const { Integer ival (d_fsize); String result = ival.tostring (); return result.lfill (' ', 10); } // format a descriptor to an output stream void format (Output& os, const t_byte ffmt[8]) { os << fmtflg (ffmt) << ' ' << fmtsiz () << ' ' << d_fname << eolc; } }; // this procedure compute the size of descritpor chain static t_long get_chain_length (s_desc* desc) { t_long result = 0; while (desc != nilp) { result += desc->length (); desc = desc->p_next; } return result; } // this procedure finds a descriptor by name static s_desc* get_named_desc (s_desc* desc, const String& name) { while (desc != nilp) { if (desc->d_fname == name) return desc; desc = desc->p_next; } return nilp; } // write the header on the output stream static void write_header (Output& os, s_desc* desc) { // get the librarian header s_lhead lhead (get_chain_length (desc)); // write the librarian header os.write ((char*) &lhead, sizeof (lhead)); // serialize the chain while (desc != nilp) { desc->wrstream (os); desc = desc->p_next; } } // read the header from an input stream static s_desc* read_header (const String& lname) { InputFile is (lname); // read the librarian header s_lhead lhead; Buffer* buf = is.Input::read (sizeof (lhead)); if (buf->tomap (&lhead, sizeof (lhead)) != sizeof (lhead)) { delete buf; throw Exception ("librarian-error", "cannot read header"); } delete buf; // check the header if (lhead.check () == false) throw Exception ("librarian-error", "invalid librarian header"); // check the input size t_long hsize = System::oswap (lhead.d_hsize); t_long lfoff = hsize + sizeof (s_lhead); if (hsize == 0) return nilp; // prepare for reading s_desc* result = nilp; s_desc* last = nilp; // read until the size is null while (hsize != 0) { // read in one descriptor s_desc* desc = new s_desc; desc->rdstream (is); // update the file offset desc->d_lfoff = lfoff; lfoff += desc->d_csize; // update result and size if (last == nilp) { result = desc; last = desc; } else { last->p_next = desc; last = desc; } hsize -= desc->length (); if (hsize < 0) { delete result; throw Exception ("librarian-error", "cannot read file descriptors"); } } return result; } // ------------------------------------------------------------------------- // - class section - // ------------------------------------------------------------------------- // create an empty librarian Librarian::Librarian (void) { d_type = OUTPUT; p_desc = nilp; for (long i = 0; i < 8; i++) d_ffmt[i] = AXL_UNK_MRK; } // create a librarian by name Librarian::Librarian (const String& lname) { d_type = INPUT; d_name = lname; p_desc = read_header (lname); for (long i = 0; i < 8; i++) d_ffmt[i] = AXL_UNK_MRK; } // destroy this librarian Librarian::~Librarian (void) { delete p_desc; } // return the class name String Librarian::repr (void) const { return "Librarian"; } // return the librarian name String Librarian::getname (void) const { rdlock (); String result = d_name; unlock (); return result; } // return an excepted file flags in the librarian t_byte Librarian::fixflag (const t_byte flags) const { return flags; } // return an excepted file size in the librarian t_long Librarian::fixsize (const t_long size) const { return size; } // return an input file by name in the librarian Input* Librarian::mapfile (const String& name) const { return new InputFile (name); } // add a new file descriptor to the chain void Librarian::add (const String& path) { wrlock (); // check for type if (d_type == INPUT) { unlock (); throw Exception ("librarian-error", "cannot add file to librarian"); } try { // check for a file first InputFile is (path); if (is.length () == 0) return; // normalize the size if we have a cipher t_long fsize = is.length (); t_long csize = fixsize (fsize); // add the descriptor and update the flags s_desc* desc = new s_desc (path, fsize, csize); desc->d_flags = fixflag (desc->d_flags); if (p_desc == nilp) p_desc = desc; else p_desc->append (desc); unlock (); } catch (...) { unlock (); throw; } } // return the number of files in the librarian long Librarian::length (void) const { rdlock (); long result = 0; s_desc* desc = p_desc; while (desc != nilp) { result++; desc = desc->p_next; } unlock (); return result; } // return true if the name exists in this librarian bool Librarian::exists (const String& name) const { rdlock (); s_desc* desc = p_desc; while (desc != nilp) { if (desc->d_fname == name) { unlock (); return true; } desc = desc->p_next; } unlock (); return false; } // return a list of file in this librarian Strvec Librarian::getlist (void) const { rdlock (); Strvec result; s_desc* desc = p_desc; while (desc != nilp) { result.add (desc->d_fname); desc = desc->p_next; } unlock (); return result; } // return a vector of file in this librarian Vector* Librarian::getstr (void) const { rdlock (); Vector* result = new Vector; s_desc* desc = p_desc; while (desc != nilp) { result->append (new String (desc->d_fname)); desc = desc->p_next; } unlock (); return result; } // extract a file by name Input* Librarian::extract (const String& name) const { rdlock (); if (d_type == OUTPUT) { unlock (); throw Exception ("librarian-error", "cannot extract from librarian"); } try { // get the descriptor by name s_desc* desc = get_named_desc (p_desc, name); if (desc == nilp) { unlock (); throw Exception ("extract-error", "cannot extract file", name); } // get the mapped file t_long size = desc->d_csize; t_long foff = desc->d_lfoff; Input* result = new InputMapped (d_name, size, foff); unlock (); return result; } catch (...) { unlock (); throw; } } // write the librarian to an output file void Librarian::write (const String& lname) const { OutputFile os (lname); rdlock (); try { // write the header write_header (os, p_desc); // write all file sequentialy s_desc* desc = p_desc; while (desc != nilp) { Input* is = mapfile (desc->d_fpath); if (is == nilp) { unlock (); throw Exception ("librarian-error", "cannot map input file stream"); } while (is->valid (0) == true) os.write (is->read ()); delete is; desc = desc->p_next; } unlock (); } catch (...) { unlock (); throw; } } // format the librarian content to an output stream void Librarian::format (Output& os) const { rdlock (); s_desc* desc = p_desc; while (desc != nilp) { desc->format (os, d_ffmt); desc = desc->p_next; } unlock (); } // return true if the path is a valid librarian file bool Librarian::valid (const String& path) { try { read_header (path); return true; } catch (...) { return false; } } // ------------------------------------------------------------------------- // - object section - // ------------------------------------------------------------------------- // the quark zone static const long QUARK_ZONE_LENGTH = 6; static QuarkZone zone (QUARK_ZONE_LENGTH); // the librarian supported quarks static const long QUARK_ADD = zone.intern ("add"); static const long QUARK_WRITE = zone.intern ("write"); static const long QUARK_LENGTH = zone.intern ("length"); static const long QUARK_GETVEC = zone.intern ("get-names"); static const long QUARK_EXISTS = zone.intern ("exists-p"); static const long QUARK_EXTRACT = zone.intern ("extract"); // create a new object in a generic way Object* Librarian::mknew (Vector* argv) { // get the number of arguments long argc = (argv == nilp) ? 0 : argv->length (); // check for 0 argument if (argc == 0) return new Librarian; // check for 1 argument if (argc == 1) { String fname = argv->getstring (0); return new Librarian (fname); } throw Exception ("argument-error", "invalid number of argument with librarian"); } // return true if the given quark is defined bool Librarian::isquark (const long quark, const bool hflg) const { rdlock (); if (zone.exists (quark) == true) { unlock (); return true; } bool result = hflg ? Nameable::isquark (quark, hflg) : false; unlock (); return result; } // apply a librarian method with a set of arguments and a quark Object* Librarian::apply (Runnable* robj, Nameset* nset, const long quark, Vector* argv) { // get the number of arguments long argc = (argv == nilp) ? 0 : argv->length (); // dispatch 0 argument if (argc == 0) { if (quark == QUARK_LENGTH) return new Integer (length ()); if (quark == QUARK_GETVEC) return getstr (); } // dispatch 1 argument if (argc == 1) { if (quark == QUARK_EXISTS) { String fname = argv->getstring (0); return new Boolean (exists (fname)); } if (quark == QUARK_ADD) { String fname = argv->getstring (0); add (fname); return nilp; } if (quark == QUARK_WRITE) { String fname = argv->getstring (0); write (fname); return nilp; } if (quark == QUARK_EXTRACT) { String fname = argv->getstring (0); Object* result = extract (fname); robj->post (result); return result; } } // call the nameable method return Nameable::apply (robj, nset, quark, argv); } }