blob: 90ac005d76b123fdd09a10cf526220980aa44ea9 [file] [log] [blame]
/**********************************************************************
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// @@@ END COPYRIGHT @@@
**********************************************************************/
/* -*-C++-*-
*****************************************************************************
*
* File: ComSpace.cpp
* Description:
*
* Created: 7/10/95
* Language: C++
*
*
*****************************************************************************
*/
#include "Platform.h"
#include <iostream>
#include <limits.h>
// #define TRACE_DP2_MALLOCS // Does not work for NSK DP2!
#ifdef TRACE_DP2_MALLOCS
#include <fstream>
extern ostream *TraceFile;
#endif
#include <stdlib.h>
#include <string.h>
#include "ComASSERT.h"
#include "ComSpace.h"
#include "str.h"
#include "HeapLog.h"
void * operator new(size_t size, Space *s)
{
if (s)
return s->allocateSpaceMemory(size);
else
{
void * result = ::operator new(size);
ComASSERT(result);
return result;
}
}
ComSpace::ComSpace(SpaceType type, NABoolean fillUp, char *name)
: type_(type),
parent_(NULL),
firstBlock_(NULL),
lastBlock_(NULL),
searchList_(NULL),
fillUp_(fillUp),
initialSize_(0)
{
derivedClass_ = COMSPACE_CLASS;
if (name != NULL)
setName(name);
outputbuf_ = FALSE;
totalSize_ = 0;
allocSize_ = 0;
}
ComSpace::~ComSpace()
{
destroy();
}
void Space::setType(SpaceType type, Lng32 initialSize)
{
type_ = type;
initialSize_ = initialSize;
};
void Space::setParent(CollHeap * parent) {
// make sure that we haven't allocated anything yet. If something
// is already allocated, we do not set parent_.
if (allocSize_)
return;
parent_ = parent;
}
void ComSpace::destroy() {
freeBlocks();
}
void Space::freeBlocks(void) {
HEAPLOG_REINITIALIZE(heapID_.heapNum)
// free up all the blocks attached to this space pointer.
Block * currBlock = firstBlock_;
switch (type_) {
case SINGLE_BLOCK_SPACE:
break;
case SYSTEM_SPACE:
case EXECUTOR_SPACE:
case GENERATOR_SPACE: {
// later change it to deallocate from executor segment. TBD.
while (currBlock) {
Block * nextBlock = currBlock->getNext();
if (parent_)
{
HEAPLOG_OFF() // no recursive logging. (eric)
parent_->deallocateMemory((void *) currBlock);
HEAPLOG_ON()
}
else
{
free(currBlock);
}
// printf("F\tComSpace\t%8x\t%8x\n", currBlock, this);
currBlock = nextBlock;
}
}
break;
}
firstBlock_ = NULL;
lastBlock_ = NULL;
searchList_ = NULL;
allocSize_ = 0;
totalSize_ = 0;
}
Lng32 Space::defaultBlockSize(SpaceType type)
{
Lng32 block_size = 0;
// use default size
switch (type)
{
case EXECUTOR_SPACE:
case GENERATOR_SPACE:
case SYSTEM_SPACE:
block_size = 32768;
break;
case SINGLE_BLOCK_SPACE:
block_size = -1;
break;
}
return block_size;
}
// Round up input value to nearest multiple of 8.
//
static Lng32 roundUp8(Lng32 val)
{
ULng32 uval = (ULng32) val;
// clear the last 3 bits, which effectively rounds
// down to the nearest multiple of 8
ULng32 roundedDown = uval LAND 0xFFFFFFF8;
// if that didn't change anything we're done
if (uval != roundedDown)
{
// else we have to round up and add the filler
val = (Lng32) (roundedDown + 8);
}
return val;
}
Block * Space::allocateBlock(SpaceType type,
Lng32 in_block_size,
NABoolean firstAllocation,
char * in_block_addr,
NABoolean failureIsFatal)
{
Block * block;
char * block_ptr = 0;
Lng32 block_size;
Lng32 data_size;
char * data_ptr;
// Don't satisfy request for more blocks if this is a SINGLE_BLOCK_SPACE.
if (type==SINGLE_BLOCK_SPACE)
return NULL;
// size of rounded-up block header
const Lng32 BlockHdrAllocSz = roundUp8(sizeof(Block));
// assert that in_block_size is a multiple of 8
ComASSERT(in_block_size==roundUp8(in_block_size));
Lng32 defBlkSz;
if (firstAllocation && (initialSize_ > 0))
defBlkSz = initialSize_;
else
defBlkSz = defaultBlockSize(type);
Lng32 needed_size = in_block_size + BlockHdrAllocSz;
// Set block_size, size of block to be allocated, to:
//
// max(needed_size, defBlkSz)
//
if (needed_size > defBlkSz)
block_size = needed_size;
else
block_size = defBlkSz;
// ---------------------------------------------------------------------
// Special case: if this is the first allocation for this Space, and the
// needed space is tiny (< 32 bytes), and this is a SYSTEM_SPACE, then
// we only allocate a tiny block (64 bytes). This is part of an effort
// to reduce the total size of tdm_arkcmp.exe. Before this change,
// there were 200+ Space objects contained in arkcmp (they came from the
// compiled system modules), each of which was 32k in size, but each
// using only 24 bytes! In other words, arkcmp was using over 6.4MB of
// virtual memory which was completely unused! This strange special
// case logic seems a small price to pay in order to reduce arkcmp's
// size by so much.
if ( firstAllocation == TRUE &&
type == EXECUTOR_SPACE && // see /cli/Statement.cpp, Statement ctor
in_block_addr == NULL &&
in_block_size < 32 &&
needed_size < 64 ) // this last one's a safety check
block_size = 64 ;
// ---------------------------------------------------------------------
if (in_block_addr != NULL) {
block_ptr = in_block_addr;
}
else {
switch (type) {
case EXECUTOR_SPACE:
case GENERATOR_SPACE:
case SYSTEM_SPACE: {
// allocate space from executor extended segment. TBD.
// For now, get it from system space.
if (parent_)
{
HEAPLOG_OFF() // no recursive logging. (eric)
block_ptr = (char *) parent_->allocateMemory((size_t) block_size, failureIsFatal);
HEAPLOG_ON()
}
else
{
block_ptr = (char *)malloc((UInt32)block_size);
}
// printf("M\tComSpace\t%8x\t%8x\t%8x\t%8x\n", block_ptr, block_size,
// block_ptr + block_size, this);
}
break;
}
}
data_ptr = block_ptr + BlockHdrAllocSz;
data_size = block_size - BlockHdrAllocSz;
if (block_ptr == NULL)
return NULL;
block = (Block *)block_ptr;
block->init(block_size, data_size, data_ptr);
totalSize_ += block_size;
return block;
}
void Space::setFirstBlock(char * blockAddr, Lng32 blockLen, NABoolean failureIsFatal)
{
if (firstBlock_)
return;
initialSize_ = blockLen;
allocateBlock(type_, 0, TRUE, blockAddr, failureIsFatal);
}
char *Space::privateAllocateSpace(ULng32 size, NABoolean failureIsFatal) {
if (size <= 0)
return NULL;
if (!firstBlock_)
{
// for the first allocation from a Space, for memory-usage issues,
// we do something special in firstAllocation
const NABoolean firstAllocation = TRUE ;
firstBlock_ = lastBlock_ = searchList_ =
allocateBlock(type_, size, firstAllocation, NULL, failureIsFatal);
}
if (firstBlock_ == NULL)
return NULL;
char * sp = 0;
Block * currBlock = searchList_;
Block * prevBlock = 0;
// try to allocate in the current block. If this is not possible,
// go thru the list of searchable blocks and try to find a block
// with enough free space. Do this only if fillUp_ is true. Otherwise
// we just allocate another block.
while ((currBlock) &&
(!(sp = currBlock->allocateMemory(size))) &&
fillUp_) {
// if the current block does not have any free space, remove it
// from the list of searchable blocks.
if (!currBlock->getFreeSpace()) {
if (!prevBlock)
// this is the first block in the list, just skip it
searchList_ = searchList_->getNextSearch();
else
prevBlock->setNextSearch(currBlock->getNextSearch());
}
else
// currBlock is not empty. Set prevBlock
prevBlock = currBlock;
// we could not allocate in currBlock. Go on with the next block
// in the searchList.
currBlock = currBlock->getNextSearch();
if ((outputbuf_) && (currBlock))
currBlock = 0;
}
if (!sp) {
// space not found in any existing block.
// Allocate a new one and append it to the last block.
// The minimum space allocated in a block is max size for 'type'.
currBlock = allocateBlock(type_, size, FALSE, NULL, failureIsFatal);
// return if we couldn't allocate a block
if (currBlock == NULL)
return NULL;
// add the new block to the list of all blocks
lastBlock_->setNext(currBlock);
lastBlock_ = currBlock;
// add the new block to the list of searchable blocks
// add it to the beginning of this list
currBlock->setNextSearch(searchList_);
searchList_ = currBlock;
sp = currBlock->allocateMemory(size);
if (!sp)
return NULL;
}
allocSize_ += size;
if (type_ == GENERATOR_SPACE)
{
// initialize the memory to 0, this sets filler bytes to a
// defined value and avoids junk data to be included in
// module files and resource forks
//str_pad(sp, size, 0);
memset(sp, 0, size);
}
return sp;
}
void * Space::allocateSpaceMemory(size_t size, NABoolean failureIsFatal) {
if (! checkSize(size, failureIsFatal))
return NULL;
void * rc = allocateAlignedSpace(size, failureIsFatal);
HEAPLOG_ADD_ENTRY(rc, size, heapID_.heapNum, getName())
if (rc) return rc;
if (failureIsFatal && size > 0)
{
// Might never return...
handleExhaustedMemory();
// If we return from this call it means that the caller wanted
// a memory allocation failure to be fatal yet did not set the
// the jump buffer. This is not good.
assert(0);
return NULL;
}
return rc;
}
char *Space::allocateAlignedSpace(size_t size, NABoolean failureIsFatal) {
if (size <= 0)
return NULL;
if (! checkSize(size, failureIsFatal))
return NULL;
// return aligned space on an 8 byte boundary
return privateAllocateSpace((ULng32) roundUp8((Lng32)size), failureIsFatal);
}
// If countPrefixSize == 0, make a null-terminated string;
// else, make a count-prefixed string (no null terminal).
//
char *Space::allocateAndCopyToAlignedSpace(const char *dp,
size_t dlen,
size_t countPrefixSize,
NABoolean failureIsFatal,
NABoolean noSizeAdjustment)
{
size_t alen;
if (noSizeAdjustment)
alen = dlen;
else
{
if (countPrefixSize == 0)
alen = dlen + 1;
else
alen = countPrefixSize + dlen;
}
if (! checkSize(alen, failureIsFatal))
return NULL;
char* rp = allocateAlignedSpace(alen, failureIsFatal);
switch (countPrefixSize) {
case 0:
break;
case sizeof(char):
ComASSERT(dlen <= UCHAR_MAX)
*(char *)rp = dlen;
break;
case sizeof(short):
ComASSERT(dlen <= USHRT_MAX)
*(short *)rp = dlen;
break;
case sizeof(Int32):
ComASSERT(dlen <= UINT_MAX)
*(Int32 *)rp = dlen;
break;
default:
ComASSERT(0==1);
}
char* rdp = rp + countPrefixSize;
str_cpy_all(rdp, dp, dlen);
if ((countPrefixSize == 0) && (NOT noSizeAdjustment))
rdp[dlen] = '\0';
return rp;
} // allocateAndCopyToAlignedSpace()
void Space::outputBuffer(ComSpace * space, char * buf, char * newbuf)
{
if ((strlen(buf) + strlen(newbuf)) > 75)
{
space->allocateAndCopyToAlignedSpace(buf, strlen(buf), sizeof(short));
strcpy(buf, newbuf);
}
else
{
strcat(buf, newbuf);
}
}
void Space::display(char *buf,
size_t buflen,
size_t countPrefixSize, ostream &outstream)
{
size_t dlen = 0;
char *bend = buf + buflen;
while (buf < bend) {
switch (countPrefixSize) {
case sizeof(char):
dlen = *(unsigned char *)buf;
break;
case sizeof(short):
dlen = *(unsigned short *)buf;
break;
case sizeof(Lng32):
dlen = *(ULng32 *)buf;
break;
default:
ComASSERT(0==1);
}
buf += countPrefixSize;
if (dlen) {
char sav = buf[--dlen];
buf[dlen] = '\0';
outstream << buf << sav << '\n';
buf[dlen] = sav;
buf += ++dlen;
}
else
outstream << '\n';
}
outstream << flush;
ComASSERT(buf == bend);
}
// NOTE: Sometimes Space::convertToOffset() returns an offset.
// Other times, it returns an actual address.
// Consequently, we declare the return value as "long"
// so it can hold either one.
Long Space::convertToOffset(void * ptr)
{
Block * curr_block = firstBlock_;
// find which block this pointer is allocated from.
short found = 0;
Lng32 prev_offset = 0;
while ((curr_block) && (! found))
{
if (((char *)ptr) >= (char *)(curr_block->getDataPtr()) &&
((char *)ptr) < ((char *)(curr_block->getDataPtr()) +
curr_block->getAllocatedSize()))
return -(Long)((char *)ptr - (char *)(curr_block->getDataPtr())
+ prev_offset);
prev_offset += curr_block->getAllocatedSize();
curr_block = curr_block->getNext();
}
// pointer not allocated from any of the existing blocks.
// Return it as is, it is either an offset already or a
// garbage pointer. The caller should handle error case.
return (Long)(ptr);
}
void* Space::convertToPtr(Long offset) const
{
Block * curr_block = firstBlock_;
// find which block this offset falls into
Long remainder = offset;
while (curr_block && remainder <= 0) {
if (curr_block->getAllocatedSize() + remainder > 0) {
return curr_block->getDataPtr() - remainder;
}
remainder += curr_block->getAllocatedSize();
curr_block = curr_block->getNext();
}
// offset does not fall into any of the existing blocks.
// Return a NULL. Let caller check for & handle error case.
return NULL;
}
// The method is not called elsewhere
Lng32 Space::allocAndCopy(void * from, ULng32 size, NABoolean failureIsFatal)
{
char * to = allocateAlignedSpace(size, failureIsFatal);
str_cpy_all(to, (char *)from, size);
return (convertToOffset(to));
}
#if 0 /* NOT CURRENTLY USED and does not compile for 64 bits */
short Space::isOffset(void * ptr)
{
if ((Lng32)ptr < 0)
return -1;
else
return 0;
}
#endif
NABoolean Space::isOverlappingMyBlocks(char *buf, ULng32 size)
{
Block *curr_block = firstBlock_;
while (curr_block)
{
if (curr_block->isOverlapping(buf, size))
return TRUE;
curr_block = curr_block->getNext();
}
return FALSE;
}
// moves all the Blocks into the output contiguous buffer.
char * Space::makeContiguous(char * out_buf, ULng32 out_buflen)
{
Block * curr_block = firstBlock_;
Lng32 curr_offset = 0;
while (curr_block)
{
if ((Lng32) out_buflen - curr_offset < curr_block->getAllocatedSize())
return 0;
str_cpy_all(&out_buf[curr_offset],
curr_block->getDataPtr(),
curr_block->getAllocatedSize());
curr_offset += curr_block->getAllocatedSize();
curr_block = curr_block->getNext();
}
return out_buf;
}
#if (defined(_DEBUG) || defined(NSK_MEMDEBUG))
void Space::dumpSpaceInfo(ostream* outstream, Lng32 indent) {
char ind[100];
Lng32 indIdx = 0;
for (; indIdx < indent; indIdx++)
ind[indIdx] = ' ';
ind[indIdx] = '\0';
if (!outstream)
outstream = &cerr;
*outstream << ind << "Dump of Space: " << this << " (";
switch (type_) {
case EXECUTOR_SPACE:
*outstream << "EXECUTOR_SPACE";
break;
case GENERATOR_SPACE:
*outstream << "GENERATOR_SPACE";
break;
case SYSTEM_SPACE:
*outstream << "SYSTEM_SPACE";
break;
}
*outstream << "):" << endl
<< ind << "Parent: " << parent_ << endl
<< ind << "Total Size(Bytes) " << totalSize_ << endl
<< ind << "Total Allocated Size (Bytes): " << allocSize_ << endl;
}
#endif
/////////////////////////////////////////////
//
// class Block
//
/////////////////////////////////////////////
void Block::init(Lng32 block_size, Lng32 data_size, char * data_ptr)
{
dataPtr_ = data_ptr;
blockSize_ = block_size;
freeSpaceSize_ = maxSize_ = data_size;
allocatedSize_ = 0;
freeSpaceOffset_ = 0;
nextBlock_ = NULL;
nextSearchBlock_ = NULL;
}
char *Block::allocateMemory(ULng32 size)
{
if (freeSpaceSize_ < (Lng32) size)
return 0;
freeSpaceSize_ -= size;
allocatedSize_ += size;
char *s = &dataPtr_[freeSpaceOffset_];
freeSpaceOffset_ += size;
return s;
}
NABoolean Block::isOverlapping(char * buf, ULng32 size)
{
if ((buf >= dataPtr_) &&
(buf < (dataPtr_ + blockSize_)))
// buf starts within block.
return TRUE;
if ((buf < dataPtr_) &&
((buf + size) > dataPtr_))
// buf starts before block, but is too long and thus overlaps block.
return TRUE;
return FALSE;
}