blob: daa2193963a548b618f4a74cfd22d3fdb7de5c68 [file] [log] [blame]
#ifndef OBJECT_DB_FILE_H
#define OBJECT_DB_FILE_H
#include "object-db.h"
#include <stdio.h>
#include <fstream>
#include <ifstream>
#include <ofstream>
#include <sstream>
#include <boost/thread/locks.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/interprocess/sync/file_lock.hpp>
#include <boost/interprocess/sync/sharable_lock.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#define _OVERRIDE
#ifdef __GNUC__
#if __GNUC_MAJOR >= 4 && __GNUC_MINOR__ >= 7
#undef _OVERRIDE
#define _OVERRIDE override
#endif // __GNUC__ version
#endif // __GNUC__
using namespace std;
// This is a file based ObjectDB implementation
// The assumption is, the Content Objects will be stored sequentially
// To provide random access, we will have a table of "address" for each
// ContentObject at the beginning of the file.
// This also requires another assumption, that is the number of COs must
// be know a priori. This requirement is reasonable for our dropbox-like
// System, as the file we publish is static file and we can easily know
// the number of COs before we store them into ObjectDB.
/* How file looks like:
* |MAGIC_NUM|capacity|size|pos for each CO ...|1st CO|2nd CO| ... |
*/
class ObjectDBFile
{
public:
typedef boost::interprocess::file_lock Filelock;
typedef boost::interprocess::scoped_lock<Filelock> WriteLock;
typedef boost::interprocess::sharable_lock<Filelock> ReadLock;
ObjectDBFile(const string &filename);
virtual ~ObjectDBFile(){}
// reserve the "address" table for n COs; must reserve before
// write anything (unless reserved quota has not be consumed yet)
void
init(int capacity);
bool
initialized() const { return m_initialized; }
// assume sequential
virtual void
append(const Bytes &co) _OVERRIDE;
// get next CO
virtual Bytes
next() _OVERRIDE;
// get n COs; if the remaining number of COs < n, return all;
virtual void
read(vector<Bytes> &vco, int n) _OVERRIDE;
// size in terms of number of COs
// This is the lazy form of size, i.e. it returns the size cached in this object
// but that may not necessarily equal to the actual size kept in file
// This is enough if the caller knows for sure that no other thread is changing the
// file or the caller does not care about the new size.
virtual int
size() const _OVERRIDE;
// this returns the actual size (also update the size cache in this object), but it is more costly, and requires file IO
int
fSize();
// the index of the CO to be read
int
index();
// set the pos to be the desired CO
// return true if success
bool
seek(int index);
// reset pos to be zero
void
rewind();
protected:
// read or write lock should have been grabbed already before the call
void
checkInit(const string &msg);
// read lock should have been grabbed already before the call
void
updateSize();
#define MAGIC_NUM 0xAAAAAAAA
protected:
string m_filename;
ifstream m_istream;
ofstream m_ostream;
Filelock m_filelock;
bool m_initialized;
// capacity in terms of number of COs
int m_cap;
int m_size;
// the index (or seq) of the CO to be read
int m_index;
};
void inline
writeInt(ostream &out, const int &x)
{
out.write((const char *)&x, sizeof(int));
}
void inline
readInt(istream &in, int &x)
{
in.read((char *)&x, sizeof(int));
}
// write size and then the actual bytes
// operator << overloading is not used to avoid confusion
void
writeBytes(ostream &out, const Bytes &bytes);
// read size and then the actual bytes
void
readBytes(istream &in, Bytes &bytes);
#endif