// ---------------------------------------------------------------------------
// - 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);
}
}
syntax highlighted by Code2HTML, v. 0.9.1