File: mswalloc.c

package info (click to toggle)
xlispstat 3.52.14-1
  • links: PTS
  • area: main
  • in suites: potato
  • size: 7,560 kB
  • ctags: 12,676
  • sloc: ansic: 91,357; lisp: 21,759; sh: 1,525; makefile: 521; csh: 1
file content (326 lines) | stat: -rw-r--r-- 8,725 bytes parent folder | download | duplicates (4)
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;
  }
}