#ifndef DISKVECTOR_H__
#define DISKVECTOR_H__
/* (c) 2005, Thomas Mølhave (thomasm@daimi.au.dk)
* Vector for storing huge amounts of data, all you
* need is enough space on your disk.
*
* Version 0.01 - Jan 27, 2005.
*/
#include <exception>
#include <fstream>
#include <stdexcept>
#include <string>
#include <iostream>
namespace Th {
/* Specification */
template<typename T> class disk_vector
{
public:
disk_vector() : mBlockSize(0), mSize(0), mCurrentOffset(0), mCurrentBlock(0) { }
/* Use operator[] to acces elements in the vector. */
T& operator[](unsigned int i);
/* Returns the size of the vector. */
unsigned int size() { return mSize; }
/* Initializes the vector
* size: The number of elements in the vector
* blockSize: The number of elements that you want to keep in memory at a time
* file: The file to store the vector in.
*/
void init(unsigned int size, unsigned int blockSize = 100, std::string file = "/tmp/iofile");
private:
std::string mFileName;
std::fstream mFile;
unsigned int mBlockSize;
unsigned int mSize;
unsigned int mCurrentOffset;
T* mCurrentBlock;
};
/* Implementation */
template<typename T>
inline void disk_vector<T>::init(unsigned int size, unsigned int blockSize, std::string file)
{
mCurrentBlock = new T[blockSize];
if (mCurrentBlock == 0)
throw std::bad_alloc();
mFileName = file;
mFile.open(file.c_str(), std::ios::trunc | std::ios::in | std::ios::out | std::ios::binary);
if (!mFile)
throw std::runtime_error("Could not open IO file: " + file);
mBlockSize = blockSize;
mSize = size;
mCurrentOffset = 0;
if (mSize > 0) //Make sure file has the correct length
{
mFile.seekp(mSize*sizeof(T)-1, std::ios::beg);
mFile.write("A", 1);
}
mFile.seekg(0, std::ios::beg);
mFile.seekp(0, std::ios::beg);
}
template<typename T>
inline T& disk_vector<T>::operator[](unsigned int i)
{
if (i >= mSize) throw std::logic_error("Index out of bounds");
//Check if wee need to fetch a new block
if (i >= mCurrentOffset && i < mCurrentOffset+mBlockSize)
{
//It is already in memory, return element
return mCurrentBlock[i-mCurrentOffset];
} else {
//We need a new block, write the old one back to disk
mFile.seekp(mCurrentOffset*sizeof(T), std::ios::beg);
mFile.write((char*)mCurrentBlock, mBlockSize*sizeof(T));
//reseek to new offset
mCurrentOffset = i;
mFile.seekg(i*sizeof(T), std::ios::beg);
mFile.read((char*)mCurrentBlock, mBlockSize*sizeof(T));
return this->operator[](i);
}
}
}
#endif