File indexing completed on 2024-04-28 09:36:47
0001 /* 0002 This file is part of KCachegrind. 0003 0004 SPDX-FileCopyrightText: 2002-2016 Josef Weidendorfer <Josef.Weidendorfer@gmx.de> 0005 0006 SPDX-License-Identifier: GPL-2.0-only 0007 */ 0008 0009 #include "pool.h" 0010 0011 #include <string.h> 0012 #include <stdlib.h> 0013 #include <qglobal.h> 0014 0015 // FixPool 0016 0017 #define CHUNK_SIZE 100000 0018 0019 struct SpaceChunk 0020 { 0021 struct SpaceChunk* next; 0022 unsigned int used; 0023 char space[1]; 0024 }; 0025 0026 FixPool::FixPool() 0027 { 0028 _first = _last = nullptr; 0029 _reservation = 0; 0030 _count = 0; 0031 _size = 0; 0032 } 0033 0034 FixPool::~FixPool() 0035 { 0036 struct SpaceChunk* chunk = _first, *next; 0037 0038 while(chunk) { 0039 next = chunk->next; 0040 free(chunk); 0041 chunk = next; 0042 } 0043 0044 if (0) qDebug("~FixPool: Had %d objects with total size %d\n", 0045 _count, _size); 0046 } 0047 0048 void* FixPool::allocate(unsigned int size) 0049 { 0050 if (!ensureSpace(size)) return nullptr; 0051 0052 _reservation = 0; 0053 void* result = _last->space + _last->used; 0054 _last->used += size; 0055 0056 _count++; 0057 _size += size; 0058 0059 return result; 0060 } 0061 0062 void* FixPool::reserve(unsigned int size) 0063 { 0064 if (!ensureSpace(size)) return nullptr; 0065 _reservation = size; 0066 0067 return _last->space + _last->used; 0068 } 0069 0070 0071 bool FixPool::allocateReserved(unsigned int size) 0072 { 0073 if (_reservation < size) return false; 0074 0075 _reservation = 0; 0076 _last->used += size; 0077 0078 _count++; 0079 _size += size; 0080 0081 return true; 0082 } 0083 0084 bool FixPool::ensureSpace(unsigned int size) 0085 { 0086 if (_last && _last->used + size <= CHUNK_SIZE) return true; 0087 0088 struct SpaceChunk* newChunk; 0089 0090 // we do not allow allocation sizes > CHUNK_SIZE 0091 if (size > CHUNK_SIZE) return false; 0092 0093 newChunk = (struct SpaceChunk*) malloc(sizeof(struct SpaceChunk) + 0094 CHUNK_SIZE); 0095 if (!newChunk) { 0096 qFatal("ERROR: Out of memory. Sorry. KCachegrind has to terminate.\n\n" 0097 "You probably tried to load a profile data file too huge for" 0098 "this system. You could try loading this file on a 64-bit OS."); 0099 exit(1); 0100 } 0101 newChunk->next = nullptr; 0102 newChunk->used = 0; 0103 0104 if (!_last) { 0105 _last = _first = newChunk; 0106 } 0107 else { 0108 _last->next = newChunk; 0109 _last = newChunk; 0110 } 0111 return true; 0112 } 0113 0114 0115 // DynPool 0116 0117 DynPool::DynPool() 0118 { 0119 _data = (char*) malloc(CHUNK_SIZE); 0120 _used = 0; 0121 _size = CHUNK_SIZE; 0122 0123 // end marker 0124 *(int*)_data = 0; 0125 } 0126 0127 DynPool::~DynPool() 0128 { 0129 // we could check for correctness by iteration over all objects 0130 0131 ::free(_data); 0132 } 0133 0134 bool DynPool::allocate(char** ptr, unsigned int size) 0135 { 0136 // round up to multiple of 4 0137 size = (size+3) & ~3; 0138 0139 /* need 12 bytes more: 0140 * - 4 bytes for forward chain 0141 * - 4 bytes for pointer to ptr 0142 * - 4 bytes as end marker (not used for new object) 0143 */ 0144 if (!ensureSpace(size + 12)) return false; 0145 0146 char** obj = (char**) (_data+_used); 0147 obj[0] = (char*)(_data + _used + size + 8); 0148 obj[1] = (char*)ptr; 0149 *(int*)(_data+_used+size+8) = 0; 0150 *ptr = _data+_used+8; 0151 0152 _used += size + 8; 0153 0154 return true; 0155 } 0156 0157 void DynPool::free(char** ptr) 0158 { 0159 if (!ptr || 0160 !*ptr || 0161 (*(char**)(*ptr - 4)) != (char*)ptr ) 0162 qFatal("Chaining error in DynPool::free"); 0163 0164 (*(char**)(*ptr - 4)) = nullptr; 0165 *ptr = nullptr; 0166 } 0167 0168 bool DynPool::ensureSpace(unsigned int size) 0169 { 0170 if (_used + size <= _size) return true; 0171 0172 unsigned int newsize = _size *3/2 + CHUNK_SIZE; 0173 char* newdata = (char*) malloc(newsize); 0174 0175 unsigned int freed = 0, len; 0176 char **p, **pnext, **pnew; 0177 0178 qDebug("DynPool::ensureSpace size: %d => %d, used %d. %p => %p", 0179 _size, newsize, _used, _data, newdata); 0180 0181 pnew = (char**) newdata; 0182 p = (char**) _data; 0183 while(*p) { 0184 pnext = (char**) *p; 0185 len = (char*)pnext - (char*)p; 0186 0187 if (0) qDebug(" [%8p] Len %d (ptr %p), freed %d (=> %p)", 0188 p, len, p[1], freed, pnew); 0189 0190 /* skip freed space ? */ 0191 if (p[1] == nullptr) { 0192 freed += len; 0193 p = pnext; 0194 continue; 0195 } 0196 0197 // new and old still at same address ? 0198 if (pnew == p) { 0199 pnew = p = pnext; 0200 continue; 0201 } 0202 0203 // copy object 0204 pnew[0] = (char*)pnew + len; 0205 pnew[1] = p[1]; 0206 memcpy((char*)pnew + 8, (char*)p + 8, len-8); 0207 0208 // update pointer to object 0209 char** ptr = (char**) p[1]; 0210 if (*ptr != ((char*)p)+8) 0211 qFatal("Chaining error in DynPool::ensureSpace"); 0212 *ptr = ((char*)pnew)+8; 0213 0214 pnew = (char**) pnew[0]; 0215 p = pnext; 0216 } 0217 pnew[0] = nullptr; 0218 0219 unsigned int newused = (char*)pnew - (char*)newdata; 0220 qDebug("DynPool::ensureSpace size: %d => %d, used %d => %d (%d freed)", 0221 _size, newsize, _used, newused, freed); 0222 0223 ::free(_data); 0224 _data = newdata; 0225 _size = newsize; 0226 _used = newused; 0227 0228 return true; 0229 } 0230 0231 /* Testing the DynPool 0232 int main() 0233 { 0234 char* bufs[CHUNK_SIZE]; 0235 int i; 0236 0237 DynPool p; 0238 0239 for(i=0;i<CHUNK_SIZE;i++) { 0240 p.allocate(bufs+i, 10+i%10); 0241 if (((i%3)==0) && (i>20)) 0242 p.free(bufs+i-20); 0243 } 0244 0245 for(i=0;i<CHUNK_SIZE;i++) { 0246 if ((bufs[i]==0) || ((i%7)==0)) continue; 0247 p.free(bufs+i); 0248 } 0249 0250 for(i=0;i<CHUNK_SIZE;i++) { 0251 if (bufs[i]) continue; 0252 p.allocate(bufs+i, 10+i%10); 0253 } 0254 } 0255 */