diff options
Diffstat (limited to 'comm/mailnews/db/mork/morkZone.cpp')
-rw-r--r-- | comm/mailnews/db/mork/morkZone.cpp | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/comm/mailnews/db/mork/morkZone.cpp b/comm/mailnews/db/mork/morkZone.cpp new file mode 100644 index 0000000000..6ee3032f48 --- /dev/null +++ b/comm/mailnews/db/mork/morkZone.cpp @@ -0,0 +1,487 @@ +/* -*- 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 ===== |