kolibrios/programs/develop/open watcom/trunk/clib/heap/grownear.c
Sergey Semyonov (Serge) 836c97f0ac Clib string & memory functions
git-svn-id: svn://kolibrios.org@553 a494cfbc-eb01-0410-851d-a64ba20cac60
2007-06-26 00:54:22 +00:00

561 lines
18 KiB
C

/****************************************************************************
*
* Open Watcom Project
*
* Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
* ========================================================================
*
* This file contains Original Code and/or Modifications of Original
* Code as defined in and that are subject to the Sybase Open Watcom
* Public License version 1.0 (the 'License'). You may not use this file
* except in compliance with the License. BY USING THIS FILE YOU AGREE TO
* ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
* provided with the Original Code and Modifications, and is also
* available at www.sybase.com/developer/opensource.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
* ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
* NON-INFRINGEMENT. Please see the License for the specific language
* governing rights and limitations under the License.
*
* ========================================================================
*
* Description: Heap growing routines - allocate near heap memory from OS.
*
****************************************************************************/
//#include "dll.h" // needs to be first
#include "variety.h"
#include <stddef.h>
#include <stdlib.h>
#include <malloc.h>
#include "heapacc.h"
#include "heap.h"
#include <errno.h>
#if defined(__DOS_EXT__)
// #include "extender.h"
// #include "tinyio.h"
#endif
#if defined(__CALL21__)
// #include "tinyio.h"
#endif
#if defined(__WINDOWS_286__) || defined(__NT__)
void* _stdcall UserAlloc(int size);
// #include "windows.h"
#endif
#if defined(__OS2__)
// #include <wos2.h>
#endif
#if defined(__WINDOWS_386__)
// extern void * pascal DPMIAlloc(unsigned long);
#endif
static frlptr __LinkUpNewMHeap( mheapptr );
#if defined(__DOS_EXT__)
extern int SegmentLimit();
#pragma aux SegmentLimit = \
"xor eax,eax" \
"mov ax,ds" \
"lsl eax,ax" \
"inc eax" \
value [eax] \
modify exact [eax];
static void __unlink( mheapptr miniheapptr )
{
mheapptr prev_link;
mheapptr next_link;
if( __nheapbeg == miniheapptr ) {
__nheapbeg = miniheapptr->next;
}
if( miniheapptr == __MiniHeapRover ) {
__MiniHeapRover = miniheapptr->prev;
if( __MiniHeapRover == NULL ) {
__MiniHeapRover = __nheapbeg;
__LargestSizeB4MiniHeapRover = 0;
}
}
if( miniheapptr == __MiniHeapFreeRover ) {
__MiniHeapFreeRover = 0;
}
prev_link = miniheapptr->prev;
next_link = miniheapptr->next;
if( prev_link != NULL ) prev_link->next = next_link;
if( next_link != NULL ) next_link->prev = prev_link;
}
void __FreeDPMIBlocks()
{
mheapptr mhp;
struct dpmi_hdr *dpmi;
mhp = __nheapbeg;
while( mhp != NULL ) {
// see if the last free entry has the full size of
// the DPMI block ( - overhead). If it is then we can give this
// DPMI block back to the DPMI host.
if( (mhp->freehead.prev)->len + sizeof(struct miniheapblkp) ==
mhp->len ) {
mheapptr pnext;
dpmi = ((struct dpmi_hdr *)mhp) - 1;
pnext = mhp->next;
__unlink( mhp );
mhp = pnext;
if( dpmi->dos_seg_value == 0 ) { // if DPMI block
TinyDPMIFree( dpmi->dpmi_handle );
} else { // else DOS block below 1MB
TinyFreeBlock( dpmi->dos_seg_value );
}
} else {
mhp = mhp->next;
}
}
}
void *__ReAllocDPMIBlock( frlptr p1, unsigned req_size )
{
mheapptr mhp;
struct dpmi_hdr *dpmi;
struct dpmi_hdr *prev_dpmi;
unsigned size;
frlptr flp, flp2;
if( !__heap_enabled ) return( 0 );
__FreeDPMIBlocks();
prev_dpmi = NULL;
for( mhp = __nheapbeg; mhp; mhp = mhp->next ) {
if( ((PTR)mhp + sizeof(struct miniheapblkp) == (PTR)p1)
&& (mhp->numalloc == 1) ) {
// The mini-heap contains only this memblk
__unlink( mhp );
dpmi = ((struct dpmi_hdr *)mhp) - 1;
if( dpmi->dos_seg_value != 0 ) return( NULL );
size = mhp->len + sizeof(struct dpmi_hdr) + TAG_SIZE;
size += ( req_size - (p1->len-TAG_SIZE) );
size += 0x0fff;
size &= ~0x0fff;
prev_dpmi = dpmi;
dpmi = TinyDPMIRealloc( dpmi, size );
if( dpmi == NULL ) {
dpmi = prev_dpmi;
return( NULL ); // indicate resize failed
}
dpmi->dos_seg_value = 0;
mhp = (mheapptr)( dpmi + 1 );
mhp->len = size - sizeof(struct dpmi_hdr) - TAG_SIZE;
flp = __LinkUpNewMHeap( mhp );
mhp->numalloc = 1;
// round up to even number
req_size = (req_size + 1) & ~1;
size = flp->len - req_size;
if( size >= FRL_SIZE ) { // Enough to spare a free block
flp->len = req_size | 1;// adjust size and set allocated bit
// Make up a free block at the end
flp2 = (frlptr)((PTR)flp + req_size);
flp2->len = size | 1;
++mhp->numalloc;
mhp->largest_blk = 0;
_nfree( (PTR)flp2 + TAG_SIZE );
} else {
flp->len |= 1; // set allocated bit
}
return( flp );
}
}
return( NULL );
}
#endif
static frlptr __LinkUpNewMHeap( mheapptr p1 ) // originally __AddNewHeap()
{
mheapptr p2;
mheapptr p2_prev;
tag *last_tag;
unsigned amount;
/* insert into ordered heap list (14-jun-91 AFS) */
/* logic wasn't inserting heaps in proper ascending order */
/* (09-nov-93 Fred) */
p2_prev = NULL;
for( p2 = __nheapbeg; p2 != NULL; p2 = p2->next ) {
if( p1 < p2 ) break;
p2_prev = p2;
}
/* ascending order should be: p2_prev < p1 < p2 */
/* except for special cases when p2_prev and/or p2 are NULL */
p1->prev = p2_prev;
p1->next = p2;
if( p2_prev != NULL ) {
p2_prev->next = p1;
} else { /* add p1 to beginning of heap */
__nheapbeg = p1;
}
if( p2 != NULL ) {
/* insert before 'p2' (list is non-empty) */
p2->prev = p1;
}
amount = p1->len - sizeof( struct miniheapblkp );
/* Fill out the new miniheap descriptor */
p1->freehead.len = 0;
p1->freehead.prev = &p1->freehead;
p1->freehead.next = &p1->freehead;
p1->rover = &p1->freehead;
p1->b4rover = 0;
p1->numalloc = 0;
p1->numfree = 0;
p1++;
((frlptr)p1)->len = amount;
/* fix up end of heap links */
last_tag = (tag *) ( (PTR)p1 + amount );
*last_tag = END_TAG;
return( (frlptr) p1 );
}
#if ! ( defined(__WINDOWS_286__) || \
defined(__WINDOWS_386__) || \
defined(__WARP__) || \
defined(__NT__) \
)
size_t __LastFree( void ) /* used by nheapgrow to know about adjustment */
{
frlptr p1;
unsigned brk_value;
if( __nheapbeg == NULL ) { /* no heap? can't have free blocks */
return( 0 );
}
p1 = __nheapbeg->freehead.prev; /* point to last free block */
brk_value = (unsigned)((PTR)p1 + p1->len + TAG_SIZE );
#if defined(__DOS_EXT__)
if( _IsPharLap() && !__X32VM) _curbrk = SegmentLimit(); /*19-feb-94*/
#endif
if( brk_value == _curbrk ) { /* if last free block is at the end */
return( p1->len );
}
return( 0 );
}
#endif
#if ! defined(__CALL21__)
#if defined(__DOS_EXT__)
static void *RationalAlloc( size_t size )
{
struct dpmi_hdr *dpmi;
mheapptr mhp;
tiny_ret_t save_DOS_block;
tiny_ret_t DOS_block;
__FreeDPMIBlocks();
/* size is a multiple of 4k */
dpmi = TinyDPMIAlloc( size );
if( dpmi != NULL ) {
mhp = (mheapptr)( dpmi + 1 );
mhp->len = size - sizeof( struct dpmi_hdr );
dpmi->dos_seg_value = 0; // indicate DPMI block
return( (void *)mhp );
}
if( __minreal & 0xfff00000 ) {
/* checks for users that want >1M real memory saved */
__minreal = 0xfffff;
}
if( size > 0x00010000 ) {
/* cannot allocate more than 64k from DOS real memory */
return( NULL );
}
save_DOS_block = TinyAllocBlock(( __minreal >> 4 ) | 1 );
if( TINY_OK( save_DOS_block ) ) {
DOS_block = TinyAllocBlock( size >> 4 );
TinyFreeBlock( save_DOS_block );
if( TINY_OK( DOS_block ) ) {
dpmi = (struct dpmi_hdr *) TinyDPMIBase( DOS_block );
dpmi->dos_seg_value = DOS_block;
mhp = (mheapptr)( dpmi + 1 );
mhp->len = size - sizeof( struct dpmi_hdr );
return( (void *)mhp );
}
}
return( NULL );
}
#endif
#endif
static int __AdjustAmount( unsigned *amount )
{
unsigned old_amount = *amount;
unsigned amt;
#if ! ( defined(__WINDOWS_286__) || \
defined(__WINDOWS_386__) || \
defined(__WARP__) || \
defined(__NT__) \
)
unsigned last_free_amt;
#endif
amt = old_amount;
amt = ( amt + TAG_SIZE + ROUND_SIZE) & ~ROUND_SIZE;
if( amt < old_amount ) {
return( 0 );
}
#if ! ( defined(__WINDOWS_286__) || \
defined(__WINDOWS_386__) || \
defined(__WARP__) || \
defined(__NT__) \
)
#if defined(__DOS_EXT__)
if( _IsRationalZeroBase() || _IsCodeBuilder() ) {
// Allocating extra to identify the dpmi block
amt += sizeof(struct dpmi_hdr);
} else {
#else
{
#endif
last_free_amt = __LastFree(); /* adjust for last free block */
if( last_free_amt >= amt ) {
amt = 0;
} else {
amt -= last_free_amt;
}
}
#endif
/* amount is even here */
/*
extra amounts (22-feb-91 AFS)
(1) adding a new heap needs:
frl free block req'd for _nmalloc request
(frl is the MINIMUM because the block
may be freed)
tag end of miniheap descriptor
struct miniheapblkp start of miniheap descriptor
(2) extending heap needs:
tag free block req'd for _nmalloc request
*/
*amount = amt;
amt += ( (TAG_SIZE) + sizeof(frl) + sizeof(struct miniheapblkp) );
if( amt < *amount ) return( 0 );
if( amt < _amblksiz ) {
/*
_amblksiz may not be even so round down to an even number
nb. pathological case: where _amblksiz == 0xffff, we don't
want the usual round up to even
*/
amt = _amblksiz & ~1u;
}
#if defined(__WINDOWS_386__) || \
defined(__WARP__) || \
defined(__NT__) || \
defined(__CALL21__) || \
defined(__DOS_EXT__)
/* make sure amount is a multiple of 4k */
*amount = amt;
amt += 0x0fff;
if( amt < *amount ) return( 0 );
amt &= ~0x0fff;
#endif
*amount = amt;
return( *amount != 0 );
}
#if defined(__WINDOWS_286__) || \
defined(__WINDOWS_386__) || \
defined(__WARP__) || \
defined(__NT__) || \
defined(__CALL21__) || \
defined(__DOS_EXT__)
static int __CreateNewNHeap( unsigned amount )
{
mheapptr p1;
frlptr flp;
unsigned brk_value;
if( !__heap_enabled ) return( 0 );
if( _curbrk == ~1u ) return( 0 );
if( __AdjustAmount( &amount ) == 0 ) return( 0 );
#if defined(__WINDOWS_286__)
brk_value = (unsigned) LocalAlloc( LMEM_FIXED, amount );
if( brk_value == 0 ) {
return( 0 );
}
#elif defined(__WINDOWS_386__)
brk_value = (unsigned) DPMIAlloc( amount );
if( brk_value == 0 ) {
return( 0 );
}
#elif defined(__WARP__)
{
PBYTE p;
if( DosAllocMem( &p, amount, PAG_COMMIT|PAG_READ|PAG_WRITE ) ) {
return( 0 );
}
brk_value = (unsigned)p;
}
#elif defined(__NT__)
// brk_value = (unsigned) VirtualAlloc( NULL, amount, MEM_COMMIT,
// PAGE_EXECUTE_READWRITE );
brk_value = (unsigned) UserAlloc (amount );
//brk_value = (unsigned) LocalAlloc( LMEM_FIXED, amount );
if( brk_value == 0 ) {
return( 0 );
}
#elif defined(__CALL21__)
{
tag _WCNEAR *tmp_tag;
tmp_tag = (tag _WCNEAR *)TinyMemAlloc( amount );
if( tmp_tag == NULL ) {
return( 0 );
}
/* make sure it will not look like the end of a heap */
tmp_tag[0] = ! END_TAG;
brk_value = (unsigned) &tmp_tag[2];
amount -= 2 * TAG_SIZE; // 11-jun-95, subtract extra tag
}
#elif defined(__DOS_EXT__)
// if( _IsRationalZeroBase() || _IsCodeBuilder() ) {
{
tag *tmp_tag;
if( _IsRational() ) {
tmp_tag = RationalAlloc( amount );
if( tmp_tag ) amount = *tmp_tag;
} else { /* CodeBuilder */
tmp_tag = TinyCBAlloc( amount );
amount -= TAG_SIZE;
}
if( tmp_tag == NULL ) {
return( 0 );
}
brk_value = (unsigned) tmp_tag;
}
// Pharlap, RSI/non-zero can never call this function
#endif
if( amount - TAG_SIZE > amount ) {
return( 0 );
} else {
amount -= TAG_SIZE;
}
if( amount < sizeof( struct miniheapblkp ) + sizeof( frl ) ) {
/* there isn't enough for a heap block (struct miniheapblkp) and
one free block (frl) */
return( 0 );
}
/* we've got a new heap block */
p1 = (mheapptr) brk_value;
p1->len = amount;
// Now link it up
flp = __LinkUpNewMHeap( p1 );
amount = flp->len;
/* build a block for _nfree() */
flp->len = amount | 1;
++p1->numalloc; /* 28-dec-90 */
p1->largest_blk = 0;
_nfree( (PTR)flp + TAG_SIZE );
return( 1 );
}
#endif
int __ExpandDGROUP( unsigned amount )
{
#if defined(__WINDOWS_286__) || \
defined(__WINDOWS_386__) || \
defined(__WARP__) || \
defined(__NT__) || \
defined(__CALL21__)
// first try to free any available storage
_nheapshrink();
return( __CreateNewNHeap( amount ) );
#else
mheapptr p1;
frlptr flp;
unsigned brk_value;
tag *last_tag;
unsigned new_brk_value;
void _WCNEAR *brk_ret;
#if defined(__DOS_EXT__)
if( ( _IsRationalZeroBase() || _IsCodeBuilder() ) ) {
return( __CreateNewNHeap( amount ) ); // Won't slice either
}
// Rational non-zero based system should go through.
#endif
if( !__heap_enabled ) return( 0 );
if( _curbrk == ~1u ) return( 0 );
if( __AdjustAmount( &amount ) == 0 ) return( 0 );
#if defined(__DOS_EXT__)
if( _IsPharLap() && !__X32VM ) { /* 19-feb-94 */
_curbrk = SegmentLimit();
}
#endif
new_brk_value = amount + _curbrk;
if( new_brk_value < _curbrk ) {
new_brk_value = ~1u;
}
brk_ret = __brk( new_brk_value );
if( brk_ret == (void _WCNEAR *) -1 ) {
return( 0 );
}
brk_value = (unsigned) brk_ret;
if( brk_value > /*0xfff8*/ ~7u ) {
return( 0 );
}
if( new_brk_value <= brk_value ) {
return( 0 );
}
amount = new_brk_value - brk_value;
if( amount - TAG_SIZE > amount ) {
return( 0 );
} else {
amount -= TAG_SIZE;
}
for( p1 = __nheapbeg; p1 != NULL; p1 = p1->next ) {
if( p1->next == NULL ) break;
if( (unsigned)p1 <= brk_value &&
((unsigned)p1)+p1->len+TAG_SIZE >= brk_value ) break;
}
if( (p1 != NULL) &&
((brk_value - TAG_SIZE) == (unsigned)( (PTR)p1 + p1->len) ) ) {
/* we are extending the previous heap block (slicing) */
/* nb. account for the end-of-heap tag */
brk_value -= TAG_SIZE;
amount += TAG_SIZE;
flp = (frlptr) brk_value;
/* adjust current entry in heap list */
p1->len += amount;
/* fix up end of heap links */
last_tag = (tag *) ( (PTR)flp + amount );
last_tag[0] = END_TAG;
} else {
if( amount < sizeof( struct miniheapblkp ) + sizeof( frl ) ) {
/* there isn't enough for a heap block (struct miniheapblkp) and
one free block (frl) */
return( 0 );
}
// Initializing the near heap if __nheapbeg == NULL,
// otherwise, a new mini-heap is getting linked up
p1 = (mheapptr) brk_value;
p1->len = amount;
flp = __LinkUpNewMHeap( p1 );
amount = flp->len;
}
/* build a block for _nfree() */
flp->len = amount | 1;
++p1->numalloc; /* 28-dec-90 */
p1->largest_blk = ~0; /* set to largest value to be safe */
_nfree( (PTR)flp + TAG_SIZE );
return( 1 );
#endif
}