7c60fe2d57b95bbc9ea8751e8d1df7399800058f
[ocaml-ancient.git] / mmalloc / mmalloc.c
1 /* Memory allocator `malloc'.
2    Copyright 1990, 1991, 1992 Free Software Foundation
3
4    Written May 1989 by Mike Haertel.
5    Heavily modified Mar 1992 by Fred Fish for mmap'd version.
6
7 The GNU C Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11
12 The GNU C Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public
18 License along with the GNU C Library; see the file COPYING.LIB.  If
19 not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.
21
22    The author may be reached (Email) at the address mike@ai.mit.edu,
23    or (US mail) as Mike Haertel c/o Free Software Foundation. */
24
25 #include <string.h>     /* Prototypes for memcpy, memmove, memset, etc */
26
27 #include "mmprivate.h"
28
29 /* Prototypes for local functions */
30
31 static int initialize PARAMS ((struct mdesc *));
32 static PTR morecore PARAMS ((struct mdesc *, size_t));
33 static PTR align PARAMS ((struct mdesc *, size_t));
34
35 /* Aligned allocation.  */
36
37 static PTR
38 align (mdp, size)
39   struct mdesc *mdp;
40   size_t size;
41 {
42   PTR result;
43   unsigned long int adj;
44
45   result = mdp -> morecore (mdp, size);
46   adj = RESIDUAL (result, BLOCKSIZE);
47   if (adj != 0)
48     {
49       adj = BLOCKSIZE - adj;
50       mdp -> morecore (mdp, adj);
51       result = (char *) result + adj;
52     }
53   return (result);
54 }
55
56 /* Set everything up and remember that we have.  */
57
58 static int
59 initialize (mdp)
60   struct mdesc *mdp;
61 {
62   mdp -> heapsize = HEAP / BLOCKSIZE;
63   mdp -> heapinfo = (malloc_info *) 
64     align (mdp, mdp -> heapsize * sizeof (malloc_info));
65   if (mdp -> heapinfo == NULL)
66     {
67       return (0);
68     }
69   memset ((PTR)mdp -> heapinfo, 0, mdp -> heapsize * sizeof (malloc_info));
70   mdp -> heapinfo[0].free.size = 0;
71   mdp -> heapinfo[0].free.next = mdp -> heapinfo[0].free.prev = 0;
72   mdp -> heapindex = 0;
73   mdp -> heapbase = (char *) mdp -> heapinfo;
74   mdp -> flags |= MMALLOC_INITIALIZED;
75   return (1);
76 }
77
78 /* Get neatly aligned memory, initializing or
79    growing the heap info table as necessary. */
80
81 static PTR
82 morecore (mdp, size)
83   struct mdesc *mdp;
84   size_t size;
85 {
86   PTR result;
87   malloc_info *newinfo, *oldinfo;
88   size_t newsize;
89
90   result = align (mdp, size);
91   if (result == NULL)
92     {
93       return (NULL);
94     }
95
96   /* Check if we need to grow the info table.  */
97   if ((size_t) BLOCK ((char *) result + size) > mdp -> heapsize)
98     {
99       newsize = mdp -> heapsize;
100       while ((size_t) BLOCK ((char *) result + size) > newsize)
101         {
102           newsize *= 2;
103         }
104       newinfo = (malloc_info *) align (mdp, newsize * sizeof (malloc_info));
105       if (newinfo == NULL)
106         {
107           mdp -> morecore (mdp, -size);
108           return (NULL);
109         }
110       memset ((PTR) newinfo, 0, newsize * sizeof (malloc_info));
111       memcpy ((PTR) newinfo, (PTR) mdp -> heapinfo,
112               mdp -> heapsize * sizeof (malloc_info));
113       oldinfo = mdp -> heapinfo;
114       newinfo[BLOCK (oldinfo)].busy.type = 0;
115       newinfo[BLOCK (oldinfo)].busy.info.size
116         = BLOCKIFY (mdp -> heapsize * sizeof (malloc_info));
117       mdp -> heapinfo = newinfo;
118       __mmalloc_free (mdp, (PTR)oldinfo);
119       mdp -> heapsize = newsize;
120     }
121
122   mdp -> heaplimit = BLOCK ((char *) result + size);
123   return (result);
124 }
125
126 /* Allocate memory from the heap.  */
127
128 PTR
129 mmalloc (md, size)
130   PTR md;
131   size_t size;
132 {
133   struct mdesc *mdp;
134   PTR result;
135   size_t block, blocks, lastblocks, start;
136   register size_t i;
137   struct list *next;
138   register size_t log;
139
140   if (size == 0)
141     {
142       return (NULL);
143     }
144
145   mdp = MD_TO_MDP (md);
146       
147   if (mdp -> mmalloc_hook != NULL)
148     {
149       return ((*mdp -> mmalloc_hook) (md, size));
150     }
151
152   if (!(mdp -> flags & MMALLOC_INITIALIZED))
153     {
154       if (!initialize (mdp))
155         {
156           return (NULL);
157         }
158     }
159
160   if (size < sizeof (struct list))
161     {
162       size = sizeof (struct list);
163     }
164
165   /* Determine the allocation policy based on the request size.  */
166   if (size <= BLOCKSIZE / 2)
167     {
168       /* Small allocation to receive a fragment of a block.
169          Determine the logarithm to base two of the fragment size. */
170       log = 1;
171       --size;
172       while ((size /= 2) != 0)
173         {
174           ++log;
175         }
176
177       /* Look in the fragment lists for a
178          free fragment of the desired size. */
179       next = mdp -> fraghead[log].next;
180       if (next != NULL)
181         {
182           /* There are free fragments of this size.
183              Pop a fragment out of the fragment list and return it.
184              Update the block's nfree and first counters. */
185           result = (PTR) next;
186           next -> prev -> next = next -> next;
187           if (next -> next != NULL)
188             {
189               next -> next -> prev = next -> prev;
190             }
191           block = BLOCK (result);
192           if (--mdp -> heapinfo[block].busy.info.frag.nfree != 0)
193             {
194               mdp -> heapinfo[block].busy.info.frag.first =
195                 RESIDUAL (next -> next, BLOCKSIZE) >> log;
196             }
197
198           /* Update the statistics.  */
199           mdp -> heapstats.chunks_used++;
200           mdp -> heapstats.bytes_used += 1 << log;
201           mdp -> heapstats.chunks_free--;
202           mdp -> heapstats.bytes_free -= 1 << log;
203         }
204       else
205         {
206           /* No free fragments of the desired size, so get a new block
207              and break it into fragments, returning the first.  */
208           result = mmalloc (md, BLOCKSIZE);
209           if (result == NULL)
210             {
211               return (NULL);
212             }
213
214           /* Link all fragments but the first into the free list.  */
215           for (i = 1; i < (size_t) (BLOCKSIZE >> log); ++i)
216             {
217               next = (struct list *) ((char *) result + (i << log));
218               next -> next = mdp -> fraghead[log].next;
219               next -> prev = &mdp -> fraghead[log];
220               next -> prev -> next = next;
221               if (next -> next != NULL)
222                 {
223                   next -> next -> prev = next;
224                 }
225             }
226
227           /* Initialize the nfree and first counters for this block.  */
228           block = BLOCK (result);
229           mdp -> heapinfo[block].busy.type = log;
230           mdp -> heapinfo[block].busy.info.frag.nfree = i - 1;
231           mdp -> heapinfo[block].busy.info.frag.first = i - 1;
232
233           mdp -> heapstats.chunks_free += (BLOCKSIZE >> log) - 1;
234           mdp -> heapstats.bytes_free += BLOCKSIZE - (1 << log);
235           mdp -> heapstats.bytes_used -= BLOCKSIZE - (1 << log);
236         }
237     }
238   else
239     {
240       /* Large allocation to receive one or more blocks.
241          Search the free list in a circle starting at the last place visited.
242          If we loop completely around without finding a large enough
243          space we will have to get more memory from the system.  */
244       blocks = BLOCKIFY(size);
245       start = block = MALLOC_SEARCH_START;
246       while (mdp -> heapinfo[block].free.size < blocks)
247         {
248           block = mdp -> heapinfo[block].free.next;
249           if (block == start)
250             {
251               /* Need to get more from the system.  Check to see if
252                  the new core will be contiguous with the final free
253                  block; if so we don't need to get as much.  */
254               block = mdp -> heapinfo[0].free.prev;
255               lastblocks = mdp -> heapinfo[block].free.size;
256               if (mdp -> heaplimit != 0 &&
257                   block + lastblocks == mdp -> heaplimit &&
258                   mdp -> morecore (mdp, 0) == ADDRESS(block + lastblocks) &&
259                   (morecore (mdp, (blocks - lastblocks) * BLOCKSIZE)) != NULL)
260                 {
261                   /* Which block we are extending (the `final free
262                      block' referred to above) might have changed, if
263                      it got combined with a freed info table.  */
264                   block = mdp -> heapinfo[0].free.prev;
265
266                   mdp -> heapinfo[block].free.size += (blocks - lastblocks);
267                   mdp -> heapstats.bytes_free +=
268                       (blocks - lastblocks) * BLOCKSIZE;
269                   continue;
270                 }
271               result = morecore(mdp, blocks * BLOCKSIZE);
272               if (result == NULL)
273                 {
274                   return (NULL);
275                 }
276               block = BLOCK (result);
277               mdp -> heapinfo[block].busy.type = 0;
278               mdp -> heapinfo[block].busy.info.size = blocks;
279               mdp -> heapstats.chunks_used++;
280               mdp -> heapstats.bytes_used += blocks * BLOCKSIZE;
281               return (result);
282             }
283         }
284
285       /* At this point we have found a suitable free list entry.
286          Figure out how to remove what we need from the list. */
287       result = ADDRESS(block);
288       if (mdp -> heapinfo[block].free.size > blocks)
289         {
290           /* The block we found has a bit left over,
291              so relink the tail end back into the free list. */
292           mdp -> heapinfo[block + blocks].free.size
293             = mdp -> heapinfo[block].free.size - blocks;
294           mdp -> heapinfo[block + blocks].free.next
295             = mdp -> heapinfo[block].free.next;
296           mdp -> heapinfo[block + blocks].free.prev
297             = mdp -> heapinfo[block].free.prev;
298           mdp -> heapinfo[mdp -> heapinfo[block].free.prev].free.next
299             = mdp -> heapinfo[mdp -> heapinfo[block].free.next].free.prev
300               = mdp -> heapindex = block + blocks;
301         }
302       else
303         {
304           /* The block exactly matches our requirements,
305              so just remove it from the list. */
306           mdp -> heapinfo[mdp -> heapinfo[block].free.next].free.prev
307             = mdp -> heapinfo[block].free.prev;
308           mdp -> heapinfo[mdp -> heapinfo[block].free.prev].free.next
309             = mdp -> heapindex = mdp -> heapinfo[block].free.next;
310           mdp -> heapstats.chunks_free--;
311         }
312
313       mdp -> heapinfo[block].busy.type = 0;
314       mdp -> heapinfo[block].busy.info.size = blocks;
315       mdp -> heapstats.chunks_used++;
316       mdp -> heapstats.bytes_used += blocks * BLOCKSIZE;
317       mdp -> heapstats.bytes_free -= blocks * BLOCKSIZE;
318     }
319
320   return (result);
321 }
322
323 /* When using this package, provide a version of malloc/realloc/free built
324    on top of it, so that if we use the default sbrk() region we will not
325    collide with another malloc package trying to do the same thing, if
326    the application contains any "hidden" calls to malloc/realloc/free (such
327    as inside a system library). */
328
329 PTR
330 malloc (size)
331   size_t size;
332 {
333   PTR result;
334
335   result = mmalloc ((PTR) NULL, size);
336   return (result);
337 }