Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#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
// 456789_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->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);
}
// } ===== 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*/
}
// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
// { ===== begin nsIMdbHeap methods =====
/*virtual*/ nsresult 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
{
nsresult outErr = NS_OK;
void* block = 0;
morkEnv* ev = morkEnv::FromMdbEnv(mev);
if (ev) {
block = this->ZoneNewRun(ev, inSize);
outErr = ev->AsErr();
} else
outErr = morkEnv_kOutOfMemoryError;
if (outBlock) *outBlock = block;
return outErr;
}
/*virtual*/ nsresult morkZone::Free(
nsIMdbEnv* mev, // free block allocated earlier by Alloc()
void* inBlock) {
nsresult outErr = NS_OK;
if (inBlock) {
morkEnv* ev = morkEnv::FromMdbEnv(mev);
if (ev) {
this->ZoneZapRun(ev, inBlock);
outErr = ev->AsErr();
} else
// XXX 1 is not a valid nsresult
outErr = static_cast<nsresult>(1);
}
return outErr;
}
// } ===== end nsIMdbHeap methods =====