275 lines
7.0 KiB
275 lines
7.0 KiB
/*
|
|
This file is part of the KDE libraries
|
|
|
|
Copyright (C) 1999 Waldo Bastian (bastian@kde.org)
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library 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. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Virtual Memory Allocator
|
|
|
|
// TODO: Add large file support.
|
|
// TODO: Error reporting. (e.g. disk-full)
|
|
|
|
#include <unistd.h>
|
|
#include <sys/mman.h>
|
|
|
|
#include <tqintdict.h>
|
|
#include <tqmap.h>
|
|
|
|
#include <tdetempfile.h>
|
|
#include <kdebug.h>
|
|
|
|
#include "kvmallocator.h"
|
|
|
|
|
|
#define KVM_ALIGN 4095
|
|
|
|
struct KVMAllocator::Block
|
|
{
|
|
off_t start;
|
|
size_t length; // Requested length
|
|
size_t size; // Actual size
|
|
void *mmap;
|
|
};
|
|
|
|
|
|
class KVMAllocatorPrivate
|
|
{
|
|
public:
|
|
KTempFile *tempfile;
|
|
off_t max_length;
|
|
TQMap<off_t, KVMAllocator::Block> used_blocks;
|
|
TQMap<off_t, KVMAllocator::Block> free_blocks;
|
|
};
|
|
|
|
/**
|
|
* Create a KVMAllocator
|
|
*/
|
|
KVMAllocator::KVMAllocator()
|
|
{
|
|
d = new KVMAllocatorPrivate;
|
|
d->tempfile = 0;
|
|
d->max_length = 0;
|
|
}
|
|
|
|
/**
|
|
* Destruct the KVMAllocator and release all memory.
|
|
*/
|
|
KVMAllocator::~KVMAllocator()
|
|
{
|
|
delete d->tempfile;
|
|
delete d;
|
|
}
|
|
|
|
/**
|
|
* Allocate a virtual memory block.
|
|
* @param _size Size in bytes of the memory block.
|
|
*/
|
|
KVMAllocator::Block *
|
|
KVMAllocator::allocate(size_t _size)
|
|
{
|
|
if (!d->tempfile)
|
|
{
|
|
d->tempfile = new KTempFile(TQString::null, "vmdata");
|
|
d->tempfile->unlink();
|
|
}
|
|
// Search in free list
|
|
TQMap<off_t,KVMAllocator::Block>::iterator it;
|
|
it = d->free_blocks.begin();
|
|
while (it != d->free_blocks.end())
|
|
{
|
|
if (it.data().size > _size)
|
|
{
|
|
Block &free_block = it.data();
|
|
Block block;
|
|
kdDebug(180)<<"VM alloc: using block from free list "<<(long)free_block.start<<" size ="<<(long)free_block.size<<" request = "<<_size<< endl;
|
|
block.start = free_block.start;
|
|
block.length = _size;
|
|
block.size = (_size + KVM_ALIGN) & ~KVM_ALIGN;
|
|
block.mmap = 0;
|
|
free_block.size -= block.size;
|
|
free_block.start += block.size;
|
|
if (!free_block.size)
|
|
d->free_blocks.remove(it);
|
|
it = d->used_blocks.replace(block.start, block);
|
|
return &(it.data());
|
|
}
|
|
++it;
|
|
}
|
|
|
|
|
|
// Create new block
|
|
Block block;
|
|
block.start = d->max_length;
|
|
block.length = _size;
|
|
block.size = (_size + KVM_ALIGN) & ~KVM_ALIGN;
|
|
block.mmap = 0;
|
|
kdDebug(180)<<"VM alloc: using new block "<<(long)block.start<<" size ="<<(long)block.size<<" request = "<<_size<< endl;
|
|
it = d->used_blocks.replace(block.start, block);
|
|
d->max_length += block.size;
|
|
return &(it.data());
|
|
}
|
|
|
|
/**
|
|
* Free a virtual memory block
|
|
*/
|
|
void
|
|
KVMAllocator::free(Block *block_p)
|
|
{
|
|
Block block = *block_p;
|
|
if (block.mmap)
|
|
{
|
|
kdDebug(180)<<"VM free: Block "<<(long)block.start<<" is still mmapped!"<<endl;
|
|
return;
|
|
}
|
|
TQMap<off_t,KVMAllocator::Block>::iterator it;
|
|
it = d->used_blocks.find(block.start);
|
|
if (it == d->used_blocks.end())
|
|
{
|
|
kdDebug(180)<<"VM free: Block "<<(long)block.start<<" is not allocated."<<endl;
|
|
return;
|
|
}
|
|
d->used_blocks.remove(it);
|
|
it = d->free_blocks.replace(block.start, block);
|
|
TQMap<off_t,KVMAllocator::Block>::iterator before = it;
|
|
--before;
|
|
if (before != d->free_blocks.end())
|
|
{
|
|
Block &block_before = before.data();
|
|
if ((block_before.start + off_t(block_before.size)) == block.start)
|
|
{
|
|
// Merge blocks.
|
|
kdDebug(180) << "VM merging: Block "<< (long)block_before.start<<
|
|
" with "<< (long)block.start<< " (before)" << endl;
|
|
block.size += block_before.size;
|
|
block.start = block_before.start;
|
|
it.data() = block;
|
|
d->free_blocks.remove(before);
|
|
}
|
|
}
|
|
|
|
TQMap<off_t,KVMAllocator::Block>::iterator after = it;
|
|
++after;
|
|
if (after != d->free_blocks.end())
|
|
{
|
|
Block &block_after = after.data();
|
|
if ((block.start + off_t(block.size)) == block_after.start)
|
|
{
|
|
// Merge blocks.
|
|
kdDebug(180) << "VM merging: Block "<< (long)block.start<<
|
|
" with "<< (long)block_after.start<< " (after)" << endl;
|
|
block.size += block_after.size;
|
|
it.data() = block;
|
|
d->free_blocks.remove(after);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Copy data from a virtual memory block to normal memory
|
|
*/
|
|
void
|
|
KVMAllocator::copy(void *dest, Block *src, int _offset, size_t length)
|
|
{
|
|
(void) copyBlock(dest, src, _offset, length);
|
|
}
|
|
|
|
bool
|
|
KVMAllocator::copyBlock(void *dest, Block *src, int _offset, size_t length)
|
|
{
|
|
//kdDebug(180)<<"VM read: seek "<<(long)src->start<<" +"<<_offset<<":"<<length<<endl;
|
|
lseek(d->tempfile->handle(), src->start+_offset, SEEK_SET);
|
|
if (length == 0)
|
|
length = src->length - _offset;
|
|
int to_go = length;
|
|
int done = 0;
|
|
char *buf = (char *) dest;
|
|
while(to_go > 0)
|
|
{
|
|
int n = read(d->tempfile->handle(), buf+done, to_go);
|
|
if (n <= 0)
|
|
{
|
|
if (n < 0)
|
|
return false; // Error
|
|
else
|
|
return true; // End of data
|
|
}
|
|
done += n;
|
|
to_go -= n;
|
|
}
|
|
// Done.
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Copy data from normal memory to a virtual memory block
|
|
*/
|
|
void
|
|
KVMAllocator::copy(Block *dest, void *src, int _offset, size_t length)
|
|
{
|
|
(void) copyBlock(dest, src, _offset, length);
|
|
}
|
|
|
|
bool
|
|
KVMAllocator::copyBlock(Block *dest, void *src, int _offset, size_t length)
|
|
{
|
|
//kdDebug(180)<<"VM write: seek "<<(long)dest->start<<" +"<<_offset<< ":" << length << endl;
|
|
lseek(d->tempfile->handle(), dest->start+_offset, SEEK_SET);
|
|
if (length == 0)
|
|
length = dest->length - _offset;
|
|
int to_go = length;
|
|
int done = 0;
|
|
char *buf = (char *) src;
|
|
while(to_go > 0)
|
|
{
|
|
int n = write(d->tempfile->handle(), buf+done, to_go);
|
|
if (n <= 0) return false; // Error
|
|
done += n;
|
|
to_go -= n;
|
|
}
|
|
// Done.
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Map a virtual memory block in memory
|
|
*/
|
|
void *
|
|
KVMAllocator::map(Block *block)
|
|
{
|
|
if (block->mmap)
|
|
return block->mmap;
|
|
|
|
void *result = mmap(0, block->length, PROT_READ| PROT_WRITE,
|
|
MAP_SHARED, d->tempfile->handle(), block->start);
|
|
block->mmap = result;
|
|
return block->mmap;
|
|
}
|
|
|
|
/**
|
|
* Unmap a virtual memory block
|
|
*/
|
|
void
|
|
KVMAllocator::unmap(Block *block)
|
|
{
|
|
// The following cast is necassery for Solaris.
|
|
// (touch it and die). --Waba
|
|
munmap((char *)block->mmap, block->length);
|
|
block->mmap = 0;
|
|
}
|