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