kvmallocator.cpp
00001 /* 00002 This file is part of the KDE libraries 00003 00004 Copyright (C) 1999 Waldo Bastian (bastian@kde.org) 00005 00006 This library is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU Library General Public 00008 License as published by the Free Software Foundation; either 00009 version 2 of the License, or (at your option) any later version. 00010 00011 This library is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 Library General Public License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to 00018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 Boston, MA 02110-1301, USA. 00020 */ 00021 //---------------------------------------------------------------------------- 00022 // 00023 // Virtual Memory Allocator 00024 00025 // TODO: Add large file support. 00026 // TODO: Error reporting. (e.g. disk-full) 00027 00028 #include <unistd.h> 00029 #include <sys/mman.h> 00030 00031 #include <tqintdict.h> 00032 #include <tqmap.h> 00033 00034 #include <tdetempfile.h> 00035 #include <kdebug.h> 00036 00037 #include "kvmallocator.h" 00038 00039 00040 #define KVM_ALIGN 4095 00041 00042 struct KVMAllocator::Block 00043 { 00044 off_t start; 00045 size_t length; // Requested length 00046 size_t size; // Actual size 00047 void *mmap; 00048 }; 00049 00050 00051 class KVMAllocatorPrivate 00052 { 00053 public: 00054 KTempFile *tempfile; 00055 off_t max_length; 00056 TQMap<off_t, KVMAllocator::Block> used_blocks; 00057 TQMap<off_t, KVMAllocator::Block> free_blocks; 00058 }; 00059 00063 KVMAllocator::KVMAllocator() 00064 { 00065 d = new KVMAllocatorPrivate; 00066 d->tempfile = 0; 00067 d->max_length = 0; 00068 } 00069 00073 KVMAllocator::~KVMAllocator() 00074 { 00075 delete d->tempfile; 00076 delete d; 00077 } 00078 00083 KVMAllocator::Block * 00084 KVMAllocator::allocate(size_t _size) 00085 { 00086 if (!d->tempfile) 00087 { 00088 d->tempfile = new KTempFile(TQString::null, "vmdata"); 00089 d->tempfile->unlink(); 00090 } 00091 // Search in free list 00092 TQMap<off_t,KVMAllocator::Block>::iterator it; 00093 it = d->free_blocks.begin(); 00094 while (it != d->free_blocks.end()) 00095 { 00096 if (it.data().size > _size) 00097 { 00098 Block &free_block = it.data(); 00099 Block block; 00100 kdDebug(180)<<"VM alloc: using block from free list "<<(long)free_block.start<<" size ="<<(long)free_block.size<<" request = "<<_size<< endl; 00101 block.start = free_block.start; 00102 block.length = _size; 00103 block.size = (_size + KVM_ALIGN) & ~KVM_ALIGN; 00104 block.mmap = 0; 00105 free_block.size -= block.size; 00106 free_block.start += block.size; 00107 if (!free_block.size) 00108 d->free_blocks.remove(it); 00109 it = d->used_blocks.replace(block.start, block); 00110 return &(it.data()); 00111 } 00112 ++it; 00113 } 00114 00115 00116 // Create new block 00117 Block block; 00118 block.start = d->max_length; 00119 block.length = _size; 00120 block.size = (_size + KVM_ALIGN) & ~KVM_ALIGN; 00121 block.mmap = 0; 00122 kdDebug(180)<<"VM alloc: using new block "<<(long)block.start<<" size ="<<(long)block.size<<" request = "<<_size<< endl; 00123 it = d->used_blocks.replace(block.start, block); 00124 d->max_length += block.size; 00125 return &(it.data()); 00126 } 00127 00131 void 00132 KVMAllocator::free(Block *block_p) 00133 { 00134 Block block = *block_p; 00135 if (block.mmap) 00136 { 00137 kdDebug(180)<<"VM free: Block "<<(long)block.start<<" is still mmapped!"<<endl; 00138 return; 00139 } 00140 TQMap<off_t,KVMAllocator::Block>::iterator it; 00141 it = d->used_blocks.find(block.start); 00142 if (it == d->used_blocks.end()) 00143 { 00144 kdDebug(180)<<"VM free: Block "<<(long)block.start<<" is not allocated."<<endl; 00145 return; 00146 } 00147 d->used_blocks.remove(it); 00148 it = d->free_blocks.replace(block.start, block); 00149 TQMap<off_t,KVMAllocator::Block>::iterator before = it; 00150 --before; 00151 if (before != d->free_blocks.end()) 00152 { 00153 Block &block_before = before.data(); 00154 if ((block_before.start + off_t(block_before.size)) == block.start) 00155 { 00156 // Merge blocks. 00157 kdDebug(180) << "VM merging: Block "<< (long)block_before.start<< 00158 " with "<< (long)block.start<< " (before)" << endl; 00159 block.size += block_before.size; 00160 block.start = block_before.start; 00161 it.data() = block; 00162 d->free_blocks.remove(before); 00163 } 00164 } 00165 00166 TQMap<off_t,KVMAllocator::Block>::iterator after = it; 00167 ++after; 00168 if (after != d->free_blocks.end()) 00169 { 00170 Block &block_after = after.data(); 00171 if ((block.start + off_t(block.size)) == block_after.start) 00172 { 00173 // Merge blocks. 00174 kdDebug(180) << "VM merging: Block "<< (long)block.start<< 00175 " with "<< (long)block_after.start<< " (after)" << endl; 00176 block.size += block_after.size; 00177 it.data() = block; 00178 d->free_blocks.remove(after); 00179 } 00180 } 00181 } 00182 00186 void 00187 KVMAllocator::copy(void *dest, Block *src, int _offset, size_t length) 00188 { 00189 (void) copyBlock(dest, src, _offset, length); 00190 } 00191 00192 bool 00193 KVMAllocator::copyBlock(void *dest, Block *src, int _offset, size_t length) 00194 { 00195 //kdDebug(180)<<"VM read: seek "<<(long)src->start<<" +"<<_offset<<":"<<length<<endl; 00196 lseek(d->tempfile->handle(), src->start+_offset, SEEK_SET); 00197 if (length == 0) 00198 length = src->length - _offset; 00199 int to_go = length; 00200 int done = 0; 00201 char *buf = (char *) dest; 00202 while(to_go > 0) 00203 { 00204 int n = read(d->tempfile->handle(), buf+done, to_go); 00205 if (n <= 0) 00206 { 00207 if (n < 0) 00208 return false; // Error 00209 else 00210 return true; // End of data 00211 } 00212 done += n; 00213 to_go -= n; 00214 } 00215 // Done. 00216 return true; 00217 } 00218 00222 void 00223 KVMAllocator::copy(Block *dest, void *src, int _offset, size_t length) 00224 { 00225 (void) copyBlock(dest, src, _offset, length); 00226 } 00227 00228 bool 00229 KVMAllocator::copyBlock(Block *dest, void *src, int _offset, size_t length) 00230 { 00231 //kdDebug(180)<<"VM write: seek "<<(long)dest->start<<" +"<<_offset<< ":" << length << endl; 00232 lseek(d->tempfile->handle(), dest->start+_offset, SEEK_SET); 00233 if (length == 0) 00234 length = dest->length - _offset; 00235 int to_go = length; 00236 int done = 0; 00237 char *buf = (char *) src; 00238 while(to_go > 0) 00239 { 00240 int n = write(d->tempfile->handle(), buf+done, to_go); 00241 if (n <= 0) return false; // Error 00242 done += n; 00243 to_go -= n; 00244 } 00245 // Done. 00246 return true; 00247 } 00248 00252 void * 00253 KVMAllocator::map(Block *block) 00254 { 00255 if (block->mmap) 00256 return block->mmap; 00257 00258 void *result = mmap(0, block->length, PROT_READ| PROT_WRITE, 00259 MAP_SHARED, d->tempfile->handle(), block->start); 00260 block->mmap = result; 00261 return block->mmap; 00262 } 00263 00267 void 00268 KVMAllocator::unmap(Block *block) 00269 { 00270 // The following cast is necassery for Solaris. 00271 // (touch it and die). --Waba 00272 munmap((char *)block->mmap, block->length); 00273 block->mmap = 0; 00274 }