1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
|
/*
* Memory Allocator for Microsoft Windows.
* Only allows blocks of up to 64K minus overhead to be allocated.
*
* This code is based on the GNU malloc.c included with GNU emacs and is
* subject to the copyright restrictions listed therein. The original of
* malloc.c is included with this distribution. (I hope this approach is
* in accord with the license.)
*/
#ifdef _Windows
#include <mem.h>
extern char *NewSegment(void);
extern void FreeSegment(char *);
extern void abort(void);
extern int Exiting;
static void init_blocks(void);
static unsigned int find_block_index(unsigned long);
static void morecore(int);
char *Lmalloc(unsigned long);
char *Lrealloc(char *, unsigned long);
void Lfree(char *);
#else
#include <string.h>
#endif
/*
* nextf[i] is the pointer to the next free block of size bsize[i]. The
* smallest allocatable block is 8 bytes. The overhead information will
* go in the first long of the block, and the returned pointer will point
* to the second.
*/
#define ISALLOC ((char) 0xf7) /* magic byte that implies allocation */
#define ISFREE ((char) 0x54) /* magic byte that implies free block */
/* this is for error checking only */
/* What we have is a flag indicating whether memory is allocated, an index
in nextf[], and a size field; to realloc() memory we copy size bytes.
Since we are not allowing allocations of more than 64K, a short size
field is adequate for holding the actual size of any block. The size
field is only valid when the block is allocated.
*/
struct mhead {
char mh_alloc; /* ISALLOC or ISFREE */
unsigned char mh_index; /* index in nextf[] */
unsigned short mh_size; /* size */
};
/* Segments are connected in a linked list. The header is 16 bytes long and
a trailer of 16 bytes is used. This should hopefully insure that far
pointers to reasonable-sized structs will not wrap when incremented or
decremented
*/
#define SEGSIZE 0x010000L /* 64K */
#define SEGMAX ((int) (SEGSIZE - 32)) /* 64K - header - trailer */
struct shead {
struct shead *sh_next; /* pointer to next segment in list */
unsigned short sh_start; /* offset to first unallocated byte in segment */
unsigned short sh_free; /* amount of free space in segment */
long pad1, pad2; /* padding up to 16 byte boundary */
};
static struct shead *segs = 0;
/* BCC 4.0 seems to call malloc from its startup code (as do earlier */
/* versions). But under 4.0 an error occurs on exit if I do GlobalFree */
/* on the first segment that is created for this initial malloc. My best */
/* guess is that the cleanup code in BCC 4.0 calls GlobalFree directly */
/* instead of free, resulting in trying to free a block already free'd. */
/* So I record the first segment and don't free it in the cleanup code. */
/* This whole grossness should vanish with Win32s. */
static struct shead *firstseg = 0;
/* Access free-list pointer of a block. It is stored at block + 4.
This is not a field in the mhead structure because we want
sizeof (struct mhead) to describe the overhead for when the block
is in use, and we do not want the free-list pointer to count in that.
*/
#define CHAIN(a) \
(*(struct mhead **) (sizeof (char *) + (char *) (a)))
/* nextf[i] is free list of blocks of size bsize[i]. */
#define MAXINDEX 156
static struct mhead *nextf[MAXINDEX];
/* bsize[i] is the size of blocks of category i.
The shift up by one in bsize allows the range test in realloc to work.
(This range test is no longer used -- it was wrong) */
static unsigned int blocksizes[MAXINDEX + 1] = {0};
unsigned int *bsize = blocksizes + 1;
/* Function to call to issue a warning. */
#ifdef _Windows
static void (*warnfunction) (char *);
#else
static void (*warnfunction) ();
#endif
#define WARNING(s) if (warnfunction) (*warnfunction)(s)
/* initialize the block size array */
static void init_blocks(void)
{
register unsigned int i, j, k, m, n;
for (m = 8, i = 0, k = 0; i < 9; i++)
for (j = 0, n = m / 4; j < 4; j++, k++, m += n)
bsize[k] = m;
for (; k < MAXINDEX - 1; k++, m+= n)
bsize[k] = m;
m = SEGMAX;
bsize[k] = m;
}
/* Figure out which nextf[] area to use for a given size */
static unsigned int find_block_index(n)
register unsigned long n;
{
register long m;
register unsigned int i, j;
n += sizeof(struct mhead); /* add 4-byte header */
for (i = 0, m = 8; n > m && i < 9; i++, m <<= 1);
j = (unsigned int) (((n - 1) >> i) - 4);
return (i ? 1 + j + ((i - 1) << 2) : 0);
}
/* Initialize the allocator. */
void
malloc_init (warnfun)
void (*warnfun) ();
{
warnfunction = warnfun;
}
/* Clean up before shutdown */
void
malloc_cleanup()
{
struct shead *s;
while ((s = segs) != 0 && s != firstseg) {
segs = s->sh_next;
FreeSegment((char *) s);
}
}
static void
morecore (nu) /* ask system for more memory */
register int nu; /* size index to get more of */
{
register char *cp;
register unsigned int siz;
register struct shead *s, *nexts;
static int inited = 0;
if (! inited) {
init_blocks();
inited = 1;
}
/* check that nu is in range */
if (nu < 0 || nu >= MAXINDEX) {
WARNING("Can't allocate a block that large");
return;
}
/* calculate the size we need */
siz = bsize[nu];
/* see if there is a segment with enough space */
for (s = 0, nexts = segs; nexts; nexts = nexts->sh_next) {
if (siz <= nexts->sh_free) {
s = nexts;
break;
}
}
/* if we did not find one, get and initialize a new segment */
if (! s) {
s = (struct shead *) NewSegment();
if (! s) return;
s->sh_next = segs;
segs = s;
s->sh_free = SEGMAX;
s->sh_start = 16;
if (! firstseg) firstseg = segs;
}
/* double check the size */
if (!s || s->sh_free < siz) return;
/* get a new block from the segment and adjust the segment header */
cp = ((char *) s) + s->sh_start;
s->sh_start += siz;
s->sh_free -= siz;
/* save and initialize the new header */
nextf[nu] = (struct mhead *) cp;
((struct mhead *) cp) -> mh_alloc = ISFREE;
((struct mhead *) cp) -> mh_index = nu;
CHAIN ((struct mhead *) cp) = 0;
}
char *
Lmalloc (n) /* get a block */
unsigned long n;
{
register struct mhead *p;
register unsigned int nunits;
/* reject if requested block is too large */
if (n > SEGMAX - sizeof(struct mhead)) {
WARNING("Can't allocate a block that large");
return (0);
}
nunits = find_block_index(n);
/* reject if too large -- should not happen with earlier test */
if (nunits >= MAXINDEX) {
WARNING("Can't allocate a block that large");
return (0);
}
/* If there are no blocks of the appropriate size, go get some */
/* COULD SPLIT UP A LARGER BLOCK HERE ... ACT */
if (nextf[nunits] == 0)
morecore (nunits);
/* Get one block off the list, and set the new list head */
if ((p = nextf[nunits]) == 0)
return 0;
nextf[nunits] = CHAIN (p);
/* Check for free block clobbered */
/* If not for this check, we would gobble a clobbered free chain ptr */
/* and bomb out on the NEXT allocate of this size block */
if (p -> mh_alloc != ISFREE || p -> mh_index != nunits)
abort ();
/* Fill in the header info */
p -> mh_alloc = ISALLOC;
p -> mh_size = (unsigned short) n;
return (char *) (p + 1);
}
void Lfree (mem)
char *mem;
{
register struct mhead *p;
#ifdef _Windows
if (Exiting) return;
#endif /* _Windows */
{
register char *ap = mem;
if (ap == 0)
return;
p = (struct mhead *) ap - 1;
if (p -> mh_alloc != ISALLOC)
abort ();
}
{
register unsigned int nunits = p -> mh_index;
p -> mh_alloc = ISFREE;
/* Put this block on the free list. */
CHAIN (p) = nextf[nunits];
nextf[nunits] = p;
}
}
char *
Lrealloc (mem, n)
char *mem;
register unsigned long n;
{
register struct mhead *p;
register unsigned long tocopy;
register long nunits;
/* Check for maximum block size */
if (n > SEGMAX - sizeof(struct mhead)) {
WARNING("Can't allocate a block that large");
return(0);
}
/* if pointer is nil just malloc */
if ((p = (struct mhead *) mem) == 0)
return Lmalloc (n);
/* find the header information */
p--;
nunits = p -> mh_index;
tocopy = p -> mh_size;
/* If desired size fits in current block, just mark its size as changed. */
if (nunits == find_block_index(n))
{
p -> mh_size = (unsigned short) n;
return mem;
}
if (n < tocopy)
tocopy = n;
{
register char *new;
if ((new = Lmalloc (n)) == 0)
return 0;
memcpy (new, mem, (size_t) tocopy);
Lfree (mem);
return new;
}
}
|