/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // This code is a port to NS Mork from public domain Mithril C++ sources. // Note many code comments here come verbatim from cut-and-pasted Mithril. // In many places, code is identical; Mithril versions stay public domain. // Changes in porting are mainly class type and scalar type name changes. #include "nscore.h" #ifndef _MDB_ # include "mdb.h" #endif #ifndef _MORK_ # include "mork.h" #endif #ifndef _MORKNODE_ # include "morkNode.h" #endif #ifndef _MORKPROBEMAP_ # include "morkProbeMap.h" #endif #ifndef _MORKENV_ # include "morkEnv.h" #endif /*============================================================================*/ /* morkMapScratch */ void morkMapScratch::halt_map_scratch(morkEnv* ev) { nsIMdbHeap* heap = sMapScratch_Heap; if (heap) { if (sMapScratch_Keys) heap->Free(ev->AsMdbEnv(), sMapScratch_Keys); if (sMapScratch_Vals) heap->Free(ev->AsMdbEnv(), sMapScratch_Vals); } } // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 /*============================================================================*/ /* morkProbeMap */ void morkProbeMap::ProbeMapBadTagError(morkEnv* ev) const { ev->NewError("bad sProbeMap_Tag"); } void morkProbeMap::WrapWithNoVoidSlotError(morkEnv* ev) const { ev->NewError("wrap without void morkProbeMap slot"); } void morkProbeMap::GrowFailsMaxFillError(morkEnv* ev) const { ev->NewError("grow fails morkEnv > sMap_Fill"); } void morkProbeMap::MapKeyIsNotIPError(morkEnv* ev) const { ev->NewError("not sMap_KeyIsIP"); } void morkProbeMap::MapValIsNotIPError(morkEnv* ev) const { ev->NewError("not sMap_ValIsIP"); } void morkProbeMap::rehash_old_map(morkEnv* ev, morkMapScratch* ioScratch) { mork_size keySize = sMap_KeySize; // size of every key bucket mork_size valSize = sMap_ValSize; // size of every associated value mork_count slots = sMap_Slots; // number of new buckets mork_u1* keys = sMap_Keys; // destination for rehashed keys mork_u1* vals = sMap_Vals; // destination for any copied values mork_bool keyIsIP = (keys && keySize == sizeof(mork_ip) && sMap_KeyIsIP); mork_bool valIsIP = (vals && valSize == sizeof(mork_ip) && sMap_ValIsIP); mork_count oldSlots = ioScratch->sMapScratch_Slots; // sMap_Slots mork_u1* oldKeys = ioScratch->sMapScratch_Keys; // sMap_Keys mork_u1* oldVals = ioScratch->sMapScratch_Vals; // sMap_Vals mork_u1* end = oldKeys + (keySize * oldSlots); // one byte past last key mork_fill fill = 0; // let's count the actual fill for a double check while (oldKeys < end) // another old key bucket to rehash if non-nil? { if (!this->ProbeMapIsKeyNil(ev, oldKeys)) // need to rehash? { ++fill; // this had better match sMap_Fill when we are all done mork_u4 hash = this->ProbeMapHashMapKey(ev, oldKeys); mork_pos i = hash % slots; // target hash bucket mork_pos startPos = i; // remember start to detect mork_u1* k = keys + (i * keySize); while (!this->ProbeMapIsKeyNil(ev, k)) { if (++i >= (mork_pos)slots) // advanced past end? need to wrap around now? i = 0; // wrap around to first slot in map's hash table if (i == startPos) // no void slots were found anywhere in map? { this->WrapWithNoVoidSlotError(ev); // should never happen return; // this is bad, and we can't go on with the rehash } k = keys + (i * keySize); } if (keyIsIP) // int special case? *((mork_ip*)k) = *((const mork_ip*)oldKeys); // fast bitwise copy else MORK_MEMCPY(k, oldKeys, keySize); // slow bitwise copy if (oldVals) // need to copy values as well? { mork_size valOffset = (i * valSize); mork_u1* v = vals + valOffset; mork_u1* ov = oldVals + valOffset; if (valIsIP) // int special case? *((mork_ip*)v) = *((const mork_ip*)ov); // fast bitwise copy else MORK_MEMCPY(v, ov, valSize); // slow bitwise copy } } oldKeys += keySize; // advance to next key bucket in old map } if (fill != sMap_Fill) // is the recorded value of sMap_Fill wrong? { ev->NewWarning("fill != sMap_Fill"); sMap_Fill = fill; } } mork_bool morkProbeMap::grow_probe_map(morkEnv* ev) { if (sMap_Heap) // can we grow the map? { mork_num newSlots = ((sMap_Slots * 4) / 3) + 1; // +25% morkMapScratch old; // a place to temporarily hold all the old arrays if (this->new_slots(ev, &old, newSlots)) // have more? { ++sMap_Seed; // note the map has changed this->rehash_old_map(ev, &old); if (ev->Good()) { mork_count slots = sMap_Slots; mork_num emptyReserve = (slots / 7) + 1; // keep this many empty mork_fill maxFill = slots - emptyReserve; // new max occupancy if (maxFill > sMap_Fill) // new max is bigger than old occupancy? sProbeMap_MaxFill = maxFill; // we can install new max for fill else this->GrowFailsMaxFillError(ev); // we have invariant failure } if (ev->Bad()) // rehash failed? need to revert map to last state? this->revert_map(ev, &old); // swap the vectors back again old.halt_map_scratch(ev); // remember to free the old arrays } } else ev->OutOfMemoryError(); return ev->Good(); } void morkProbeMap::revert_map(morkEnv* ev, morkMapScratch* ioScratch) { mork_count tempSlots = ioScratch->sMapScratch_Slots; // sMap_Slots mork_u1* tempKeys = ioScratch->sMapScratch_Keys; // sMap_Keys mork_u1* tempVals = ioScratch->sMapScratch_Vals; // sMap_Vals ioScratch->sMapScratch_Slots = sMap_Slots; ioScratch->sMapScratch_Keys = sMap_Keys; ioScratch->sMapScratch_Vals = sMap_Vals; sMap_Slots = tempSlots; sMap_Keys = tempKeys; sMap_Vals = tempVals; } void morkProbeMap::put_probe_kv(morkEnv* ev, const void* inAppKey, const void* inAppVal, mork_pos inPos) { mork_u1* mapVal = 0; mork_u1* mapKey = 0; mork_num valSize = sMap_ValSize; if (valSize && inAppVal) // map holds values? caller sends value? { mork_u1* val = sMap_Vals + (valSize * inPos); if (valSize == sizeof(mork_ip) && sMap_ValIsIP) // int special case? *((mork_ip*)val) = *((const mork_ip*)inAppVal); else mapVal = val; // show possible need to call ProbeMapPushIn() } if (inAppKey) // caller sends the key? { mork_num keySize = sMap_KeySize; mork_u1* key = sMap_Keys + (keySize * inPos); if (keySize == sizeof(mork_ip) && sMap_KeyIsIP) // int special case? *((mork_ip*)key) = *((const mork_ip*)inAppKey); else mapKey = key; // show possible need to call ProbeMapPushIn() } else ev->NilPointerError(); if ((inAppVal && mapVal) || (inAppKey && mapKey)) this->ProbeMapPushIn(ev, inAppKey, inAppVal, mapKey, mapVal); if (sMap_Fill > sProbeMap_MaxFill) this->grow_probe_map(ev); } void morkProbeMap::get_probe_kv(morkEnv* ev, void* outAppKey, void* outAppVal, mork_pos inPos) const { const mork_u1* mapVal = 0; const mork_u1* mapKey = 0; mork_num valSize = sMap_ValSize; if (valSize && outAppVal) // map holds values? caller wants value? { const mork_u1* val = sMap_Vals + (valSize * inPos); if (valSize == sizeof(mork_ip) && sMap_ValIsIP) // int special case? *((mork_ip*)outAppVal) = *((const mork_ip*)val); else mapVal = val; // show possible need to call ProbeMapPullOut() } if (outAppKey) // caller wants the key? { mork_num keySize = sMap_KeySize; const mork_u1* key = sMap_Keys + (keySize * inPos); if (keySize == sizeof(mork_ip) && sMap_KeyIsIP) // int special case? *((mork_ip*)outAppKey) = *((const mork_ip*)key); else mapKey = key; // show possible need to call ProbeMapPullOut() } if ((outAppVal && mapVal) || (outAppKey && mapKey)) this->ProbeMapPullOut(ev, mapKey, mapVal, outAppKey, outAppVal); } mork_test morkProbeMap::find_key_pos(morkEnv* ev, const void* inAppKey, mork_u4 inHash, mork_pos* outPos) const { mork_u1* k = sMap_Keys; // array of keys, each of size sMap_KeySize mork_num size = sMap_KeySize; // number of bytes in each key mork_count slots = sMap_Slots; // total number of key buckets mork_pos i = inHash % slots; // target hash bucket mork_pos startPos = i; // remember start to detect mork_test outTest = this->MapTest(ev, k + (i * size), inAppKey); while (outTest == morkTest_kMiss) { if (++i >= (mork_pos)slots) // advancing goes beyond end? need to wrap around now? i = 0; // wrap around to first slot in map's hash table if (i == startPos) // no void slots were found anywhere in map? { this->WrapWithNoVoidSlotError(ev); // should never happen break; // end loop on kMiss; note caller expects either kVoid or kHit } outTest = this->MapTest(ev, k + (i * size), inAppKey); } *outPos = i; return outTest; } void morkProbeMap::probe_map_lazy_init(morkEnv* ev) { if (this->need_lazy_init() && sMap_Fill == 0) // pending lazy action? { // The constructor cannot successfully call virtual ProbeMapClearKey(), // so we lazily do so now, when we add the first member to the map. mork_u1* keys = sMap_Keys; if (keys) // okay to call lazy virtual clear method on new map keys? { if (sProbeMap_ZeroIsClearKey) // zero is good enough to clear keys? { mork_num keyVolume = sMap_Slots * sMap_KeySize; if (keyVolume) MORK_MEMSET(keys, 0, keyVolume); } else this->ProbeMapClearKey(ev, keys, sMap_Slots); } else this->MapNilKeysError(ev); } sProbeMap_LazyClearOnAdd = 0; // don't do this ever again } mork_bool morkProbeMap::MapAtPut(morkEnv* ev, const void* inAppKey, const void* inAppVal, void* outAppKey, void* outAppVal) { mork_bool outPut = morkBool_kFalse; if (this->GoodProbeMap()) /* looks good? */ { if (this->need_lazy_init() && sMap_Fill == 0) // pending lazy action? this->probe_map_lazy_init(ev); if (ev->Good()) { mork_pos slotPos = 0; mork_u4 hash = this->MapHash(ev, inAppKey); mork_test test = this->find_key_pos(ev, inAppKey, hash, &slotPos); outPut = (test == morkTest_kHit); if (outPut) // replacing an old assoc? no change in member count? { if (outAppKey || outAppVal) /* copy old before cobber? */ this->get_probe_kv(ev, outAppKey, outAppVal, slotPos); } else // adding a new assoc increases membership by one { ++sMap_Fill; /* one more member in the collection */ } if (test != morkTest_kMiss) /* found slot to hold new assoc? */ { ++sMap_Seed; /* note the map has changed */ this->put_probe_kv(ev, inAppKey, inAppVal, slotPos); } } } else this->ProbeMapBadTagError(ev); return outPut; } mork_bool morkProbeMap::MapAt(morkEnv* ev, const void* inAppKey, void* outAppKey, void* outAppVal) { if (this->GoodProbeMap()) /* looks good? */ { if (this->need_lazy_init() && sMap_Fill == 0) // pending lazy action? this->probe_map_lazy_init(ev); mork_pos slotPos = 0; mork_u4 hash = this->MapHash(ev, inAppKey); mork_test test = this->find_key_pos(ev, inAppKey, hash, &slotPos); if (test == morkTest_kHit) /* found an assoc pair for inAppKey? */ { this->get_probe_kv(ev, outAppKey, outAppVal, slotPos); return morkBool_kTrue; } } else this->ProbeMapBadTagError(ev); return morkBool_kFalse; } mork_num morkProbeMap::MapCutAll(morkEnv* ev) { mork_num outCutAll = 0; if (this->GoodProbeMap()) /* looks good? */ { outCutAll = sMap_Fill; /* number of members cut, which is all of them */ if (sMap_Keys && !sProbeMap_ZeroIsClearKey) this->ProbeMapClearKey(ev, sMap_Keys, sMap_Slots); sMap_Fill = 0; /* map now has no members */ } else this->ProbeMapBadTagError(ev); return outCutAll; } // { ===== node interface ===== /*virtual*/ morkProbeMap::~morkProbeMap() // assert NodeStop() finished earlier { MORK_ASSERT(sMap_Keys == 0); MORK_ASSERT(sProbeMap_Tag == 0); } /*public virtual*/ void morkProbeMap::CloseMorkNode( morkEnv* ev) // CloseMap() only if open { if (this->IsOpenNode()) { this->MarkClosing(); this->CloseProbeMap(ev); this->MarkShut(); } } void morkProbeMap::CloseProbeMap(morkEnv* ev) { if (this->IsNode()) { nsIMdbHeap* heap = sMap_Heap; if (heap) // able to free map arrays? { void* block = sMap_Keys; if (block) { heap->Free(ev->AsMdbEnv(), block); sMap_Keys = 0; } block = sMap_Vals; if (block) { heap->Free(ev->AsMdbEnv(), block); sMap_Vals = 0; } } sMap_Keys = 0; sMap_Vals = 0; this->CloseNode(ev); sProbeMap_Tag = 0; sProbeMap_MaxFill = 0; this->MarkShut(); } else this->NonNodeError(ev); } void* morkProbeMap::clear_alloc(morkEnv* ev, mork_size inSize) { void* p = 0; nsIMdbHeap* heap = sMap_Heap; if (heap) { if (NS_SUCCEEDED(heap->Alloc(ev->AsMdbEnv(), inSize, (void**)&p)) && p) { MORK_MEMSET(p, 0, inSize); return p; } } else ev->NilPointerError(); return (void*)0; } /*| map_new_keys: allocate an array of inSlots new keys filled with zero. **| (cf IronDoc's FeHashTable_new_keys()) |*/ mork_u1* morkProbeMap::map_new_keys(morkEnv* ev, mork_num inSlots) { mork_num size = inSlots * sMap_KeySize; return (mork_u1*)this->clear_alloc(ev, size); } /*| map_new_vals: allocate an array of inSlots new values filled with zero. **| When values are zero sized, we just return a null pointer. **| **| (cf IronDoc's FeHashTable_new_values()) |*/ mork_u1* morkProbeMap::map_new_vals(morkEnv* ev, mork_num inSlots) { mork_u1* values = 0; mork_num size = inSlots * sMap_ValSize; if (size) values = (mork_u1*)this->clear_alloc(ev, size); return values; } void morkProbeMap::MapSeedOutOfSyncError(morkEnv* ev) { ev->NewError("sMap_Seed out of sync"); } void morkProbeMap::MapFillUnderflowWarning(morkEnv* ev) { ev->NewWarning("sMap_Fill underflow"); } void morkProbeMap::MapNilKeysError(morkEnv* ev) { ev->NewError("nil sMap_Keys"); } void morkProbeMap::MapZeroKeySizeError(morkEnv* ev) { ev->NewError("zero sMap_KeySize"); } /*static*/ void morkProbeMap::ProbeMapCutError(morkEnv* ev) { ev->NewError("morkProbeMap cannot cut"); } void morkProbeMap::init_probe_map(morkEnv* ev, mork_size inSlots) { // Note we cannot successfully call virtual ProbeMapClearKey() when we // call init_probe_map() inside the constructor; so we leave this problem // to the caller. (The constructor will call ProbeMapClearKey() later // after setting a suitable lazy flag to show this action is pending.) if (ev->Good()) { morkMapScratch old; if (inSlots < 7) // capacity too small? inSlots = 7; // increase to reasonable minimum else if (inSlots > (128 * 1024)) // requested capacity too big? inSlots = (128 * 1024); // decrease to reasonable maximum if (this->new_slots(ev, &old, inSlots)) sProbeMap_Tag = morkProbeMap_kTag; mork_count slots = sMap_Slots; mork_num emptyReserve = (slots / 7) + 1; // keep this many empty sProbeMap_MaxFill = slots - emptyReserve; MORK_MEMSET(&old, 0, sizeof(morkMapScratch)); // don't bother halting } } mork_bool morkProbeMap::new_slots(morkEnv* ev, morkMapScratch* old, mork_num inSlots) { mork_bool outNew = morkBool_kFalse; // Note we cannot successfully call virtual ProbeMapClearKey() when we // call new_slots() inside the constructor; so we leave this problem // to the caller. (The constructor will call ProbeMapClearKey() later // after setting a suitable lazy flag to show this action is pending.) // allocate every new array before we continue: mork_u1* newKeys = this->map_new_keys(ev, inSlots); mork_u1* newVals = this->map_new_vals(ev, inSlots); // okay for newVals to be null when values are zero sized? mork_bool okayValues = (newVals || !sMap_ValSize); if (newKeys && okayValues) { outNew = morkBool_kTrue; // we created every array needed // init mapScratch using slots from current map: old->sMapScratch_Heap = sMap_Heap; old->sMapScratch_Slots = sMap_Slots; old->sMapScratch_Keys = sMap_Keys; old->sMapScratch_Vals = sMap_Vals; // replace all map array slots using the newly allocated members: ++sMap_Seed; // the map has changed sMap_Keys = newKeys; sMap_Vals = newVals; sMap_Slots = inSlots; } else // free any allocations if only partially successful { nsIMdbHeap* heap = sMap_Heap; if (newKeys) heap->Free(ev->AsMdbEnv(), newKeys); if (newVals) heap->Free(ev->AsMdbEnv(), newVals); MORK_MEMSET(old, 0, sizeof(morkMapScratch)); // zap scratch space } return outNew; } void morkProbeMap::clear_probe_map(morkEnv* ev, nsIMdbHeap* ioMapHeap) { sProbeMap_Tag = 0; sMap_Seed = 0; sMap_Slots = 0; sMap_Fill = 0; sMap_Keys = 0; sMap_Vals = 0; sProbeMap_MaxFill = 0; sMap_Heap = ioMapHeap; if (!ioMapHeap) ev->NilPointerError(); } morkProbeMap::morkProbeMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioNodeHeap, mork_size inKeySize, mork_size inValSize, nsIMdbHeap* ioMapHeap, mork_size inSlots, mork_bool inZeroIsClearKey) : morkNode(ev, inUsage, ioNodeHeap), sMap_Heap(ioMapHeap) , sMap_Keys(0), sMap_Vals(0) , sMap_Seed(0) // change count of members or structure , sMap_Slots(0) // count of slots in the hash table , sMap_Fill(0) // number of used slots in the hash table , sMap_KeySize(0) // size of each key (cannot be zero) , sMap_ValSize(0) // size of each val (zero allowed) , sMap_KeyIsIP(morkBool_kFalse) // sMap_KeySize == sizeof(mork_ip) , sMap_ValIsIP(morkBool_kFalse) // sMap_ValSize == sizeof(mork_ip) , sProbeMap_MaxFill(0), sProbeMap_LazyClearOnAdd(0), sProbeMap_ZeroIsClearKey(inZeroIsClearKey), sProbeMap_Tag(0) { // Note we cannot successfully call virtual ProbeMapClearKey() when we // call init_probe_map() inside the constructor; so we leave this problem // to the caller. (The constructor will call ProbeMapClearKey() later // after setting a suitable lazy flag to show this action is pending.) if (ev->Good()) { this->clear_probe_map(ev, ioMapHeap); if (ev->Good()) { sMap_KeySize = inKeySize; sMap_ValSize = inValSize; sMap_KeyIsIP = (inKeySize == sizeof(mork_ip)); sMap_ValIsIP = (inValSize == sizeof(mork_ip)); this->init_probe_map(ev, inSlots); if (ev->Good()) { if (!inZeroIsClearKey) // must lazy clear later with virtual method? sProbeMap_LazyClearOnAdd = morkProbeMap_kLazyClearOnAdd; mNode_Derived = morkDerived_kProbeMap; } } } } /*============================================================================*/ /*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b) morkProbeMap::MapTest(morkEnv* ev, const void* inMapKey, const void* inAppKey) const // Note inMapKey is always a key already stored in the map, while inAppKey // is always a method argument parameter from a client method call. // This matters the most in morkProbeMap subclasses, which have the // responsibility of putting 'app' keys into slots for 'map' keys, and // the bit pattern representation might be different in such cases. // morkTest_kHit means that inMapKey equals inAppKey (and this had better // also imply that hash(inMapKey) == hash(inAppKey)). // morkTest_kMiss means that inMapKey does NOT equal inAppKey (but this // implies nothing at all about hash(inMapKey) and hash(inAppKey)). // morkTest_kVoid means that inMapKey is not a valid key bit pattern, // which means that key slot in the map is not being used. Note that // kVoid is only expected as a return value in morkProbeMap subclasses, // because morkProbeMap must ask whether a key slot is used or not. // morkChainMap however, always knows when a key slot is used, so only // key slots expected to have valid bit patterns will be presented to // the MapTest() methods for morkChainMap subclasses. // // NOTE: it is very important that subclasses correctly return the value // morkTest_kVoid whenever the slot for inMapKey contains a bit pattern // that means the slot is not being used, because this is the only way a // probe map can terminate an unsuccessful search for a key in the map. { mork_size keySize = sMap_KeySize; if (keySize == sizeof(mork_ip) && sMap_KeyIsIP) { mork_ip mapKey = *((const mork_ip*)inMapKey); if (mapKey == *((const mork_ip*)inAppKey)) return morkTest_kHit; else { return (mapKey) ? morkTest_kMiss : morkTest_kVoid; } } else { mork_bool allSame = morkBool_kTrue; mork_bool allZero = morkBool_kTrue; const mork_u1* ak = (const mork_u1*)inAppKey; const mork_u1* mk = (const mork_u1*)inMapKey; const mork_u1* end = mk + keySize; --mk; // prepare for preincrement: while (++mk < end) { mork_u1 byte = *mk; if (byte) // any nonzero byte in map key means slot is not nil? allZero = morkBool_kFalse; if (byte != *ak++) // bytes differ in map and app keys? allSame = morkBool_kFalse; } if (allSame) return morkTest_kHit; else return (allZero) ? morkTest_kVoid : morkTest_kMiss; } } /*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b) morkProbeMap::MapHash(morkEnv* ev, const void* inAppKey) const { mork_size keySize = sMap_KeySize; if (keySize == sizeof(mork_ip) && sMap_KeyIsIP) { return *((const mork_ip*)inAppKey); } else { const mork_u1* key = (const mork_u1*)inAppKey; const mork_u1* end = key + keySize; --key; // prepare for preincrement: while (++key < end) { if (*key) // any nonzero byte in map key means slot is not nil? return morkBool_kFalse; } return morkBool_kTrue; } return (mork_u4)NS_PTR_TO_INT32(inAppKey); } /*============================================================================*/ /*virtual*/ mork_u4 morkProbeMap::ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const // ProbeMapHashMapKey() does logically the same thing as MapHash(), and // the default implementation actually calls virtual MapHash(). However, // Subclasses must override this method whenever the formats of keys in // the map differ from app keys outside the map, because MapHash() only // works on keys in 'app' format, while ProbeMapHashMapKey() only works // on keys in 'map' format. This method is called in order to rehash all // map keys when a map is grown, and this causes all old map members to // move into new slot locations. // // Note it is absolutely imperative that a hash for a key in 'map' format // be exactly the same the hash of the same key in 'app' format, or else // maps will seem corrupt later when keys in 'app' format cannot be found. { return this->MapHash(ev, inMapKey); } /*virtual*/ mork_bool morkProbeMap::ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey) // ProbeMapIsKeyNil() must say whether the representation of logical 'nil' // is currently found inside the key at ioMapKey, for a key found within // the map. The the map iterator uses this method to find map keys that // are actually being used for valid map associations; otherwise the // iterator cannot determine which map slots actually denote used keys. // The default method version returns true if all the bits equal zero. { if (sMap_KeySize == sizeof(mork_ip) && sMap_KeyIsIP) { return !*((const mork_ip*)ioMapKey); } else { const mork_u1* key = (const mork_u1*)ioMapKey; const mork_u1* end = key + sMap_KeySize; --key; // prepare for preincrement: while (++key < end) { if (*key) // any nonzero byte in map key means slot is not nil? return morkBool_kFalse; } return morkBool_kTrue; } } /*virtual*/ void morkProbeMap::ProbeMapClearKey( morkEnv* ev, // put 'nil' into all keys inside map void* ioMapKey, mork_count inKeyCount) // array of keys inside map // ProbeMapClearKey() must put some representation of logical 'nil' into // every key slot in the map, such that MapTest() will later recognize // that this bit pattern shows each key slot is not actually being used. // // This method is typically called whenever the map is either created or // grown into a larger size, where ioMapKey is a pointer to an array of // inKeyCount keys, where each key is this->MapKeySize() bytes in size. // Note that keys are assumed immediately adjacent with no padding, so // if any alignment requirements must be met, then subclasses should have // already accounted for this when specifying a key size in the map. // // Since this method will be called when a map is being grown in size, // nothing should be assumed about the state slots of the map, since the // ioMapKey array might not yet live in sMap_Keys, and the array length // inKeyCount might not yet live in sMap_Slots. However, the value kept // in sMap_KeySize never changes, so this->MapKeySize() is always correct. { if (ioMapKey && inKeyCount) { MORK_MEMSET(ioMapKey, 0, (inKeyCount * sMap_KeySize)); } else ev->NilPointerWarning(); } /*virtual*/ void morkProbeMap::ProbeMapPushIn( morkEnv* ev, // move (key,val) into the map const void* inAppKey, const void* inAppVal, // (key,val) outside map void* outMapKey, void* outMapVal) // (key,val) inside map // This method actually puts keys and vals in the map in suitable format. // // ProbeMapPushIn() must copy a caller key and value in 'app' format // into the map slots provided, which are in 'map' format. When the // 'app' and 'map' formats are identical, then this is just a bitwise // copy of this->MapKeySize() key bytes and this->MapValSize() val bytes, // and this is exactly what the default implementation performs. However, // if 'app' and 'map' formats are different, and MapTest() depends on this // difference in format, then subclasses must override this method to do // whatever is necessary to store the input app key in output map format. // // Do NOT write more than this->MapKeySize() bytes of a map key, or more // than this->MapValSize() bytes of a map val, or corruption might ensue. // // The inAppKey and inAppVal parameters are the same ones passed into a // call to MapAtPut(), and the outMapKey and outMapVal parameters are ones // determined by how the map currently positions key inAppKey in the map. // // Note any key or val parameter can be a null pointer, in which case // this method must do nothing with those parameters. In particular, do // no key move at all when either inAppKey or outMapKey is nil, and do // no val move at all when either inAppVal or outMapVal is nil. Note that // outMapVal should always be nil when this->MapValSize() is nil. {} /*virtual*/ void morkProbeMap::ProbeMapPullOut( morkEnv* ev, // move (key,val) out from the map const void* inMapKey, const void* inMapVal, // (key,val) inside map void* outAppKey, void* outAppVal) const // (key,val) outside map // This method actually gets keys and vals from the map in suitable format. // // ProbeMapPullOut() must copy a key and val in 'map' format into the // caller key and val slots provided, which are in 'app' format. When the // 'app' and 'map' formats are identical, then this is just a bitwise // copy of this->MapKeySize() key bytes and this->MapValSize() val bytes, // and this is exactly what the default implementation performs. However, // if 'app' and 'map' formats are different, and MapTest() depends on this // difference in format, then subclasses must override this method to do // whatever is necessary to store the input map key in output app format. // // The outAppKey and outAppVal parameters are the same ones passed into a // call to either MapAtPut() or MapAt(), while inMapKey and inMapVal are // determined by how the map currently positions the target key in the map. // // Note any key or val parameter can be a null pointer, in which case // this method must do nothing with those parameters. In particular, do // no key move at all when either inMapKey or outAppKey is nil, and do // no val move at all when either inMapVal or outAppVal is nil. Note that // inMapVal should always be nil when this->MapValSize() is nil. {} /*============================================================================*/ /* morkProbeMapIter */ morkProbeMapIter::morkProbeMapIter(morkEnv* ev, morkProbeMap* ioMap) : sProbeMapIter_Map(0), sProbeMapIter_Seed(0), sProbeMapIter_HereIx(morkProbeMapIter_kBeforeIx) { if (ioMap) { if (ioMap->GoodProbeMap()) { if (ioMap->need_lazy_init()) // pending lazy action? ioMap->probe_map_lazy_init(ev); sProbeMapIter_Map = ioMap; sProbeMapIter_Seed = ioMap->sMap_Seed; } else ioMap->ProbeMapBadTagError(ev); } else ev->NilPointerError(); } void morkProbeMapIter::CloseMapIter(morkEnv* ev) { MORK_USED_1(ev); sProbeMapIter_Map = 0; sProbeMapIter_Seed = 0; sProbeMapIter_HereIx = morkProbeMapIter_kAfterIx; } morkProbeMapIter::morkProbeMapIter() // zero most slots; caller must call InitProbeMapIter() { sProbeMapIter_Map = 0; sProbeMapIter_Seed = 0; sProbeMapIter_HereIx = morkProbeMapIter_kBeforeIx; } void morkProbeMapIter::InitProbeMapIter(morkEnv* ev, morkProbeMap* ioMap) { sProbeMapIter_Map = 0; sProbeMapIter_Seed = 0; sProbeMapIter_HereIx = morkProbeMapIter_kBeforeIx; if (ioMap) { if (ioMap->GoodProbeMap()) { if (ioMap->need_lazy_init()) // pending lazy action? ioMap->probe_map_lazy_init(ev); sProbeMapIter_Map = ioMap; sProbeMapIter_Seed = ioMap->sMap_Seed; } else ioMap->ProbeMapBadTagError(ev); } else ev->NilPointerError(); } mork_bool morkProbeMapIter::IterFirst(morkEnv* ev, void* outAppKey, void* outAppVal) { sProbeMapIter_HereIx = morkProbeMapIter_kAfterIx; // default to done morkProbeMap* map = sProbeMapIter_Map; if (map && map->GoodProbeMap()) /* looks good? */ { sProbeMapIter_Seed = map->sMap_Seed; /* sync the seeds */ mork_u1* k = map->sMap_Keys; // array of keys, each of size sMap_KeySize mork_num size = map->sMap_KeySize; // number of bytes in each key mork_count slots = map->sMap_Slots; // total number of key buckets mork_pos here = 0; // first hash bucket while (here < (mork_pos)slots) { if (!map->ProbeMapIsKeyNil(ev, k + (here * size))) { map->get_probe_kv(ev, outAppKey, outAppVal, here); sProbeMapIter_HereIx = (mork_i4)here; return morkBool_kTrue; } ++here; // next bucket } } else map->ProbeMapBadTagError(ev); return morkBool_kFalse; } mork_bool morkProbeMapIter::IterNext(morkEnv* ev, void* outAppKey, void* outAppVal) { morkProbeMap* map = sProbeMapIter_Map; if (map && map->GoodProbeMap()) /* looks good? */ { if (sProbeMapIter_Seed == map->sMap_Seed) /* in sync? */ { if (sProbeMapIter_HereIx != morkProbeMapIter_kAfterIx) { mork_pos here = (mork_pos)sProbeMapIter_HereIx; if (sProbeMapIter_HereIx < 0) here = 0; else ++here; sProbeMapIter_HereIx = morkProbeMapIter_kAfterIx; // default to done mork_u1* k = map->sMap_Keys; // key array, each of size sMap_KeySize mork_num size = map->sMap_KeySize; // number of bytes in each key mork_count slots = map->sMap_Slots; // total number of key buckets while (here < (mork_pos)slots) { if (!map->ProbeMapIsKeyNil(ev, k + (here * size))) { map->get_probe_kv(ev, outAppKey, outAppVal, here); sProbeMapIter_HereIx = (mork_i4)here; return morkBool_kTrue; } ++here; // next bucket } } } else map->MapSeedOutOfSyncError(ev); } else map->ProbeMapBadTagError(ev); return morkBool_kFalse; } mork_bool morkProbeMapIter::IterHere(morkEnv* ev, void* outAppKey, void* outAppVal) { morkProbeMap* map = sProbeMapIter_Map; if (map && map->GoodProbeMap()) /* looks good? */ { if (sProbeMapIter_Seed == map->sMap_Seed) /* in sync? */ { mork_pos here = (mork_pos)sProbeMapIter_HereIx; mork_count slots = map->sMap_Slots; // total number of key buckets if (sProbeMapIter_HereIx >= 0 && (here < (mork_pos)slots)) { mork_u1* k = map->sMap_Keys; // key array, each of size sMap_KeySize mork_num size = map->sMap_KeySize; // number of bytes in each key if (!map->ProbeMapIsKeyNil(ev, k + (here * size))) { map->get_probe_kv(ev, outAppKey, outAppVal, here); return morkBool_kTrue; } } } else map->MapSeedOutOfSyncError(ev); } else map->ProbeMapBadTagError(ev); return morkBool_kFalse; } mork_change* morkProbeMapIter::First(morkEnv* ev, void* outKey, void* outVal) { if (this->IterFirst(ev, outKey, outVal)) return &sProbeMapIter_Change; return (mork_change*)0; } mork_change* morkProbeMapIter::Next(morkEnv* ev, void* outKey, void* outVal) { if (this->IterNext(ev, outKey, outVal)) return &sProbeMapIter_Change; return (mork_change*)0; } mork_change* morkProbeMapIter::Here(morkEnv* ev, void* outKey, void* outVal) { if (this->IterHere(ev, outKey, outVal)) return &sProbeMapIter_Change; return (mork_change*)0; } mork_change* morkProbeMapIter::CutHere(morkEnv* ev, void* outKey, void* outVal) { morkProbeMap::ProbeMapCutError(ev); return (mork_change*)0; } // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 // NOTE: the following methods ONLY work for sMap_ValIsIP pointer values. // (Note the implied assumption that zero is never a good value pattern.) void* morkProbeMapIter::IterFirstVal(morkEnv* ev, void* outKey) // equivalent to { void* v=0; this->IterFirst(ev, outKey, &v); return v; } { morkProbeMap* map = sProbeMapIter_Map; if (map) { if (map->sMap_ValIsIP) { void* v = 0; this->IterFirst(ev, outKey, &v); return v; } else map->MapValIsNotIPError(ev); } return (void*)0; } void* morkProbeMapIter::IterNextVal(morkEnv* ev, void* outKey) // equivalent to { void* v=0; this->IterNext(ev, outKey, &v); return v; } { morkProbeMap* map = sProbeMapIter_Map; if (map) { if (map->sMap_ValIsIP) { void* v = 0; this->IterNext(ev, outKey, &v); return v; } else map->MapValIsNotIPError(ev); } return (void*)0; } void* morkProbeMapIter::IterHereVal(morkEnv* ev, void* outKey) // equivalent to { void* v=0; this->IterHere(ev, outKey, &v); return v; } { morkProbeMap* map = sProbeMapIter_Map; if (map) { if (map->sMap_ValIsIP) { void* v = 0; this->IterHere(ev, outKey, &v); return v; } else map->MapValIsNotIPError(ev); } return (void*)0; } // NOTE: the following methods ONLY work for sMap_KeyIsIP pointer values. // (Note the implied assumption that zero is never a good key pattern.) void* morkProbeMapIter::IterFirstKey(morkEnv* ev) // equivalent to { void* k=0; this->IterFirst(ev, &k, 0); return k; } { morkProbeMap* map = sProbeMapIter_Map; if (map) { if (map->sMap_KeyIsIP) { void* k = 0; this->IterFirst(ev, &k, (void*)0); return k; } else map->MapKeyIsNotIPError(ev); } return (void*)0; } void* morkProbeMapIter::IterNextKey(morkEnv* ev) // equivalent to { void* k=0; this->IterNext(ev, &k, 0); return k; } { morkProbeMap* map = sProbeMapIter_Map; if (map) { if (map->sMap_KeyIsIP) { void* k = 0; this->IterNext(ev, &k, (void*)0); return k; } else map->MapKeyIsNotIPError(ev); } return (void*)0; } void* morkProbeMapIter::IterHereKey(morkEnv* ev) // equivalent to { void* k=0; this->IterHere(ev, &k, 0); return k; } { morkProbeMap* map = sProbeMapIter_Map; if (map) { if (map->sMap_KeyIsIP) { void* k = 0; this->IterHere(ev, &k, (void*)0); return k; } else map->MapKeyIsNotIPError(ev); } return (void*)0; } // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789