summaryrefslogtreecommitdiff
path: root/db/mork/src/morkZone.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'db/mork/src/morkZone.cpp')
-rw-r--r--db/mork/src/morkZone.cpp527
1 files changed, 527 insertions, 0 deletions
diff --git a/db/mork/src/morkZone.cpp b/db/mork/src/morkZone.cpp
new file mode 100644
index 0000000000..239e86bba0
--- /dev/null
+++ b/db/mork/src/morkZone.cpp
@@ -0,0 +1,527 @@
+/* -*- 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<nsresult>(1);
+ }
+
+ return outErr;
+}
+
+// } ===== end nsIMdbHeap methods =====
+