summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/db/mork/morkZone.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/db/mork/morkZone.cpp')
-rw-r--r--comm/mailnews/db/mork/morkZone.cpp487
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 =====