* emacs.c (main): Correct spelling of HAVE_X_WINDOW to
[bpt/emacs.git] / src / ralloc.c
CommitLineData
dcfdbac7 1/* Block-relocating memory allocator.
956ace37 2 Copyright (C) 1992 Free Software Foundation, Inc.
dcfdbac7
JB
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 1, or (at your option)
9any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
18the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20/* NOTES:
21
22 Only relocate the blocs neccessary for SIZE in r_alloc_sbrk,
23 rather than all of them. This means allowing for a possible
24 hole between the first bloc and the end of malloc storage. */
25
2c46d29f 26#ifdef emacs
aef4d570 27
dcfdbac7 28#include "config.h"
956ace37 29#include "lisp.h" /* Needed for VALBITS. */
2c46d29f 30
aef4d570
RM
31#undef NULL
32
f275fd9a
RS
33/* The important properties of this type are that 1) it's a pointer, and
34 2) arithmetic on it should work as if the size of the object pointed
35 to has a size of 1. */
36#ifdef __STDC__
37typedef void *POINTER;
38#else
39typedef char *POINTER;
40#endif
41
42typedef unsigned long SIZE;
43
2c46d29f
RS
44/* Declared in dispnew.c, this version doesn't screw up if regions
45 overlap. */
46extern void safe_bcopy ();
2c46d29f 47
aef4d570
RM
48#include "getpagesize.h"
49
50#else /* Not emacs. */
51
2c46d29f 52#include <stddef.h>
aef4d570 53
2c46d29f
RS
54typedef size_t SIZE;
55typedef void *POINTER;
aef4d570 56
aef4d570
RM
57#include <unistd.h>
58#include <malloc.h>
59#include <string.h>
60
2c46d29f 61#define safe_bcopy(x, y, z) memmove (y, x, z)
2c46d29f 62
aef4d570 63#endif /* emacs. */
dcfdbac7
JB
64
65#define NIL ((POINTER) 0)
66
2c46d29f
RS
67/* A flag to indicate whether we have initialized ralloc yet. For
68 Emacs's sake, please do not make this local to malloc_init; on some
69 machines, the dumping procedure makes all static variables
70 read-only. On these machines, the word static is #defined to be
71 the empty string, meaning that r_alloc_initialized becomes an
72 automatic variable, and loses its value each time Emacs is started up. */
73static int r_alloc_initialized = 0;
74
75static void r_alloc_init ();
dcfdbac7 76\f
956ace37
JB
77/* Declarations for working with the malloc, ralloc, and system breaks. */
78
bbc60227
RM
79/* Function to set the real break value. */
80static POINTER (*real_morecore) ();
dcfdbac7
JB
81
82/* The break value, as seen by malloc (). */
83static POINTER virtual_break_value;
84
85/* The break value, viewed by the relocatable blocs. */
86static POINTER break_value;
87
88/* The REAL (i.e., page aligned) break value of the process. */
89static POINTER page_break_value;
90
91/* Macros for rounding. Note that rounding to any value is possible
92 by changing the definition of PAGE. */
93#define PAGE (getpagesize ())
94#define ALIGNED(addr) (((unsigned int) (addr) & (PAGE - 1)) == 0)
2c46d29f 95#define ROUNDUP(size) (((unsigned int) (size) + PAGE - 1) & ~(PAGE - 1))
dcfdbac7 96#define ROUND_TO_PAGE(addr) (addr & (~(PAGE - 1)))
dcfdbac7 97\f
956ace37
JB
98/* Functions to get and return memory from the system. */
99
dcfdbac7
JB
100/* Obtain SIZE bytes of space. If enough space is not presently available
101 in our process reserve, (i.e., (page_break_value - break_value)),
98b7fe02 102 this means getting more page-aligned space from the system.
dcfdbac7 103
98b7fe02
JB
104 Return non-zero if all went well, or zero if we couldn't allocate
105 the memory. */
106static int
dcfdbac7
JB
107obtain (size)
108 SIZE size;
109{
110 SIZE already_available = page_break_value - break_value;
111
112 if (already_available < size)
113 {
956ace37 114 SIZE get = ROUNDUP (size - already_available);
dcfdbac7 115
bbc60227 116 if ((*real_morecore) (get) == 0)
98b7fe02 117 return 0;
dcfdbac7
JB
118
119 page_break_value += get;
120 }
121
122 break_value += size;
98b7fe02
JB
123
124 return 1;
dcfdbac7
JB
125}
126
98b7fe02
JB
127/* Obtain SIZE bytes of space and return a pointer to the new area.
128 If we could not allocate the space, return zero. */
dcfdbac7
JB
129
130static POINTER
131get_more_space (size)
132 SIZE size;
133{
134 POINTER ptr = break_value;
98b7fe02
JB
135 if (obtain (size))
136 return ptr;
137 else
138 return 0;
dcfdbac7
JB
139}
140
141/* Note that SIZE bytes of space have been relinquished by the process.
956ace37 142 If SIZE is more than a page, return the space to the system. */
dcfdbac7
JB
143
144static void
145relinquish (size)
146 SIZE size;
147{
956ace37 148 POINTER new_page_break;
dcfdbac7 149
956ace37
JB
150 break_value -= size;
151 new_page_break = (POINTER) ROUNDUP (break_value);
152
153 if (new_page_break != page_break_value)
dcfdbac7 154 {
bbc60227
RM
155 if ((*real_morecore) ((char *) new_page_break
156 - (char *) page_break_value) == 0)
dcfdbac7
JB
157 abort ();
158
956ace37 159 page_break_value = new_page_break;
dcfdbac7
JB
160 }
161
956ace37
JB
162 /* Zero the space from the end of the "official" break to the actual
163 break, so that bugs show up faster. */
164 bzero (break_value, ((char *) page_break_value - (char *) break_value));
dcfdbac7
JB
165}
166\f
956ace37
JB
167/* The meat - allocating, freeing, and relocating blocs. */
168
169/* These structures are allocated in the malloc arena.
170 The linked list is kept in order of increasing '.data' members.
171 The data blocks abut each other; if b->next is non-nil, then
172 b->data + b->size == b->next->data. */
dcfdbac7
JB
173typedef struct bp
174{
175 struct bp *next;
176 struct bp *prev;
177 POINTER *variable;
178 POINTER data;
179 SIZE size;
180} *bloc_ptr;
181
182#define NIL_BLOC ((bloc_ptr) 0)
183#define BLOC_PTR_SIZE (sizeof (struct bp))
184
185/* Head and tail of the list of relocatable blocs. */
186static bloc_ptr first_bloc, last_bloc;
187
956ace37 188/* Find the bloc referenced by the address in PTR. Returns a pointer
dcfdbac7
JB
189 to that block. */
190
191static bloc_ptr
192find_bloc (ptr)
193 POINTER *ptr;
194{
195 register bloc_ptr p = first_bloc;
196
197 while (p != NIL_BLOC)
198 {
199 if (p->variable == ptr && p->data == *ptr)
200 return p;
201
202 p = p->next;
203 }
204
205 return p;
206}
207
208/* Allocate a bloc of SIZE bytes and append it to the chain of blocs.
98b7fe02
JB
209 Returns a pointer to the new bloc, or zero if we couldn't allocate
210 memory for the new block. */
dcfdbac7
JB
211
212static bloc_ptr
213get_bloc (size)
214 SIZE size;
215{
98b7fe02
JB
216 register bloc_ptr new_bloc;
217
218 if (! (new_bloc = (bloc_ptr) malloc (BLOC_PTR_SIZE))
219 || ! (new_bloc->data = get_more_space (size)))
220 {
221 if (new_bloc)
222 free (new_bloc);
223
224 return 0;
225 }
dcfdbac7 226
dcfdbac7
JB
227 new_bloc->size = size;
228 new_bloc->next = NIL_BLOC;
8c7f1e35 229 new_bloc->variable = (POINTER *) NIL;
dcfdbac7
JB
230
231 if (first_bloc)
232 {
233 new_bloc->prev = last_bloc;
234 last_bloc->next = new_bloc;
235 last_bloc = new_bloc;
236 }
237 else
238 {
239 first_bloc = last_bloc = new_bloc;
240 new_bloc->prev = NIL_BLOC;
241 }
242
243 return new_bloc;
244}
245
246/* Relocate all blocs from BLOC on upward in the list to the zone
247 indicated by ADDRESS. Direction of relocation is determined by
248 the position of ADDRESS relative to BLOC->data.
249
250 Note that ordering of blocs is not affected by this function. */
251
252static void
253relocate_some_blocs (bloc, address)
254 bloc_ptr bloc;
255 POINTER address;
256{
257 register bloc_ptr b;
258 POINTER data_zone = bloc->data;
259 register SIZE data_zone_size = 0;
260 register SIZE offset = bloc->data - address;
261 POINTER new_data_zone = data_zone - offset;
262
263 for (b = bloc; b != NIL_BLOC; b = b->next)
264 {
265 data_zone_size += b->size;
266 b->data -= offset;
267 *b->variable = b->data;
268 }
269
270 safe_bcopy (data_zone, new_data_zone, data_zone_size);
271}
272
273/* Free BLOC from the chain of blocs, relocating any blocs above it
274 and returning BLOC->size bytes to the free area. */
275
276static void
277free_bloc (bloc)
278 bloc_ptr bloc;
279{
280 if (bloc == first_bloc && bloc == last_bloc)
281 {
282 first_bloc = last_bloc = NIL_BLOC;
283 }
284 else if (bloc == last_bloc)
285 {
286 last_bloc = bloc->prev;
287 last_bloc->next = NIL_BLOC;
288 }
289 else if (bloc == first_bloc)
290 {
291 first_bloc = bloc->next;
292 first_bloc->prev = NIL_BLOC;
293 relocate_some_blocs (bloc->next, bloc->data);
294 }
295 else
296 {
297 bloc->next->prev = bloc->prev;
298 bloc->prev->next = bloc->next;
299 relocate_some_blocs (bloc->next, bloc->data);
300 }
301
302 relinquish (bloc->size);
303 free (bloc);
304}
305\f
956ace37
JB
306/* Interface routines. */
307
dcfdbac7
JB
308static int use_relocatable_buffers;
309
98b7fe02 310/* Obtain SIZE bytes of storage from the free pool, or the system, as
2c46d29f 311 necessary. If relocatable blocs are in use, this means relocating
98b7fe02
JB
312 them. This function gets plugged into the GNU malloc's __morecore
313 hook.
314
315 If we're out of memory, we should return zero, to imitate the other
316 __morecore hook values - in particular, __default_morecore in the
317 GNU malloc package. */
dcfdbac7
JB
318
319POINTER
320r_alloc_sbrk (size)
321 long size;
322{
323 POINTER ptr;
324
325 if (! use_relocatable_buffers)
bbc60227 326 return (*real_morecore) (size);
dcfdbac7
JB
327
328 if (size > 0)
329 {
98b7fe02
JB
330 if (! obtain (size))
331 return 0;
332
dcfdbac7
JB
333 if (first_bloc)
334 {
335 relocate_some_blocs (first_bloc, first_bloc->data + size);
956ace37
JB
336
337 /* Zero out the space we just allocated, to help catch bugs
338 quickly. */
dcfdbac7
JB
339 bzero (virtual_break_value, size);
340 }
341 }
342 else if (size < 0)
343 {
344 if (first_bloc)
345 relocate_some_blocs (first_bloc, first_bloc->data + size);
346 relinquish (- size);
347 }
348
349 ptr = virtual_break_value;
350 virtual_break_value += size;
351 return ptr;
352}
353
354/* Allocate a relocatable bloc of storage of size SIZE. A pointer to
355 the data is returned in *PTR. PTR is thus the address of some variable
98b7fe02
JB
356 which will use the data area.
357
358 If we can't allocate the necessary memory, set *PTR to zero, and
359 return zero. */
dcfdbac7
JB
360
361POINTER
362r_alloc (ptr, size)
363 POINTER *ptr;
364 SIZE size;
365{
366 register bloc_ptr new_bloc;
367
2c46d29f
RS
368 if (! r_alloc_initialized)
369 r_alloc_init ();
370
dcfdbac7 371 new_bloc = get_bloc (size);
98b7fe02
JB
372 if (new_bloc)
373 {
374 new_bloc->variable = ptr;
375 *ptr = new_bloc->data;
376 }
377 else
378 *ptr = 0;
dcfdbac7
JB
379
380 return *ptr;
381}
382
2c46d29f
RS
383/* Free a bloc of relocatable storage whose data is pointed to by PTR.
384 Store 0 in *PTR to show there's no block allocated. */
dcfdbac7
JB
385
386void
387r_alloc_free (ptr)
388 register POINTER *ptr;
389{
390 register bloc_ptr dead_bloc;
391
dcfdbac7
JB
392 dead_bloc = find_bloc (ptr);
393 if (dead_bloc == NIL_BLOC)
394 abort ();
395
396 free_bloc (dead_bloc);
2c46d29f 397 *ptr = 0;
dcfdbac7
JB
398}
399
16a5c729 400/* Given a pointer at address PTR to relocatable data, resize it to SIZE.
98b7fe02
JB
401 Do this by shifting all blocks above this one up in memory, unless
402 SIZE is less than or equal to the current bloc size, in which case
403 do nothing.
dcfdbac7 404
98b7fe02
JB
405 Change *PTR to reflect the new bloc, and return this value.
406
407 If more memory cannot be allocated, then leave *PTR unchanged, and
408 return zero. */
dcfdbac7
JB
409
410POINTER
411r_re_alloc (ptr, size)
412 POINTER *ptr;
413 SIZE size;
414{
16a5c729 415 register bloc_ptr bloc;
dcfdbac7 416
16a5c729
JB
417 bloc = find_bloc (ptr);
418 if (bloc == NIL_BLOC)
dcfdbac7
JB
419 abort ();
420
16a5c729 421 if (size <= bloc->size)
956ace37 422 /* Wouldn't it be useful to actually resize the bloc here? */
dcfdbac7
JB
423 return *ptr;
424
98b7fe02
JB
425 if (! obtain (size - bloc->size))
426 return 0;
427
16a5c729 428 relocate_some_blocs (bloc->next, bloc->data + size);
dcfdbac7 429
16a5c729
JB
430 /* Zero out the new space in the bloc, to help catch bugs faster. */
431 bzero (bloc->data + bloc->size, size - bloc->size);
0abf54e3 432
16a5c729
JB
433 /* Indicate that this block has a new size. */
434 bloc->size = size;
dcfdbac7
JB
435
436 return *ptr;
437}
438\f
439/* The hook `malloc' uses for the function which gets more space
440 from the system. */
441extern POINTER (*__morecore) ();
442
443/* Intialize various things for memory allocation. */
444
2c46d29f
RS
445static void
446r_alloc_init ()
dcfdbac7 447{
2c46d29f 448 if (r_alloc_initialized)
dcfdbac7
JB
449 return;
450
2c46d29f 451 r_alloc_initialized = 1;
bbc60227 452 real_morecore = __morecore;
dcfdbac7 453 __morecore = r_alloc_sbrk;
8c7f1e35 454
bbc60227 455 virtual_break_value = break_value = (*real_morecore) (0);
aef4d570 456 if (break_value == NIL)
2c46d29f 457 abort ();
8c7f1e35 458
dcfdbac7 459 page_break_value = (POINTER) ROUNDUP (break_value);
2c46d29f
RS
460 /* Clear the rest of the last page; this memory is in our address space
461 even though it is after the sbrk value. */
dcfdbac7
JB
462 bzero (break_value, (page_break_value - break_value));
463 use_relocatable_buffers = 1;
2c46d29f 464}