/* -*- 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 //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->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*/ } //3456789_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(1); } return outErr; } // } ===== end nsIMdbHeap methods =====