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 */