source: tags/arb_5.5/ARBDB/admalloc.c

Last change on this file was 6143, checked in by westram, 16 years ago
  • backport [6141] (parts changing code, but only strings and comments)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.7 KB
Line 
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#ifndef DARWIN
5#include <malloc.h>
6#endif
7#include <string.h>
8#include <limits.h>
9
10#include "adlocal.h"
11/*#include "arbdb.h"*/
12
13/* #define DUMP_MEMBLKS */
14
15#ifndef NDEBUG
16/*  #define TEST_MEMBLKS */
17#endif
18
19int gbm_system_page_size = 4096;
20#define GBM_MALLOC_OVERHEAD 32          /* pointer for alloc */
21
22#define GBM_MAGIC 0x74732876
23
24#define GBM_TABLE_SIZE  (gbm_system_page_size-GBM_MALLOC_OVERHEAD)  /* 4k Tables */
25
26#define GBM_ALIGNED 8
27#define GBM_LD_ALIGNED  3
28
29#define GBM_MAX_TABLES  16      /* n different sizes -> max = GBM_MAX_TABLES * GBM_ALIGNED */
30#define GBM_MAX_SIZE    (GBM_MAX_TABLES*GBM_ALIGNED)
31#define GBM_MAX_INDEX   256     /* mussed be 2expx */
32
33
34struct gbm_data_struct {
35    long    magic;              /* indicates free element */
36    struct  gbm_data_struct *next;      /* next free element */
37};
38
39struct gbm_table_struct {       /* a block containing data */
40    struct gbm_table_struct *next;
41    struct gbm_data_struct  data[1];
42};
43
44struct gbm_struct {
45    struct gbm_data_struct  *gds;           /* free data area */
46    size_t             size;            /* free size of current table */
47    size_t             allsize;     /* full size of all tables */
48    struct gbm_table_struct *first; /* link list of tables */
49    struct gbm_data_struct  *tables[GBM_MAX_TABLES + 1];    /* free entries */
50    long             tablecnt[GBM_MAX_TABLES + 1];  /* number of free entries */
51    long             useditems[GBM_MAX_TABLES + 1]; /* number of used items (everything) */
52    size_t      extern_data_size;       /* not handled by this routine */
53    long        extern_data_items;
54}               gbm_global[GBM_MAX_INDEX];
55
56struct gbm_struct2 {
57    char *old_sbrk;
58} gbm_global2;
59
60
61#define GBB_INCR        11      /* memsize increment in percent between
62                                   adjacent clusters */
63#define GBB_CLUSTERS    64      /* # of different clusters */
64#define GBB_ALIGN       GBM_LD_ALIGNED  /* align memsize of clusters (# of bits) */
65#define GBB_MINSIZE GBM_MAX_SIZE        /* minimal size of allocated big block */
66#define GBB_MAX_TRIALS  4               /* maximal number of clusters to search
67                                           for an unused block */
68#define GBB_MAGIC   0x67823747
69
70struct gbb_data;
71
72struct gbb_freedata /* part of gbb_data if it`s a free block */
73{
74    long        magic;
75    struct gbb_data *next;  /* next unused memblock */
76};
77
78struct gbb_data
79{
80    size_t  size;           /* real size of memblock
81                               (from `content` to end of block) */
82    long    allocFromSystem;    /* ==0 -> it`s a block imported by gbm_put_mem */
83
84    struct  gbb_freedata content; /* startposition of block returned to user
85                                     or chain info for free blocks */
86};
87
88#define GBB_HEADER_SIZE (sizeof(struct gbb_data)-sizeof(struct gbb_freedata))
89
90static struct gbb_Cluster
91{
92    size_t size;  /* minimum size of memblocks in this cluster */
93    struct gbb_data *first; /* first free block */
94
95} gbb_cluster[GBB_CLUSTERS+1];
96
97/* @@@ */
98NOT4PERL void *GB_calloc(unsigned int nelem, unsigned int elsize)
99{
100    size_t size = nelem*elsize;
101    void *mem = malloc(size);
102
103    if (mem) {
104        memset(mem,0,size);
105    }
106    else {
107        fprintf(stderr,"Panic Error: insufficient memory: tried to get %i*%i bytes\n",nelem,elsize);
108    }
109    return mem;
110}
111
112char *GB_strdup(const char *p) {
113    /* does strdup(), but working with NULL
114     * (Note: use nulldup() instead!) 
115     */
116    return p ? strdup(p) : NULL;
117}
118
119char *GB_strduplen(const char *p, unsigned len) {
120    /* fast replacement for strdup, if len is known */
121    if (p) {
122        char *neu;
123
124        ad_assert(strlen(p) == len);
125        /* Note: Common reason for failure: a zero-char was manually printed by a GBS_global_string...-function */
126       
127        neu = (char*)malloc(len+1);
128        memcpy(neu, p, len+1);
129        return neu;
130    }
131    return 0;
132}
133
134char *GB_strpartdup(const char *start, const char *end) {
135    /* strdup of a part of a string (including 'start' and 'end')
136     * 'end' may point behind end of string -> copy only till zero byte
137     * if 'end'=('start'-1) -> return ""
138     * if 'end'<('start'-1) -> return 0
139     * if 'end' == NULL -> copy whole string
140     */
141
142    char *result;
143    if (end) {
144        int len = end-start+1;
145
146        if (len >= 0) {
147            const char *eos = memchr(start, 0, len);
148
149            if (eos) len = eos-start;
150            result = malloc(len+1);
151            memcpy(result, start, len);
152            result[len] = 0;
153        }
154        else {
155            result = 0;
156        }
157    }
158    else { /* end = 0 -> return copy of complete string */
159        result = nulldup(start);
160    }
161
162    return result;
163}
164
165char *GB_strndup(const char *start, int len) {
166    return GB_strpartdup(start, start+len-1);
167}
168
169NOT4PERL void *GB_recalloc(void *ptr, unsigned int oelem, unsigned int nelem, unsigned int elsize)
170{
171    size_t nsize = nelem*elsize;
172    void *mem = malloc(nsize);
173
174    if (mem) {
175        size_t osize = oelem*elsize;
176
177        if (nsize>=osize) {
178            memmove(mem, ptr, osize);
179            if (nsize>osize) {
180                memset(((char*)mem)+osize, 0, nsize-osize);
181            }
182        }
183        else {
184            memmove(mem, ptr, nsize);
185        }
186    }
187    else {
188        fprintf(stderr,"Panic Error: insufficient memory: tried to get %i*%i bytes\n",nelem,elsize);
189    }
190
191    return mem;
192}
193
194
195void gbm_init_mem(void)
196{
197    int i;
198    static int flag = 0;
199
200    if (flag) return;
201
202    flag = 1;
203    for (i=0;i<GBM_MAX_INDEX;i++)
204    {
205        memset((char *)&gbm_global[i],0,sizeof(struct gbm_struct));
206        gbm_global[i].tables[0] = 0;        /* CORE zero get mem */
207    }
208    gbm_global2.old_sbrk = (char *)sbrk(0);
209
210    /* init GBB:
211     * --------- */
212
213    gbb_cluster[0].size  = GBB_MINSIZE;
214    gbb_cluster[0].first = NULL;
215
216    for (i=1; i<GBB_CLUSTERS; i++)
217    {
218        long nextSize = gbb_cluster[i-1].size * (100+GBB_INCR);
219
220        nextSize /= 100;
221        nextSize >>= GBB_ALIGN;
222        nextSize ++;
223        nextSize <<= GBB_ALIGN;
224
225        gbb_cluster[i].size  = nextSize;
226        gbb_cluster[i].first = NULL;
227
228        /*printf("cluster %i: size=%i\n", i, gbb_cluster[i].size);*/
229    }
230
231    /* last cluster contains ALL bigger blocks */
232
233    gbb_cluster[GBB_CLUSTERS].size  = INT_MAX;
234    gbb_cluster[GBB_CLUSTERS].first = NULL;
235
236    /* give some block to memory-management (testwise) */
237
238#if (defined(DEBUG) && 0)
239    {
240
241        int i;
242
243        for (i=200; i<3000; i+=1)
244        {
245            char *someMem = (char*)calloc(1,(size_t)i);
246
247            if (someMem) gbm_put_memblk(someMem,i);
248        }
249    }
250#endif
251}
252
253void GB_memerr(void)
254{
255    GB_internal_error("memory allocation error - maybe you're out of swap space?");
256}
257
258#ifdef TEST_MEMBLKS
259
260#define TEST() testMemblocks(__FILE__,__LINE__)
261
262void testMemblocks(const char *file, int line)
263{
264    int idx;
265
266    for (idx=0; idx<GBB_CLUSTERS; idx++)
267    {
268        struct gbb_Cluster *cl = &(gbb_cluster[idx]);
269        struct gbb_data *blk = cl->first;
270
271        while (blk)
272        {
273            if (blk->size<cl->size)
274            {
275                fprintf(stderr, "Illegal block (size=%li) in cluster %i (size=%li) (%s,%i)\n", blk->size,idx,cl->size,file,line);
276                ad_assert(0);
277            }
278            blk = blk->content.next;
279        }
280    }
281}
282
283#else
284#   define TEST()
285#endif
286
287
288#if (MEMORY_TEST==0)
289
290static void imemerr(const char *why)
291{
292    GB_internal_errorf("Dangerous internal error: '%s'\n"
293                       "Inconsistent database: Do not overwrite old files with this database", why);
294}
295
296static int getClusterIndex(size_t size) /* searches the index of the
297                                           lowest cluster for that:
298                                           size <= cluster->size */
299{
300    int l,m,h;
301
302    if (size<GBB_MINSIZE) return 0;
303
304    l = 1;
305    h = GBB_CLUSTERS;
306
307    while (l!=h)
308    {
309        m = (l+h)/2;
310        if (gbb_cluster[m].size < size)  l = m+1;
311        else                 h = m;
312    }
313
314    ad_assert(l<=GBB_CLUSTERS);
315
316    return l;
317}
318
319void gbm_put_memblk(char *memblk, size_t size) /* gives any memory block (allocated or not)
320                                                  into the responsibility of this module;
321                                                  the block has to be aligned!!! */
322{
323    struct gbb_data  *block;
324    int idx;
325
326    TEST();
327
328#ifdef DUMP_MEMBLKS
329    printf("put %p (%li bytes)\n",memblk,size);
330#endif
331
332    if (size<(GBB_HEADER_SIZE+GBB_MINSIZE))
333    {
334        GB_internal_errorf("gmb_put_memblk() called with size below %zu bytes",
335                           GBB_HEADER_SIZE+GBB_MINSIZE);
336        return;
337    }
338
339    block              = (struct gbb_data *)memblk;
340    block->size            = size-GBB_HEADER_SIZE;
341    block->allocFromSystem = 0;
342
343    idx = getClusterIndex(block->size)-1;
344    ad_assert(idx>=0);
345
346    block->content.next     = gbb_cluster[idx].first;
347    block->content.magic    = GBB_MAGIC;
348    gbb_cluster[idx].first  = block;
349
350    ad_assert(idx==GBB_CLUSTERS || block->size>=gbb_cluster[idx].size);
351    TEST();
352}
353
354static char *gbm_get_memblk(size_t size)
355{
356    struct gbb_data  *block = NULL;
357    int           trials = GBB_MAX_TRIALS,
358        idx;
359
360    TEST();
361
362    idx = getClusterIndex(size);
363    ad_assert(gbb_cluster[idx].size>=size);
364
365    while (trials--)    /* search a cluster containing a block */
366    {
367        if ((block = gbb_cluster[idx].first)!=NULL) break;  /* found! */
368        if (idx==GBB_CLUSTERS) break;               /* last cluster! */
369        idx++;
370    }
371
372    if (!block) /* if no unused block -> allocate from system */
373    {
374        int allocationSize;
375
376    allocFromSys:
377
378        allocationSize = (idx==GBB_CLUSTERS
379                          ? (size_t)size
380                          : (size_t)(gbb_cluster[idx].size)) + GBB_HEADER_SIZE;
381
382        block  = (struct gbb_data *)GB_calloc(1, allocationSize);
383        if (!block) { GB_memerr(); return NULL; }
384
385        block->size = allocationSize-GBB_HEADER_SIZE;
386        block->allocFromSystem = 1;
387
388        ad_assert(block->size>=size);
389
390#ifdef DUMP_MEMBLKS
391        printf("allocated %li bytes\n", size);
392#endif
393    }
394    else
395    {
396        struct gbb_data **blockPtr = &(gbb_cluster[idx].first);
397
398        if (idx==GBB_CLUSTERS)  /* last cluster (test for block size necessary) */
399        {
400            while ((block=*blockPtr)!=NULL && block->size<size)
401                blockPtr = &(block->content.next);
402
403            if (!block) goto allocFromSys;
404            ad_assert(block->size>=size);
405        }
406
407        if (block->content.magic!=GBB_MAGIC) { imemerr("bad magic number if free block"); return NULL; }
408        *blockPtr = block->content.next;
409        memset((char*)&(block->content),0,size);    /* act like calloc() */
410
411#ifdef DUMP_MEMBLKS
412        printf("using unused block "
413               "(add=%p,size=%li, block->size=%li,cluster->size=%li)\n",
414               block, size, block->size,gbb_cluster[idx].size);
415#endif
416
417        ad_assert(block->size>=size);
418    }
419
420    ad_assert(block->size>=size);
421
422    TEST();
423
424    return (char*)&(block->content);
425}
426
427
428char *gbm_get_mem(size_t size, long index)
429{
430    unsigned long           nsize, pos;
431    char                   *erg;
432    struct gbm_data_struct *gds;
433    struct gbm_struct      *ggi;
434
435    if (size < sizeof(struct gbm_data_struct)) size = sizeof(struct gbm_data_struct);
436    index &= GBM_MAX_INDEX-1;
437    ggi = & gbm_global[index];
438    nsize = (size + (GBM_ALIGNED - 1)) & (-GBM_ALIGNED);
439
440    if (nsize > GBM_MAX_SIZE)
441    {
442        ggi->extern_data_size += nsize;
443        ggi->extern_data_items++;
444
445        erg = gbm_get_memblk((size_t)nsize);
446        return erg;
447    }
448
449    pos = nsize >> GBM_LD_ALIGNED;
450    if ( (gds = ggi->tables[pos]) )
451    {
452        ggi->tablecnt[pos]--;
453        erg = (char *)gds;
454        if (gds->magic != GBM_MAGIC)
455        {
456            printf("%lX!= %lX\n",gds->magic,(long)GBM_MAGIC);
457            GB_internal_error("Dangerous internal error: Inconsistent database: "
458                              "Do not overwrite old files with this database");
459        }
460        ggi->tables[pos] = ggi->tables[pos]->next;
461    }
462    else
463    {
464        if (ggi->size < nsize)
465        {
466            struct gbm_table_struct *gts = (struct gbm_table_struct *)GB_MEMALIGN(gbm_system_page_size, GBM_TABLE_SIZE);
467
468            if (!gts) { GB_memerr(); return NULL; }
469
470            memset((char *)gts,0,GBM_TABLE_SIZE);
471            ggi->gds = &gts->data[0];
472            gts->next = ggi->first; /* link tables */
473            ggi->first = gts;
474            ggi->size = GBM_TABLE_SIZE - sizeof(void *);
475            ggi->allsize += GBM_TABLE_SIZE;
476        }
477        erg = (char *)ggi->gds;
478        ggi->gds = (struct gbm_data_struct *)(((char *)ggi->gds) + nsize);
479        ggi->size -= (size_t)nsize;
480    }
481
482    ggi->useditems[pos]++;
483    memset(erg,0,nsize);
484
485    return erg;
486}
487
488void gbm_free_mem(char *data, size_t size, long index)
489{
490    long               nsize, pos;
491    struct gbm_struct *ggi;
492
493    if (size < sizeof(struct gbm_data_struct)) size = sizeof(struct gbm_data_struct);
494
495    index &= GBM_MAX_INDEX-1;
496    ggi = & gbm_global[index];
497    nsize = (size + (GBM_ALIGNED - 1)) & (-GBM_ALIGNED);
498
499    if (nsize > GBM_MAX_SIZE)
500    {
501        struct gbb_data *block;
502
503        if (gb_isMappedMemory(data))
504        {
505            block = (struct gbb_data *)data;
506
507            block->size = size-GBB_HEADER_SIZE;
508            block->allocFromSystem = 0;
509
510            /* printf("put mapped Block (size=%li)\n", size); */
511
512            if (size>=(GBB_HEADER_SIZE+GBB_MINSIZE))
513                gbm_put_memblk((char*)block, size);
514
515        }
516        else
517        {
518            block = (struct gbb_data *)(data-GBB_HEADER_SIZE);
519
520            ggi->extern_data_size -= (size_t)nsize;
521            ggi->extern_data_items--;
522
523            if (block->size<size) { imemerr("block size does not match"); return; }
524
525            if (block->allocFromSystem)
526            {
527                /* printf("free %li bytes\n", size);  */
528                free((char *)block);
529            }
530            else
531            {
532                /* printf("put unused block (size=%li block->size=%li)\n",
533                   size,block->size); */
534                gbm_put_memblk((char*)block,block->size + GBB_HEADER_SIZE);
535            }
536        }
537    }
538    else
539    {
540        if (gb_isMappedMemory(data)) return;    /*   @@@ reason: size may be shorter */
541        if ( ((struct gbm_data_struct *)data)->magic == GBM_MAGIC)
542            /* double free */
543        {
544            imemerr("double free");
545            return;
546        }
547
548        pos = nsize >> GBM_LD_ALIGNED;
549        ((struct gbm_data_struct *) data)->next = ggi->tables[pos];
550        ((struct gbm_data_struct *) data)->magic = GBM_MAGIC;
551        ggi->tables[pos] = ((struct gbm_data_struct *) data);
552        ggi->tablecnt[pos]++;
553        ggi->useditems[pos]--;
554    }
555}
556
557#endif /* MEMORY_TEST==0 */
558
559void gbm_debug_mem(GB_MAIN_TYPE *Main)
560{
561    int i;
562    int index;
563    long total = 0;
564    long index_total;
565    struct gbm_struct *ggi;
566
567    printf("Memory Debug Information:\n");
568    for (index = 0; index < GBM_MAX_INDEX; index++)
569    {
570        index_total = 0;
571        ggi = &gbm_global[index];
572        for (i = 0; i < GBM_MAX_TABLES; i++)
573        {
574            index_total += i * GBM_ALIGNED * (int) ggi->useditems[i];
575            total += i * GBM_ALIGNED * (int) ggi->useditems[i];
576
577            if (ggi->useditems[i] || ggi->tablecnt[i])
578            {
579                {
580                    int j;
581                    for (j = index; j < Main->keycnt; j+=GBM_MAX_INDEX ) {
582                        if (Main->keys[j].key){
583                            printf("%15s", Main->keys[j].key);
584                        }else{
585                            printf("%15s", "*** unused ****");
586                        }
587                    }
588                }
589                printf("\t'I=%3i' 'Size=%3i' * 'Items %4i' = 'size %7i'    'sum=%7li'   'totalsum=%7li' :   Free %3i\n",
590                       index,
591                       i * GBM_ALIGNED,
592                       (int) ggi->useditems[i],
593                       i * GBM_ALIGNED * (int) ggi->useditems[i],
594                       index_total,
595                       total,
596                       (int) ggi->tablecnt[i]);
597            }
598        }
599        if ( ggi->extern_data_size)
600        {
601            index_total += ggi->extern_data_size;
602            total += ggi->extern_data_size;
603            printf("\t\t'I=%3i' External Data Items=%3li = Sum=%3li  'sum=%7li'  'total=%7li\n",
604                   index,
605                   ggi->extern_data_items,
606                   (long)ggi->extern_data_size,
607                   index_total,
608                   total);
609        }
610    }
611
612    {
613        char *topofmem = (char *)sbrk(0);
614        printf("spbrk %lx old %lx size %ti\n",
615               (long)topofmem,
616               (long)gbm_global2.old_sbrk,
617               topofmem-gbm_global2.old_sbrk);
618    }
619}
Note: See TracBrowser for help on using the repository browser.