summaryrefslogtreecommitdiffstats
path: root/private/crt32/heap/realloc.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/crt32/heap/realloc.c477
1 files changed, 477 insertions, 0 deletions
diff --git a/private/crt32/heap/realloc.c b/private/crt32/heap/realloc.c
new file mode 100644
index 000000000..53d5a23b5
--- /dev/null
+++ b/private/crt32/heap/realloc.c
@@ -0,0 +1,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_ */