summaryrefslogtreecommitdiffstats
path: root/kdecore/kvmallocator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kdecore/kvmallocator.cpp')
-rw-r--r--kdecore/kvmallocator.cpp274
1 files changed, 274 insertions, 0 deletions
diff --git a/kdecore/kvmallocator.cpp b/kdecore/kvmallocator.cpp
new file mode 100644
index 000000000..21d418434
--- /dev/null
+++ b/kdecore/kvmallocator.cpp
@@ -0,0 +1,274 @@
+/*
+ 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 <qintdict.h>
+#include <qmap.h>
+
+#include <ktempfile.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;
+ QMap<off_t, KVMAllocator::Block> used_blocks;
+ QMap<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(QString::null, "vmdata");
+ d->tempfile->unlink();
+ }
+ // Search in free list
+ QMap<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;
+ }
+ QMap<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);
+ QMap<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);
+ }
+ }
+
+ QMap<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;
+}