Files
Mozilla/mozilla/db/mork/src/morkZone.cpp
bienvenu%netscape.com 6876ad97b0 fix crash importing long mailing list r=naving, sr=sspitzer 62084
git-svn-id: svn://10.0.0.236/trunk@121034 18797224-902f-48f8-a5cc-f745e15eee43
2002-05-08 03:16:22 +00:00

579 lines
15 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Netscape Public License
* Version 1.1 (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.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _MDB_
#include "mdb.h"
#endif
#ifndef _MORK_
#include "mork.h"
#endif
#ifndef _MORKNODE_
#include "morkNode.h"
#endif
#ifndef _MORKZONE_
#include "morkZone.h"
#endif
#ifndef _MORKENV_
#include "morkEnv.h"
#endif
//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
// { ===== begin morkNode interface =====
// public: // morkNode virtual methods
void morkZone::CloseMorkNode(morkEnv* ev) // CloseZone() only if open
{
if ( this->IsOpenNode() )
{
this->MarkClosing();
this->CloseZone(ev);
this->MarkShut();
}
}
morkZone::~morkZone() // assert that CloseZone() executed earlier
{
MORK_ASSERT(this->IsShutNode());
}
// public: // morkMap construction & destruction
morkZone::morkZone(morkEnv* ev, const morkUsage& inUsage,
nsIMdbHeap* ioNodeHeap, nsIMdbHeap* ioZoneHeap)
: morkNode(ev, inUsage, ioNodeHeap)
, mZone_Heap( 0 )
, mZone_HeapVolume( 0 )
, mZone_BlockVolume( 0 )
, mZone_RunVolume( 0 )
, mZone_ChipVolume( 0 )
, mZone_FreeOldRunVolume( 0 )
, mZone_HunkCount( 0 )
, mZone_FreeOldRunCount( 0 )
, mZone_HunkList( 0 )
, mZone_FreeOldRunList( 0 )
, mZone_At( 0 )
, mZone_AtSize( 0 )
// morkRun* mZone_FreeRuns[ morkZone_kBuckets + 1 ];
{
morkRun** runs = mZone_FreeRuns;
morkRun** end = runs + (morkZone_kBuckets + 1); // one past last slot
--runs; // prepare for preincrement
while ( ++runs < end ) // another slot in array?
*runs = 0; // clear all the slots
if ( ev->Good() )
{
if ( ioZoneHeap )
{
nsIMdbHeap_SlotStrongHeap(ioZoneHeap, ev, &mZone_Heap);
if ( ev->Good() )
mNode_Derived = morkDerived_kZone;
}
else
ev->NilPointerError();
}
}
void morkZone::CloseZone(morkEnv* ev) // called by CloseMorkNode()
{
if ( this )
{
if ( this->IsNode() )
{
nsIMdbHeap* heap = mZone_Heap;
if ( heap )
{
morkHunk* hunk = 0;
nsIMdbEnv* mev = ev->AsMdbEnv();
morkHunk* next = mZone_HunkList;
while ( ( hunk = next ) != 0 )
{
#ifdef morkHunk_USE_TAG_SLOT
if ( !hunk->HunkGoodTag() )
hunk->BadHunkTagWarning(ev);
#endif /* morkHunk_USE_TAG_SLOT */
next = hunk->HunkNext();
heap->Free(mev, hunk);
}
}
nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mZone_Heap);
this->MarkShut();
}
else
this->NonNodeError(ev);
}
else
ev->NilPointerError();
}
// } ===== end morkNode methods =====
/*static*/ void
morkZone::NonZoneTypeError(morkEnv* ev)
{
ev->NewError("non morkZone");
}
/*static*/ void
morkZone::NilZoneHeapError(morkEnv* ev)
{
ev->NewError("nil mZone_Heap");
}
/*static*/ void
morkHunk::BadHunkTagWarning(morkEnv* ev)
{
ev->NewWarning("bad mHunk_Tag");
}
/*static*/ void
morkRun::BadRunTagError(morkEnv* ev)
{
ev->NewError("bad mRun_Tag");
}
/*static*/ void
morkRun::RunSizeAlignError(morkEnv* ev)
{
ev->NewError("bad RunSize() alignment");
}
// { ===== begin morkZone methods =====
mork_size morkZone::zone_grow_at(morkEnv* ev, mork_size inNeededSize)
{
mZone_At = 0; // remove any ref to current hunk
mZone_AtSize = 0; // zero available bytes in current hunk
mork_size runSize = 0; // actual size of a particular run
// try to find a run in old run list with at least inNeededSize bytes:
morkRun* run = mZone_FreeOldRunList; // cursor in list scan
morkRun* prev = 0; // the node before run in the list scan
while ( run ) // another run in list to check?
{
morkOldRun* oldRun = (morkOldRun*) run;
mork_size oldSize = oldRun->OldSize();
if ( oldSize >= inNeededSize ) // found one big enough?
{
runSize = oldSize;
break; // end while loop early
}
prev = run; // remember last position in singly linked list
run = run->RunNext(); // advance cursor to next node in list
}
if ( runSize && run ) // found a usable old run?
{
morkRun* next = run->RunNext();
if ( prev ) // another node in free list precedes run?
prev->RunSetNext(next); // unlink run
else
mZone_FreeOldRunList = next; // unlink run from head of list
morkOldRun *oldRun = (morkOldRun *) run;
oldRun->OldSetSize(runSize);
mZone_At = (mork_u1*) run;
mZone_AtSize = runSize;
#ifdef morkZone_CONFIG_DEBUG
#ifdef morkZone_CONFIG_ALIGN_8
mork_ip lowThree = ((mork_ip) mZone_At) & 7;
if ( lowThree ) // not 8 byte aligned?
#else /*morkZone_CONFIG_ALIGN_8*/
mork_ip lowTwo = ((mork_ip) mZone_At) & 3;
if ( lowTwo ) // not 4 byte aligned?
#endif /*morkZone_CONFIG_ALIGN_8*/
ev->NewWarning("mZone_At not aligned");
#endif /*morkZone_CONFIG_DEBUG*/
}
else // need to allocate a brand new run
{
inNeededSize += 7; // allow for possible alignment padding
mork_size newSize = ( inNeededSize > morkZone_kNewHunkSize )?
inNeededSize : morkZone_kNewHunkSize;
morkHunk* hunk = this->zone_new_hunk(ev, newSize);
if ( hunk )
{
morkRun* hunkRun = hunk->HunkRun();
mork_u1* at = (mork_u1*) hunkRun->RunAsBlock();
mork_ip lowBits = ((mork_ip) at) & 7;
if ( lowBits ) // not 8 byte aligned?
{
mork_ip skip = (8 - lowBits); // skip the complement to align
at += skip;
newSize -= skip;
}
mZone_At = at;
mZone_AtSize = newSize;
}
}
return mZone_AtSize;
}
morkHunk* morkZone::zone_new_hunk(morkEnv* ev, mdb_size inSize) // alloc
{
mdb_size hunkSize = inSize + sizeof(morkHunk);
void* outBlock = 0; // we are going straight to the heap:
mZone_Heap->Alloc(ev->AsMdbEnv(), hunkSize, &outBlock);
if ( outBlock )
{
#ifdef morkZone_CONFIG_VOL_STATS
mZone_HeapVolume += hunkSize; // track all heap allocations
#endif /* morkZone_CONFIG_VOL_STATS */
morkHunk* hunk = (morkHunk*) outBlock;
#ifdef morkHunk_USE_TAG_SLOT
hunk->HunkInitTag();
#endif /* morkHunk_USE_TAG_SLOT */
hunk->HunkSetNext(mZone_HunkList);
mZone_HunkList = hunk;
++mZone_HunkCount;
morkRun* run = hunk->HunkRun();
run->RunSetSize(inSize);
#ifdef morkRun_USE_TAG_SLOT
run->RunInitTag();
#endif /* morkRun_USE_TAG_SLOT */
return hunk;
}
if ( ev->Good() ) // got this far without any error reported yet?
ev->OutOfMemoryError();
return (morkHunk*) 0;
}
void* morkZone::zone_new_chip(morkEnv* ev, mdb_size inSize) // alloc
{
#ifdef morkZone_CONFIG_VOL_STATS
mZone_BlockVolume += inSize; // sum sizes of both chips and runs
#endif /* morkZone_CONFIG_VOL_STATS */
mork_u1* at = mZone_At;
mork_size atSize = mZone_AtSize; // available bytes in current hunk
if ( atSize >= inSize ) // current hunk can satisfy request?
{
mZone_At = at + inSize;
mZone_AtSize = atSize - inSize;
return at;
}
else if ( atSize > morkZone_kMaxHunkWaste ) // over max waste allowed?
{
morkHunk* hunk = this->zone_new_hunk(ev, inSize);
if ( hunk )
return hunk->HunkRun();
return (void*) 0; // show allocation has failed
}
else // get ourselves a new hunk for suballocation:
{
atSize = this->zone_grow_at(ev, inSize); // get a new hunk
}
if ( atSize >= inSize ) // current hunk can satisfy request?
{
at = mZone_At;
mZone_At = at + inSize;
mZone_AtSize = atSize - inSize;
return at;
}
if ( ev->Good() ) // got this far without any error reported yet?
ev->OutOfMemoryError();
return (void*) 0; // show allocation has failed
}
void* morkZone::ZoneNewChip(morkEnv* ev, mdb_size inSize) // alloc
{
#ifdef morkZone_CONFIG_ARENA
#ifdef morkZone_CONFIG_DEBUG
if ( !this->IsZone() )
this->NonZoneTypeError(ev);
else if ( !mZone_Heap )
this->NilZoneHeapError(ev);
#endif /*morkZone_CONFIG_DEBUG*/
#ifdef morkZone_CONFIG_ALIGN_8
inSize += 7;
inSize &= ~((mork_ip) 7); // force to multiple of 8 bytes
#else /*morkZone_CONFIG_ALIGN_8*/
inSize += 3;
inSize &= ~((mork_ip) 3); // force to multiple of 4 bytes
#endif /*morkZone_CONFIG_ALIGN_8*/
#ifdef morkZone_CONFIG_VOL_STATS
mZone_ChipVolume += inSize; // sum sizes of chips only
#endif /* morkZone_CONFIG_VOL_STATS */
return this->zone_new_chip(ev, inSize);
#else /*morkZone_CONFIG_ARENA*/
void* outBlock = 0;
mZone_Heap->Alloc(ev->AsMdbEnv(), inSize, &outBlock);
return outBlock;
#endif /*morkZone_CONFIG_ARENA*/
}
// public: // ...but runs do indeed know how big they are
void* morkZone::ZoneNewRun(morkEnv* ev, mdb_size inSize) // alloc
{
#ifdef morkZone_CONFIG_ARENA
#ifdef morkZone_CONFIG_DEBUG
if ( !this->IsZone() )
this->NonZoneTypeError(ev);
else if ( !mZone_Heap )
this->NilZoneHeapError(ev);
#endif /*morkZone_CONFIG_DEBUG*/
inSize += morkZone_kRoundAdd;
inSize &= morkZone_kRoundMask;
if ( inSize <= morkZone_kMaxCachedRun )
{
morkRun** bucket = mZone_FreeRuns + (inSize >> morkZone_kRoundBits);
morkRun* hit = *bucket;
if ( hit ) // cache hit?
{
*bucket = hit->RunNext();
hit->RunSetSize(inSize);
return hit->RunAsBlock();
}
}
mdb_size blockSize = inSize + sizeof(morkRun); // plus run overhead
#ifdef morkZone_CONFIG_VOL_STATS
mZone_RunVolume += blockSize; // sum sizes of runs only
#endif /* morkZone_CONFIG_VOL_STATS */
morkRun* run = (morkRun*) this->zone_new_chip(ev, blockSize);
if ( run )
{
run->RunSetSize(inSize);
#ifdef morkRun_USE_TAG_SLOT
run->RunInitTag();
#endif /* morkRun_USE_TAG_SLOT */
return run->RunAsBlock();
}
if ( ev->Good() ) // got this far without any error reported yet?
ev->OutOfMemoryError();
return (void*) 0; // indicate failed allocation
#else /*morkZone_CONFIG_ARENA*/
void* outBlock = 0;
mZone_Heap->Alloc(ev->AsMdbEnv(), inSize, &outBlock);
return outBlock;
#endif /*morkZone_CONFIG_ARENA*/
}
void morkZone::ZoneZapRun(morkEnv* ev, void* ioRunBlock) // free
{
#ifdef morkZone_CONFIG_ARENA
morkRun* run = morkRun::BlockAsRun(ioRunBlock);
mdb_size runSize = run->RunSize();
#ifdef morkZone_CONFIG_VOL_STATS
mZone_BlockVolume -= runSize; // tracking sizes of both chips and runs
#endif /* morkZone_CONFIG_VOL_STATS */
#ifdef morkZone_CONFIG_DEBUG
if ( !this->IsZone() )
this->NonZoneTypeError(ev);
else if ( !mZone_Heap )
this->NilZoneHeapError(ev);
else if ( !ioRunBlock )
ev->NilPointerError();
else if ( runSize & morkZone_kRoundAdd )
run->RunSizeAlignError(ev);
#ifdef morkRun_USE_TAG_SLOT
else if ( !run->RunGoodTag() )
run->BadRunTagError(ev);
#endif /* morkRun_USE_TAG_SLOT */
#endif /*morkZone_CONFIG_DEBUG*/
if ( runSize <= morkZone_kMaxCachedRun ) // goes into free run list?
{
morkRun** bucket = mZone_FreeRuns + (runSize >> morkZone_kRoundBits);
run->RunSetNext(*bucket); // push onto free run list
*bucket = run;
}
else // free old run list
{
run->RunSetNext(mZone_FreeOldRunList); // push onto free old run list
mZone_FreeOldRunList = run;
++mZone_FreeOldRunCount;
#ifdef morkZone_CONFIG_VOL_STATS
mZone_FreeOldRunVolume += runSize;
#endif /* morkZone_CONFIG_VOL_STATS */
morkOldRun* oldRun = (morkOldRun*) run; // to access extra size slot
oldRun->OldSetSize(runSize); // so we know how big this is later
}
#else /*morkZone_CONFIG_ARENA*/
mZone_Heap->Free(ev->AsMdbEnv(), ioRunBlock);
#endif /*morkZone_CONFIG_ARENA*/
}
void* morkZone::ZoneGrowRun(morkEnv* ev, void* ioRunBlock, mdb_size inSize)
{
#ifdef morkZone_CONFIG_ARENA
morkRun* run = morkRun::BlockAsRun(ioRunBlock);
mdb_size runSize = run->RunSize();
#ifdef morkZone_CONFIG_DEBUG
if ( !this->IsZone() )
this->NonZoneTypeError(ev);
else if ( !mZone_Heap )
this->NilZoneHeapError(ev);
#endif /*morkZone_CONFIG_DEBUG*/
#ifdef morkZone_CONFIG_ALIGN_8
inSize += 7;
inSize &= ~((mork_ip) 7); // force to multiple of 8 bytes
#else /*morkZone_CONFIG_ALIGN_8*/
inSize += 3;
inSize &= ~((mork_ip) 3); // force to multiple of 4 bytes
#endif /*morkZone_CONFIG_ALIGN_8*/
if ( inSize > runSize )
{
void* newBuf = this->ZoneNewRun(ev, inSize);
if ( newBuf )
{
MORK_MEMCPY(newBuf, ioRunBlock, runSize);
this->ZoneZapRun(ev, ioRunBlock);
return newBuf;
}
}
else
return ioRunBlock; // old size is big enough
if ( ev->Good() ) // got this far without any error reported yet?
ev->OutOfMemoryError();
return (void*) 0; // indicate failed allocation
#else /*morkZone_CONFIG_ARENA*/
void* outBlock = 0;
mZone_Heap->Free(ev->AsMdbEnv(), ioRunBlock);
return outBlock;
#endif /*morkZone_CONFIG_ARENA*/
}
//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
// { ===== begin nsIMdbHeap methods =====
/*virtual*/ mdb_err
morkZone::Alloc(nsIMdbEnv* mev, // allocate a piece of memory
mdb_size inSize, // requested size of new memory block
void** outBlock) // memory block of inSize bytes, or nil
{
mdb_err outErr = 0;
void* block = 0;
morkEnv* ev = morkEnv::FromMdbEnv(mev);
if ( ev )
{
block = this->ZoneNewRun(ev, inSize);
outErr = ev->AsErr();
}
else
outErr = 1;
if ( outBlock )
*outBlock = block;
return outErr;
}
/*virtual*/ mdb_err
morkZone::Free(nsIMdbEnv* mev, // free block allocated earlier by Alloc()
void* inBlock)
{
mdb_err outErr = 0;
if ( inBlock )
{
morkEnv* ev = morkEnv::FromMdbEnv(mev);
if ( ev )
{
this->ZoneZapRun(ev, inBlock);
outErr = ev->AsErr();
}
else
outErr = 1;
}
return outErr;
}
/*virtual*/ mdb_err
morkZone::HeapAddStrongRef(nsIMdbEnv* mev) // does nothing
{
MORK_USED_1(mev);
return 0;
}
/*virtual*/ mdb_err
morkZone::HeapCutStrongRef(nsIMdbEnv* mev) // does nothing
{
MORK_USED_1(mev);
return 0;
}
// } ===== end nsIMdbHeap methods =====