# include "dgd.h" # ifndef STRUCT_AL # define STRUCT_AL 2 /* default memory alignment */ # endif # define MAGIC_MASK 0xff000000L /* magic number mask */ # define SIZE_MASK 0x00ffffffL /* size mask */ # define SM_MAGIC 0xc5000000L /* static mem */ # define DM_MAGIC 0xc6000000L /* dynamic mem */ # define UINTSIZE ALGN(sizeof(Uint), STRUCT_AL) # define SIZETSIZE ALGN(sizeof(size_t), STRUCT_AL) # ifdef DEBUG # define MOFFSET ALGN(sizeof(header), STRUCT_AL) # else # define MOFFSET UINTSIZE # endif typedef struct _chunk_ { Uint size; /* size of chunk */ struct _chunk_ *next; /* next chunk */ } chunk; # ifdef DEBUG typedef struct _header_ { Uint size; /* size of chunk */ int line; /* line it was allocated from */ char *file; /* file it was allocated from */ struct _header_ *prev; /* previous in list */ struct _header_ *next; /* next in list */ } header; # endif static allocinfo mstat; /* memory statistics */ /* * NAME: newmem() * DESCRIPTION: allocate new memory */ static char *newmem(size, list) size_t size; char **list; { char *mem; if (list != (char **) NULL) { size += ALGN(sizeof(char *), STRUCT_AL); } mem = (char *) malloc(size); if (mem == (char *) NULL) { fatal("out of memory"); } if (list != (char **) NULL) { *((char **) mem) = *list; *list = mem; mem += ALGN(sizeof(char *), STRUCT_AL); } return mem; } /* * static memory manager */ # define INIT_MEM 15360 /* initial static memory chunk */ # define SLIMIT (STRINGSZ + MOFFSET) # define SSMALL (MOFFSET + STRINGSZ / 8) # define SCHUNKS (STRINGSZ / STRUCT_AL - 1) # define LCHUNKS 32 typedef struct { size_t size; /* size of chunks in list */ chunk *list; /* list of chunks (possibly empty) */ } clist; static char *slist; /* list of static chunks */ static chunk *schunk; /* current chunk */ static size_t schunksz; /* size of current chunk */ static chunk *schunks[SCHUNKS]; /* lists of small free chunks */ static clist lchunks[LCHUNKS]; /* lists of large free chunks */ static unsigned int nlc; /* # elements in large chunk list */ static chunk *sflist; /* list of small unused chunks */ static int slevel; /* static level */ /* * NAME: lchunk() * DESCRIPTION: get the address of a list of large chunks */ static chunk **lchunk(size, new) register size_t size; bool new; { register unsigned int h, l, m; l = m = 0; h = nlc; while (l < h) { m = (l + h) >> 1; if (lchunks[m].size == size) { return &lchunks[m].list; /* found */ } else if (lchunks[m].size > size) { h = m; /* search in lower half */ } else { l = ++m; /* search in upper half */ } } if (!new) { return (chunk **) NULL; /* not found */ } /* make a new list */ if (nlc == LCHUNKS) { fatal("too many different large static chunks"); } for (l = nlc++; l > m; --l) { lchunks[l] = lchunks[l - 1]; } lchunks[m].size = size; lchunks[m].list = (chunk *) NULL; return &lchunks[m].list; } /* * NAME: salloc() * DESCRIPTION: allocate static memory */ static chunk *salloc(size) register size_t size; { register chunk *c; /* try lists of free chunks */ if (size >= SLIMIT) { register chunk **lc; lc = lchunk(size, FALSE); if (lc != (chunk **) NULL && *lc != (chunk *) NULL) { /* large chunk */ c = *lc; *lc = c->next; return c; } } else if ((c=schunks[(size - MOFFSET) / STRUCT_AL - 1]) != (chunk *) NULL) { /* small chunk */ schunks[(size - MOFFSET) / STRUCT_AL - 1] = c->next; return c; } /* try unused chunk list */ if (sflist != (chunk *) NULL && sflist->size >= size) { if (sflist->size - size <= MOFFSET) { /* remainder is too small to put in free list */ c = sflist; sflist = c->next; } else { register chunk *n; /* split the chunk in two */ c = sflist; n = (chunk *) ((char *) sflist + size); n->size = c->size - size; if (n->size <= SSMALL) { /* small chunk */ n->next = schunks[(n->size - MOFFSET) / STRUCT_AL - 1]; schunks[(n->size - MOFFSET) / STRUCT_AL - 1] = n; sflist = c->next; } else { /* large enough chunk */ n->next = c->next; sflist = n; } c->size = size; } return c; } /* try current chunk */ if (schunk == (chunk *) NULL || (schunk->size < size && (schunksz != 0 || size <= INIT_MEM))) { /* * allocate default static memory block */ if (schunk != (chunk *) NULL) { schunk->next = sflist; sflist = schunk; } schunk = (chunk *) newmem(INIT_MEM, &slist); mstat.smemsize += schunk->size = INIT_MEM; if (schunksz != 0) { /* fragmentation matters */ P_message("*** Ran out of static memory (increase static_chunk)\012"); /* LF */ } } if (schunk->size >= size) { if (schunk->size - size <= MOFFSET) { /* remainder is too small */ c = schunk; schunk = (chunk *) NULL; } else { c = schunk; schunk = (chunk *) ((char *) schunk + size); if ((schunk->size=c->size - size) <= SSMALL) { /* small chunk */ schunk->next = schunks[(schunk->size - MOFFSET) / STRUCT_AL -1]; schunks[(schunk->size - MOFFSET) / STRUCT_AL - 1] = schunk; schunk = (chunk *) NULL; } c->size = size; } return c; } /* allocate static memory directly */ c = (chunk *) newmem(size, &slist); mstat.smemsize += c->size = size; return c; } /* * NAME: sfree() * DESCRIPTION: free static memory */ static void sfree(c) register chunk *c; { if (c->size < SLIMIT) { /* small chunk */ c->next = schunks[(c->size - MOFFSET) / STRUCT_AL - 1]; schunks[(c->size - MOFFSET) / STRUCT_AL - 1] = c; } else { register chunk **lc; /* large chunk */ lc = lchunk(c->size, TRUE); c->next = *lc; *lc = c; } } /* * NAME: mstatic() * DESCRIPTION: enter static mode */ void m_static() { slevel++; } /* * NAME: mdynamic() * DESCRIPTION: reenter dynamic mode */ void m_dynamic() { --slevel; } /* * dynamic memory manager */ typedef struct _spnode_ { Uint size; /* size of chunk */ struct _spnode_ *parent; /* parent node */ struct _spnode_ *left; /* left child node */ struct _spnode_ *right; /* right child node */ } spnode; static size_t dchunksz; /* dynamic chunk size */ static spnode *dtree; /* splay tree of large dynamic free chunks */ /* * NAME: insert() * DESCRIPTION: insert a chunk in the splay tree */ static void insert(c) chunk *c; { register spnode *n, *t; register spnode *l, *r; register Uint size; n = dtree; dtree = t = (spnode *) c; t->parent = (spnode *) NULL; if (n == (spnode *) NULL) { /* first in splay tree */ t->left = (spnode *) NULL; t->right = (spnode *) NULL; } else { size = t->size; l = r = t; for (;;) { if (n->size < size) { if ((t=n->right) == (spnode *) NULL) { l->right = n; n->parent = l; r->left = (spnode *) NULL; break; /* finished */ } if (t->size >= size) { l->right = n; n->parent = l; l = n; n = t; } else { /* rotate */ if ((n->right=t->left) != (spnode *) NULL) { t->left->parent = n; } t->left = n; n->parent = t; l->right = t; t->parent = l; if ((n=t->right) == (spnode *) NULL) { r->left = (spnode *) NULL; break; /* finished */ } l = t; } } else { if ((t=n->left) == (spnode *) NULL) { r->left = n; n->parent = r; l->right = (spnode *) NULL; break; /* finished */ } if (t->size < size) { r->left = n; n->parent = r; r = n; n = t; } else { /* rotate */ if ((n->left=t->right) != (spnode *) NULL) { t->right->parent = n; } t->right = n; n->parent = t; r->left = t; t->parent = r; if ((n=t->left) == (spnode *) NULL) { l->right = (spnode *) NULL; break; /* finished */ } r = t; } } } /* exchange left and right subtree */ n = dtree; t = n->left; n->left = n->right; n->right = t; } } /* * NAME: seek() * DESCRIPTION: find a chunk of the proper size in the splay tree */ static chunk *seek(size) register Uint size; { spnode dummy; register spnode *n, *t; register spnode *l, *r; if ((n=dtree) == (spnode *) NULL) { /* empty splay tree */ return (chunk *) NULL; } else { l = r = &dummy; for (;;) { if (n->size < size) { if ((t=n->right) == (spnode *) NULL) { l->right = n; n->parent = l; if (r == &dummy) { /* all chunks are too small */ dtree = dummy.right; dtree->parent = (spnode *) NULL; return (chunk *) NULL; } if ((r->parent->left=r->right) != (spnode *) NULL) { r->right->parent = r->parent; } n = r; break; /* finished */ } if (t->size >= size) { l->right = n; n->parent = l; l = n; n = t; } else { /* rotate */ if ((n->right=t->left) != (spnode *) NULL) { t->left->parent = n; } t->left = n; n->parent = t; l->right = t; t->parent = l; if ((n=t->right) == (spnode *) NULL) { if (r == &dummy) { /* all chunks are too small */ dtree = dummy.right; dtree->parent = (spnode *) NULL; return (chunk *) NULL; } if ((r->parent->left=r->right) != (spnode *) NULL) { r->right->parent = r->parent; } n = r; break; /* finished */ } l = t; } } else { if ((t=n->left) == (spnode *) NULL) { if ((r->left=n->right) != (spnode *) NULL) { n->right->parent = r; } l->right = (spnode *) NULL; break; /* finished */ } if (t->size < size) { r->left = n; n->parent = r; r = n; n = t; } else { /* rotate */ if ((n->left=t->right) != (spnode *) NULL) { t->right->parent = n; } if (t->left == (spnode *) NULL) { r->left = n; n->parent = r; l->right = (spnode *) NULL; n = t; break; /* finished */ } t->right = n; n->parent = t; r->left = t; t->parent = r; r = t; n = t->left; } } } n->parent = (spnode *) NULL; if ((n->right=dummy.left) != (spnode *) NULL) { dummy.left->parent = n; } if ((n->left=dummy.right) != (spnode *) NULL) { dummy.right->parent = n; } return (chunk *) (dtree = n); } } /* * NAME: delete() * DESCRIPTION: delete a chunk from the splay tree */ static void delete(c) chunk *c; { register spnode *t, *r; register spnode *p, *n; n = (spnode *) c; p = n->parent; if (n->left == (spnode *) NULL) { /* there is no left subtree */ if (p == (spnode *) NULL) { if ((dtree=n->right) != (spnode *) NULL) { dtree->parent = (spnode *) NULL; } } else if (n == p->left) { if ((p->left=n->right) != (spnode *) NULL) { p->left->parent = p; } } else if ((p->right=n->right) != (spnode *) NULL) { p->right->parent = p; } } else { t = n->left; /* walk to the right in the left subtree */ while ((r=t->right) != (spnode *) NULL) { if ((t->right=r->left) != (spnode *) NULL) { r->left->parent = t; } r->left = t; t->parent = r; t = r; } if (p == (spnode *) NULL) { dtree = t; } else if (n == p->left) { p->left = t; } else { p->right = t; } t->parent = p; if ((t->right=n->right) != (spnode *) NULL) { t->right->parent = t; } } } # define DSMALL 64 # define DLIMIT (DSMALL + MOFFSET) # define DCHUNKS (DSMALL / STRUCT_AL - 1) # define DCHUNKSZ 32768 static char *dlist; /* list of dynamic memory chunks */ static chunk *dchunks[DCHUNKS]; /* list of free small chunks */ static chunk *dchunk; /* chunk of small chunks */ /* * NAME: dalloc() * DESCRIPTION: allocate dynamic memory */ static chunk *dalloc(size) register size_t size; { register chunk *c; register char *p; register size_t sz; if (dchunksz == 0) { /* * memory manager hasn't been initialized yet */ c = (chunk *) newmem(size, (char **) NULL); c->size = size; return c; } if (size < DLIMIT) { /* * small chunk */ if ((c=dchunks[(size - MOFFSET) / STRUCT_AL - 1]) != (chunk *) NULL) { /* small chunk from free list */ dchunks[(size - MOFFSET) / STRUCT_AL - 1] = c->next; return c; } if (dchunk == (chunk *) NULL) { /* get new chunks chunk */ dchunk = dalloc(DCHUNKSZ); /* cannot use alloc() here */ p = (char *) dchunk + UINTSIZE; ((chunk *) p)->size = dchunk->size - UINTSIZE - SIZETSIZE; dchunk->size |= DM_MAGIC; dchunk = (chunk *) p; } sz = dchunk->size - size; c = dchunk; c->size = size; if (sz >= DLIMIT - STRUCT_AL) { /* enough is left for another small chunk */ dchunk = (chunk *) ((char *) c + size); dchunk->size = sz; } else { /* waste sz bytes of memory */ dchunk = (chunk *) NULL; } return c; } size += SIZETSIZE; c = seek((Uint) size); if (c != (chunk *) NULL) { /* * remove from free list */ delete(c); } else { /* * get new dynamic chunk */ for (sz = dchunksz; sz < size + SIZETSIZE + UINTSIZE; sz += dchunksz) ; p = newmem(sz, &dlist); mstat.dmemsize += sz; /* no previous chunk */ *(size_t *) p = 0; c = (chunk *) (p + SIZETSIZE); /* initialize chunk */ c->size = sz - SIZETSIZE - UINTSIZE; p += c->size; *(size_t *) p = c->size; /* no following chunk */ p += SIZETSIZE; ((chunk *) p)->size = 0; } if ((sz=c->size - size) >= DLIMIT + SIZETSIZE) { /* * split block, put second part in free list */ c->size = size; p = (char *) c + size - SIZETSIZE; *(size_t *) p = size; p += SIZETSIZE; ((chunk *) p)->size = sz; *((size_t *) (p + sz - SIZETSIZE)) = sz; insert((chunk *) p); /* add to free list */ } return c; } /* * NAME: dfree() * DESCRIPTION: free dynamic memory */ static void dfree(c) register chunk *c; { register char *p; if (dchunksz == 0) { /* * memory manager not yet initialized */ free((char *) c); return; } if (c->size < DLIMIT) { /* small chunk */ c->next = dchunks[(c->size - MOFFSET) / STRUCT_AL - 1]; dchunks[(c->size - MOFFSET) / STRUCT_AL - 1] = c; return; } p = (char *) c - SIZETSIZE; if (*(size_t *) p != 0) { p -= *(size_t *) p - SIZETSIZE; if ((((chunk *) p)->size & MAGIC_MASK) == 0) { /* * merge with previous block */ delete((chunk *) p); ((chunk *) p)->size += c->size; c = (chunk *) p; *((size_t *) (p + c->size - SIZETSIZE)) = c->size; } } p = (char*) c + c->size; if (((chunk *) p)->size != 0 && (((chunk *) p)->size & MAGIC_MASK) == 0) { /* * merge with next block */ delete((chunk *) p); c->size += ((chunk *) p)->size; *((size_t *) ((char *) c + c->size - SIZETSIZE)) = c->size; } insert(c); /* add to free list */ } /* * NAME: mem->init() * DESCRIPTION: initialize memory manager */ void m_init(ssz, dsz) size_t ssz, dsz; { schunksz = ALGN(ssz, STRUCT_AL); dchunksz = ALGN(dsz, STRUCT_AL); if (schunksz != 0) { if (schunk != (chunk *) NULL) { schunk->next = sflist; sflist = schunk; } schunk = (chunk *) newmem(schunksz, &slist); mstat.smemsize += schunk->size = schunksz; } } # ifdef DEBUG static header *hlist; /* list of all dynamic memory chunks */ # endif /* * NAME: mem->alloc() * DESCRIPTION: allocate memory */ # ifdef DEBUG char *m_alloc(size, file, line) register size_t size; char *file; int line; # else char *m_alloc(size) register size_t size; # endif { register chunk *c; # ifdef DEBUG if (size == 0) { fatal("m_alloc(0)"); } # endif size = ALGN(size + MOFFSET, STRUCT_AL); # ifndef DEBUG if (size < ALGN(sizeof(chunk), STRUCT_AL)) { size = ALGN(sizeof(chunk), STRUCT_AL); } # endif if (slevel > 0) { c = salloc(size); mstat.smemused += c->size; c->size |= SM_MAGIC; } else { c = dalloc(size); mstat.dmemused += c->size; c->size |= DM_MAGIC; # ifdef DEBUG ((header *) c)->prev = (header *) NULL; ((header *) c)->next = hlist; if (hlist != (header *) NULL) { hlist->prev = (header *) c; } hlist = (header *) c; # endif } # ifdef DEBUG ((header *) c)->file = file; ((header *) c)->line = line; # endif return (char *) c + MOFFSET; } /* * NAME: mem->free() * DESCRIPTION: free memory */ void m_free(mem) char *mem; { register chunk *c; c = (chunk *) (mem - MOFFSET); if ((c->size & MAGIC_MASK) == SM_MAGIC) { c->size &= SIZE_MASK; mstat.smemused -= c->size; sfree(c); } else if ((c->size & MAGIC_MASK) == DM_MAGIC) { c->size &= SIZE_MASK; mstat.dmemused -= c->size; # ifdef DEBUG if (((header *) c)->next != (header *) NULL) { ((header *) c)->next->prev = ((header *) c)->prev; } if (((header *) c) == hlist) { hlist = ((header *) c)->next; } else { ((header *) c)->prev->next = ((header *) c)->next; } # endif dfree(c); } else { fatal("bad pointer in m_free"); } } /* * NAME: mem->realloc() * DESCRIPTION: reallocate memory */ # ifdef DEBUG char *m_realloc(mem, size1, size2, file, line) char *mem, *file; register size_t size1, size2; int line; # else char *m_realloc(mem, size1, size2) char *mem; register size_t size1, size2; # endif { register chunk *c1, *c2; if (mem == (char *) NULL) { if (size2 == 0) { return (char *) NULL; } # ifdef DEBUG return m_alloc(size2, file, line); # else return m_alloc(size2); # endif } if (size2 == 0) { m_free(mem); return (char *) NULL; } size2 = ALGN(size2 + MOFFSET, STRUCT_AL); # ifndef DEBUG if (size2 < ALGN(sizeof(chunk), STRUCT_AL)) { size2 = ALGN(sizeof(chunk), STRUCT_AL); } # endif c1 = (chunk *) (mem - MOFFSET); if ((c1->size & MAGIC_MASK) == SM_MAGIC) { # ifdef DEBUG if (size1 > (c1->size & SIZE_MASK)) { fatal("bad size1 in m_realloc"); } # endif if ((c1->size & SIZE_MASK) < size2) { c2 = salloc(size2); if (size1 != 0) { memcpy((char *) c2 + MOFFSET, mem, size1); } c1->size &= SIZE_MASK; mstat.smemused += c2->size - c1->size; c2->size |= SM_MAGIC; sfree(c1); c1 = c2; } } else if ((c1->size & MAGIC_MASK) == DM_MAGIC) { # ifdef DEBUG if (size1 > (c1->size & SIZE_MASK)) { fatal("bad size1 in m_realloc"); } # endif if ((c1->size & SIZE_MASK) < ((size2 < DLIMIT) ? size2 : size2 + SIZETSIZE)) { c2 = dalloc(size2); if (size1 != 0) { memcpy((char *) c2 + MOFFSET, mem, size1); } c1->size &= SIZE_MASK; mstat.dmemused += c2->size - c1->size; c2->size |= DM_MAGIC; # ifdef DEBUG ((header *) c2)->next = ((header *) c1)->next; if (((header *) c1)->next != (header *) NULL) { ((header *) c2)->next->prev = (header *) c2; } ((header *) c2)->prev = ((header *) c1)->prev; if (((header *) c1) == hlist) { hlist = (header *) c2; } else { ((header *) c2)->prev->next = (header *) c2; } # endif dfree(c1); c1 = c2; } } else { fatal("bad pointer in m_realloc"); } # ifdef DEBUG ((header *) c1)->file = file; ((header *) c1)->line = line; # endif return (char *) c1 + MOFFSET; } /* * NAME: mem->check() * DESCRIPTION: return TRUE if there is enough static memory left, or FALSE * otherwise */ bool m_check() { if (schunk == (chunk *) NULL) { return FALSE; } else { return (schunksz == 0 || schunk->size >= schunksz); } } /* * NAME: mem->purge() * DESCRIPTION: purge dynamic memory */ void m_purge() { register char *p; # ifdef DEBUG while (hlist != (header *) NULL) { char buf[160]; register size_t n; n = (hlist->size & SIZE_MASK) - MOFFSET; if (n >= DLIMIT) { n -= SIZETSIZE; } sprintf(buf, "FREE(%08lx/%u), %s line %u:\012", /* LF */ (unsigned long) (hlist + 1), n, hlist->file, hlist->line); if (n > 26) { n = 26; } for (p = (char *) (hlist + 1); n > 0; --n, p++) { if (*p >= ' ') { sprintf(buf + strlen(buf), " '%c", *p); } else { sprintf(buf + strlen(buf), " %02x", UCHAR(*p)); } } strcat(buf, "\012"); /* LF */ P_message(buf); m_free((char *) (hlist + 1)); } # endif /* purge dynamic memory */ while (dlist != (char *) NULL) { p = dlist; dlist = *(char **) p; free(p); } memset(dchunks, '\0', sizeof(dchunks)); dchunk = (chunk *) NULL; dtree = (spnode *) NULL; mstat.dmemsize = mstat.dmemused = 0; if (schunksz != 0 && (schunk == (chunk *) NULL || schunk->size < schunksz || (mstat.smemsize - mstat.smemused) * 2 < schunksz * 3)) { /* expand static memory */ if (schunk != (chunk *) NULL) { schunk->next = sflist; sflist = schunk; } schunk = (chunk *) newmem(schunksz, &slist); mstat.smemsize += schunk->size = schunksz; } } /* * NAME: mem->info() * DESCRIPTION: return informaton about memory usage */ allocinfo *m_info() { return &mstat; } /* * NAME: mem->finish() * DESCRIPTION: finish up memory manager */ void m_finish() { register char *p; schunksz = 0; dchunksz = 0; /* purge dynamic memory */ # ifdef DEBUG hlist = (header *) NULL; # endif m_purge(); /* purge static memory */ while (slist != (char *) NULL) { p = slist; slist = *(char **) p; free(p); } memset(schunks, '\0', sizeof(schunks)); memset(lchunks, '\0', sizeof(lchunks)); nlc = 0; schunk = (chunk *) NULL; sflist = (chunk *) NULL; slevel = 0; mstat.smemsize = mstat.smemused = 0; }