mirror of
https://github.com/ksherlock/profuse.git
synced 2025-01-10 07:30:09 +00:00
178 lines
3.0 KiB
C++
178 lines
3.0 KiB
C++
|
#include "BlockCache.h"
|
||
|
|
||
|
struct FileBlockCache::Entry {
|
||
|
FileBlockCache::Entry *next;
|
||
|
unsigned block;
|
||
|
unsigned count;
|
||
|
bool dirty;
|
||
|
uint8_t buffer[512];
|
||
|
};
|
||
|
|
||
|
FileBlockCache::FileBlockCache(Device *device)
|
||
|
{
|
||
|
_device = device;
|
||
|
|
||
|
// allocate initial buffer.
|
||
|
for (unsigned i = 0; i < 10; ++i)
|
||
|
{
|
||
|
Entry *e = new Entry;
|
||
|
std::memset(e, 0, sizeof(Entry));
|
||
|
|
||
|
_pages.push_back(e);
|
||
|
_unused.push_back(e);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FileBlockCache::~FileBlockCache()
|
||
|
{
|
||
|
std::vector<Entry *>::iterator iter;
|
||
|
|
||
|
// todo -- check if dirty.
|
||
|
|
||
|
// deallocate everything that was allocated.
|
||
|
for (iter = _pages.begin(); iter != _pages.end(); ++iter)
|
||
|
{
|
||
|
Entrty *e = *iter;
|
||
|
if (e->dirty) sync(e);
|
||
|
delete e;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
uint8_t *FileBlockCache::acquire(unsigned block)
|
||
|
{
|
||
|
Entry *e = findEntry(block);
|
||
|
|
||
|
if (e)
|
||
|
{
|
||
|
if (++e->count == 1)
|
||
|
_unused.remove(e);
|
||
|
return e->buffer;
|
||
|
}
|
||
|
|
||
|
if (_unused.empty())
|
||
|
{
|
||
|
e = new Entry;
|
||
|
_pages.push_back(e);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
e = _unused.pop_front();
|
||
|
|
||
|
removeEntry(e);
|
||
|
}
|
||
|
std::memset(e, 0, sizeof(Entry));
|
||
|
unsigned hash = hashFunction(block);
|
||
|
|
||
|
e->block = block;
|
||
|
e->count = 1;
|
||
|
e->dirty = 0;
|
||
|
|
||
|
_device->read(block, e->buffer);
|
||
|
|
||
|
_e->next = _hashMap[hash];
|
||
|
_hashMap[hash] = e;
|
||
|
|
||
|
return e->buffer;
|
||
|
}
|
||
|
|
||
|
|
||
|
void FileBlockCache::release(unsigned block, bool dirty)
|
||
|
{
|
||
|
Entry *e = findEntry(block);
|
||
|
|
||
|
// throw error?
|
||
|
if (!e) return;
|
||
|
|
||
|
if (dirty) e->dirty = true;
|
||
|
|
||
|
if (e->count == 0) return;
|
||
|
if (--e->count == 0)
|
||
|
{
|
||
|
_unused.push_back(e);
|
||
|
}
|
||
|
// sync if dirty??
|
||
|
}
|
||
|
|
||
|
void FileBlockCache::markDirty(unsigned block)
|
||
|
{
|
||
|
Entry *e = findEntry(e);
|
||
|
|
||
|
if (e && e->count) e->dirty = true;
|
||
|
}
|
||
|
|
||
|
Entry *FileBlockCache::findEntry(unsigned block)
|
||
|
{
|
||
|
unsigned hash = hashFunction(block);
|
||
|
Entry *e;
|
||
|
|
||
|
e = _hashMap[hash];
|
||
|
|
||
|
while (e && e->block != block)
|
||
|
{
|
||
|
e = e->next;
|
||
|
}
|
||
|
|
||
|
return e;
|
||
|
}
|
||
|
|
||
|
void FileBlockCache::removeEntry(Entry *e)
|
||
|
{
|
||
|
unsigned hash;
|
||
|
Entry *curr;
|
||
|
Entry *prev;
|
||
|
|
||
|
if (!e) return;
|
||
|
|
||
|
hash = hashFunction(e->block);
|
||
|
|
||
|
curr = _hashMap[hash];
|
||
|
|
||
|
if (curr == e)
|
||
|
{
|
||
|
_hashMap[hash] = e->next;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
prev = curr;
|
||
|
curr = curr->next;
|
||
|
|
||
|
if (!curr) break;
|
||
|
|
||
|
if (e == curr)
|
||
|
{
|
||
|
prev->next = e->next;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unsigned FileBlockCache::hashFunction(unsigned block)
|
||
|
{
|
||
|
return block % HashEntries;
|
||
|
}
|
||
|
|
||
|
void FileBlockCache::sync(Entry *e)
|
||
|
{
|
||
|
if (!e) return;
|
||
|
if (!e->dirty) return;
|
||
|
|
||
|
_device->write(e->block, e->buffer);
|
||
|
e->dirty = false;
|
||
|
}
|
||
|
|
||
|
void FileBlockCache::sync()
|
||
|
{
|
||
|
std::vector<Entry *>::iterator iter;
|
||
|
|
||
|
for (iter = _pages.begin(); iter != _pages.end(); ++iter)
|
||
|
{
|
||
|
Entry *e = *iter;
|
||
|
|
||
|
if (e && e->dirty) sync(e);
|
||
|
}
|
||
|
}
|