summaryrefslogblamecommitdiffstats
path: root/private/crt32/heap/realloc.c
blob: 53d5a23b5b564a913134516e764dd571a84a79e6 (plain) (tree)
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
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477




























































































































































































































































































































































































































































































                                                                                         
/***
*realloc.c - Reallocate a block of memory in the heap
*
*	Copyright (c) 1989-1991, Microsoft Corporation. All rights reserved.
*
*Purpose:
*	Defines the realloc() and _expand() functions.
*
*Revision History:
*	10-25-89   GJF	Module created.
*	11-06-89   GJF	Massively revised to handle 'tiling' and to properly
*			update proverdesc.
*	11-10-89   GJF	Added MTHREAD support.
*	11-17-89   GJF	Fixed pblck validation (i.e., conditional call to
*			_heap_abort())
*	12-18-89   GJF	Changed header file name to heap.h, also added explicit
*			_cdecl or _pascal to function defintions
*	12-20-89   GJF	Removed references to plastdesc
*	01-04-90   GJF	Fixed a couple of subtle and nasty bugs in _expand().
*	03-11-90   GJF	Replaced _cdecl with _CALLTYPE1, added #include
*			<cruntime.h> and removed #include <register.h>.
*	03-29-90   GJF	Made _heap_expand_block() _CALLTYPE4.
*	07-25-90   SBM	Replaced <stdio.h> by <stddef.h>, replaced
*			<assertm.h> by <assert.h>
*	09-28-90   GJF	New-style function declarators.
*	12-28-90   SRW	Added cast of void * to char * for Mips C Compiler
*	03-05-91   GJF	Changed strategy for rover - old version available
*			by #define-ing _OLDROVER_.
*	04-08-91   GJF	Temporary hack for Win32/DOS folks - special version
*			of realloc that uses just malloc, _msize, memcpy and
*			free. Change conditioned on _WIN32DOS_.
*	05-28-91   GJF	Removed M_I386 conditionals and put in _WIN32_
*			conditionals to build non-tiling heap for Win32.
*
*******************************************************************************/

#ifndef _WIN32DOS_

#include <cruntime.h>
#include <assert.h>
#include <heap.h>
#include <malloc.h>
#include <os2dll.h>
#include <stddef.h>
#include <string.h>

/* useful macro to compute the size of an allocation block given both a
 * pointer to the descriptor and a pointer to the user area of the block
 * (more efficient variant of _BLKSIZE macro, given the extra information)
 */
#define BLKSZ(pdesc,pblk)	((unsigned)_ADDRESS((pdesc)->pnextdesc) - \
				(unsigned)(pblck))

/* expand an allocation block, in place, up to or beyond a specified size
 * by coalescing it with subsequent free blocks (if possible)
 */
static int _CALLTYPE4 _heap_expand_block(_PBLKDESC, size_t *, size_t);

/***
*void *realloc(void *pblck, size_t newsize) - reallocate a block of memory in
*	the heap
*
*Purpose:
*	Re-allocates a block in the heap to newsize bytes. newsize may be
*	either greater or less than the original size of the block. The
*	re-allocation may result in moving the block as well as changing
*	the size. If the block is moved, the contents of the original block
*	are copied over.
*
*	Special ANSI Requirements:
*
*	(1) realloc(NULL, newsize) is equivalent to malloc(newsize)
*
*	(2) realloc(pblck, 0) is equivalent to free(pblck) (except that
*	    NULL is returned)
*
*	(3) if the realloc() fails, the object pointed to by pblck is left
*	    unchanged
*
*	Special Notes For Cruiser Implementaton: For OS/2 2.0, realloc() is
*	required to ensure that the re-allocated block does not cross a 64 Kb
*	boundary unless the new size is more than 64 Kb or the original block
*	already crossed such a boundary.
*
*	Special Notes For Multi-thread: The heap is locked immediately prior
*	to assigning pdesc. This is after special cases (1) and (2), listed
*	above, are taken care of. The lock is released immediately prior to
*	the final return statement.
*
*Entry:
*	void *pblck	- pointer to block in the heap previously allocated
*			  by a call to malloc(), realloc() or _expand().
*
*	size_t newsize	- requested size for the re-allocated block
*
*Exit:
*	Success:  Pointer to the re-allocated memory block
*	Failure:  NULL
*
*Uses:
*
*Exceptions:
*	If pblck does not point to a valid allocation block in the heap,
*	realloc() will behave unpredictably and probably corrupt the heap.
*
*******************************************************************************/

void * _CALLTYPE1 realloc (
	REG1 void *pblck,
	size_t newsize
	)
{
	REG2 _PBLKDESC pdesc;
	void *pretblck;
	size_t oldsize;
	size_t currsize;

	/* special cases, handling mandated by ANSI
	 */
	if ( pblck == NULL )
		/* just do a malloc of newsize bytes and return a pointer to
		 * the new block
		  */
		return( malloc(newsize) );

	if ( newsize == 0 ) {
		/* free the block and return NULL
		 */
		free(pblck);
		return( NULL );
	}

	/* make newsize a valid allocation block size (i.e., round up to the
	 * nearest whole number of dwords)
	 */
	newsize = _ROUND2(newsize,4);

	/* if multi-thread support enabled, lock the heap here
	 */
	_mlock(_HEAP_LOCK);

	/* set pdesc to point to the descriptor for *pblck
	 */
	pdesc = _BACKPTR(pblck);

	if ( _ADDRESS(pdesc) != ((char *)pblck - _HDRSIZE) )
		_heap_abort();

	/* see if pblck is big enough already, or can be expanded (in place)
	 * to be big enough.
	 */
#ifdef	_CRUISER_

	/* if the block was expanded in place, ensure that it does not cross
	 * a 64 Kb boundary unless it already did so or newsize is greater
	 * than 64 Kb
	 */
	if ( ((oldsize = currsize = BLKSZ(pdesc, pblck)) >= newsize) ||
	((_heap_expand_block(pdesc, &currsize, newsize) == 0) &&
	((newsize > _SEGSIZE_) || (_DISTTOBNDRY(pdesc) >= newsize) ||
	(_DISTTOBNDRY(pdesc) < oldsize))) ) {

#else	/* ndef _CRUISER_ */

#ifdef	_WIN32_

	if ( ((oldsize = currsize = BLKSZ(pdesc, pblck)) > newsize) ||
	(_heap_expand_block(pdesc, &currsize, newsize) == 0) ) {

#else	/* ndef _WIN32_ */

#error ERROR - ONLY CRUISER OR WIN32 TARGET SUPPORTED!

#endif	/* _WIN32_ */

#endif	/* _CRUISER_ */

		/* if necessary, mark pdesc as inuse
		 */
		if ( _IS_FREE(pdesc) ) {
			_SET_INUSE(pdesc);
#ifdef	_OLDROVER_
			_heap_advance_rover();
#endif	/* _OLDROVER_ */
		}

		/* trim pdesc down to be exactly newsize bytes, if necessary
		 */
		if ( currsize > newsize ) {
			_heap_split_block(pdesc, newsize);
			_free_lk((char *)pblck + newsize + _HDRSIZE);
		}

		pretblck = pblck;
		goto realloc_done;
	}

	/* try malloc-ing a new block of the requested size. if successful,
	 * copy over the data from the original block and free it.
	 */
	if ( (pretblck = _malloc_lk(newsize)) != NULL ) {
		memcpy(pretblck, pblck, oldsize);
		_free_lk(pblck);
	}
	else {
		/* restore pblck to its orignal size
		 */
		_heap_split_block(pdesc, oldsize);
		_free_lk((char *)pblck + oldsize + _HDRSIZE);
	}

realloc_done:
	/* if multi-thread support is enabled, unlock the heap here
	 */
	_munlock(_HEAP_LOCK);

	return(pretblck);
}


/***
*void *_expand(void *pblck, size_t newsize) - expand/contract a block of memory
*	in the heap
*
*Purpose:
*	Resizes a block in the heap to newsize bytes. newsize may be either
*	greater (expansion) or less (contraction) than the original size of
*	the block. The block is NOT moved. In the case of expansion, if the
*	block cannot be expanded to newsize bytes, it is expanded as much as
*	possible.
*
*	Special Notes For Cruiser Implementaton: For OS/2 2.0, _expand() is
*	required to ensure the resized block does not cross a 64 Kb boundary
*	unless the requested new size is more than 64 Kb or the block already
*	did so. Note that, because of the peculiar semantics of _expand(), it
*	may produce block of less than 64 Kb in size which does cross a 64 Kb
*	boundary.
*
*	Special Notes For Multi-thread: The heap is locked just before pdesc
*	is assigned and unlocked immediately prior to the return statement.
*
*Entry:
*	void *pblck	- pointer to block in the heap previously allocated
*			  by a call to malloc(), realloc() or _expand().
*
*	size_t newsize	- requested size for the resized block
*
*Exit:
*	Success:  Pointer to the resized memory block (i.e., pblck)
*	Failure:  NULL
*
*Uses:
*
*Exceptions:
*	If pblck does not point to a valid allocation block in the heap,
*	_expand() will behave unpredictably and probably corrupt the heap.
*
*******************************************************************************/

void * _CALLTYPE1 _expand (
	REG1 void *pblck,
	size_t newsize
	)
{
	REG2 _PBLKDESC pdesc;
	void *pretblck = pblck;
	size_t oldsize;
	size_t currsize;
	unsigned index;
#ifdef	_CRUISER_
	size_t dist;
#endif	/* _CRUISER_ */

	/* make newsize a valid allocation block size (i.e., round up to the
	 * nearest whole number of dwords)
	 */
	newsize = _ROUND2(newsize,4);

	/* if multi-thread support enabled, lock the heap here
	 */
	_mlock(_HEAP_LOCK);

	/* set pdesc to point to the descriptor for *pblck
	 */
	pdesc = _BACKPTR(pblck);

	/* see if pblck is big enough already, or can be expanded (in place)
	 * to be big enough.
	 */
	if ( ((oldsize = currsize = BLKSZ(pdesc, pblck)) >= newsize) ||
	(_heap_expand_block(pdesc, &currsize, newsize) == 0) ) {
		/* pblck is (now) big enough. trim it down, if necessary
		 */
		if ( currsize > newsize ) {
			_heap_split_block(pdesc, newsize);
			_free_lk((char *)pblck + newsize + _HDRSIZE);
			currsize = newsize;
		}
		goto expand_done;
	}

	/* if the heap block is at the end of a region, attempt to grow the
	 * region
	 */
	if ( (pdesc->pnextdesc == &_heap_desc.sentinel) ||
       _IS_DUMMY(pdesc->pnextdesc) ) {

		/* look up the region index
		 */
		for ( index = 0 ; index < _HEAP_REGIONMAX ; index++ )
			if ( (_heap_regions[index]._regbase < pblck) &&
			(((char *)(_heap_regions[index]._regbase) +
			_heap_regions[index]._currsize) >= (char *)pblck) )
				break;

		/* make sure a valid region index was obtained (pblck could
		 * lie in a portion of heap memory donated by a user call to
		 * _heapadd(), which therefore would not appear in the region
		 * table)
		 */
		 if ( index == _HEAP_REGIONMAX ) {
			pretblck = NULL;
			goto expand_done;
		}

		/* try growing the region. the difference between newsize and
		 * the current size of the block, rounded up to the nearest
		 * whole number of pages, is the amount the region needs to
		 * be grown. if successful, try expanding the block again
		 */
		if ( (_heap_grow_region(index, _ROUND2(newsize - currsize,
		_PAGESIZE_)) == 0) &&
		(_heap_expand_block(pdesc, &currsize, newsize) == 0) ) {
			/* pblck is (now) big enough. trim it down to be
			 * exactly size bytes, if necessary
			 */
			if ( currsize > newsize ) {
				_heap_split_block(pdesc, newsize);
				_free_lk((char *)pblck + newsize + _HDRSIZE);
				currsize = newsize;
			}
		}
		else
			pretblck = NULL;
	}
	else
		pretblck = NULL;

expand_done:
#ifdef	_CRUISER_
	/* check for crossing of 64 Kb boundaries. the resized allocation
	 * block must be trimmed down if the following conditions all hold
	 * true:
	 *		(1) the block was grown
	 *		(2) the requested new size was less than 64 Kb
	 *		(3) the block now crosses a 64 Kb boundary
	 *		(4) the block did not originally cross a 64 Kb
	 *		    boundary
	 */
	if ( (currsize > oldsize) && (newsize <= _SEGSIZE_) &&
	(currsize > (dist = _DISTTOBNDRY(pblck))) && (oldsize <= dist) ) {
		_heap_split_block(pdesc, dist);
		_free_lk((char *)pblck + dist + _HDRSIZE);
		pretblck = NULL;
	}
#endif	/* _CRUISER_ */

	/* if multi-thread support is enabled, unlock the heap here
	 */
	_munlock(_HEAP_LOCK);

	return(pretblck);
}


/***
*int _heap_expand_block(pdesc, pcurrsize, newsize) - expand an allocation block
*	in place (without trying to 'grow' the heap)
*
*Purpose:
*
*Entry:
*	_PBLKDESC pdesc   - pointer to the allocation block descriptor
*	size_t *pcurrsize - pointer to size of the allocation block (i.e.,
*			    *pcurrsize == _BLKSIZE(pdesc), on entry)
*	size_t newsize	  - requested minimum size for the expanded allocation
*			    block (i.e., newsize >= _BLKSIZE(pdesc), on exit)
*
*Exit:
*	Success:  0
*	Failure: -1
*	In either case, *pcurrsize is updated with the new size of the block
*
*Exceptions:
*	It is assumed that pdesc points to a valid allocation block descriptor.
*	It is also assumed that _BLKSIZE(pdesc) == *pcurrsize on entry. If
*	either of these assumptions is violated, _heap_expand_block will almost
*	certainly trash the heap.
*
*******************************************************************************/

static int _CALLTYPE4 _heap_expand_block (
	REG1 _PBLKDESC pdesc,
	REG3 size_t *pcurrsize,
	size_t newsize
	)
{
	REG2 _PBLKDESC pdesc2;

	assert(("_heap_expand_block: bad pdesc arg", _CHECK_PDESC(pdesc)));
	assert(("_heap_expand_block: bad pcurrsize arg", *pcurrsize == _BLKSIZE(pdesc)));

	for ( pdesc2 = pdesc->pnextdesc ; _IS_FREE(pdesc2) ;
	pdesc2 = pdesc->pnextdesc ) {

		/* coalesce with pdesc. check for special case of pdesc2
		 * being proverdesc.
		 */
		pdesc->pnextdesc = pdesc2->pnextdesc;

		if ( pdesc2 == _heap_desc.proverdesc )
#ifdef	_OLDROVER_
			/* temporarily set proverdesc to pdesc
			 */
#endif	/* _OLDROVER_ */
			_heap_desc.proverdesc = pdesc;

		/* update *pcurrsize, place *pdesc2 on the empty descriptor
		 * list and see if the coalesced block is now big enough
		 */
		*pcurrsize += _MEMSIZE(pdesc2);

		_PUTEMPTY(pdesc2)
	}

#ifdef	_OLDROVER_
	if ( pdesc == _heap_desc.proverdesc )
		_heap_advance_rover();
#endif	/* _OLDROVER_ */

	if ( *pcurrsize >= newsize )
		return(0);
	else
		return(-1);
}

#else	/* _WIN32DOS_ */

/*
 * TEMPORARY HACK! THE CODE BELOW IS INTENDED TO ALLOW LIMITED USE OF THE
 * C RUNTIME ON WIN32/DOS. IT WILL BE DELETED AS SOON AS THEY IMPLEMENT
 * VirtualAlloc()!
 */

#include <cruntime.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>

void * _CALLTYPE1 realloc (
	REG1 void *pblck,
	size_t newsize
	)
{
	void *pnew;
	size_t oldsize;

	if ( (pnew = malloc(newsize)) != NULL ) {
		oldsize = _msize(pblck);
		memcpy(pnew, pblck, min(newsize, oldsize));
		free(pblck);
	}

	return pnew;
}

#endif	/* _WIN32DOS_ */