/* -*- 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 mostly a port to C++ from public domain IronDoc C sources. // Note many code comments here come verbatim from cut-and-pasted IronDoc. #ifndef _MDB_ # include "mdb.h" #endif #ifndef _MORK_ # include "mork.h" #endif #ifndef _MORKNODE_ # include "morkNode.h" #endif #ifndef _MORKMAP_ # include "morkMap.h" #endif #ifndef _MORKENV_ # include "morkEnv.h" #endif class morkHashArrays { public: nsIMdbHeap* mHashArrays_Heap; // copy of mMap_Heap mork_count mHashArrays_Slots; // copy of mMap_Slots mork_u1* mHashArrays_Keys; // copy of mMap_Keys mork_u1* mHashArrays_Vals; // copy of mMap_Vals morkAssoc* mHashArrays_Assocs; // copy of mMap_Assocs mork_change* mHashArrays_Changes; // copy of mMap_Changes morkAssoc** mHashArrays_Buckets; // copy of mMap_Buckets morkAssoc* mHashArrays_FreeList; // copy of mMap_FreeList public: void finalize(morkEnv* ev); }; void morkHashArrays::finalize(morkEnv* ev) { nsIMdbEnv* menv = ev->AsMdbEnv(); nsIMdbHeap* heap = mHashArrays_Heap; if (heap) { if (mHashArrays_Keys) heap->Free(menv, mHashArrays_Keys); if (mHashArrays_Vals) heap->Free(menv, mHashArrays_Vals); if (mHashArrays_Assocs) heap->Free(menv, mHashArrays_Assocs); if (mHashArrays_Changes) heap->Free(menv, mHashArrays_Changes); if (mHashArrays_Buckets) heap->Free(menv, mHashArrays_Buckets); } } // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 // ````` ````` ````` ````` ````` // { ===== begin morkNode interface ===== /*public virtual*/ void morkMap::CloseMorkNode( morkEnv* ev) // CloseMap() only if open { if (this->IsOpenNode()) { this->MarkClosing(); this->CloseMap(ev); this->MarkShut(); } } /*public virtual*/ morkMap::~morkMap() // assert CloseMap() executed earlier { MORK_ASSERT(mMap_FreeList == 0); MORK_ASSERT(mMap_Buckets == 0); MORK_ASSERT(mMap_Keys == 0); MORK_ASSERT(mMap_Vals == 0); MORK_ASSERT(mMap_Changes == 0); MORK_ASSERT(mMap_Assocs == 0); } /*public non-poly*/ void morkMap::CloseMap( morkEnv* ev) // called by CloseMorkNode(); { if (this->IsNode()) { nsIMdbHeap* heap = mMap_Heap; if (heap) /* need to free the arrays? */ { nsIMdbEnv* menv = ev->AsMdbEnv(); if (mMap_Keys) heap->Free(menv, mMap_Keys); if (mMap_Vals) heap->Free(menv, mMap_Vals); if (mMap_Assocs) heap->Free(menv, mMap_Assocs); if (mMap_Buckets) heap->Free(menv, mMap_Buckets); if (mMap_Changes) heap->Free(menv, mMap_Changes); } mMap_Keys = 0; mMap_Vals = 0; mMap_Buckets = 0; mMap_Assocs = 0; mMap_Changes = 0; mMap_FreeList = 0; MORK_MEMSET(&mMap_Form, 0, sizeof(morkMapForm)); this->MarkShut(); } else this->NonNodeError(ev); } // } ===== end morkNode methods ===== // ````` ````` ````` ````` ````` void morkMap::clear_map(morkEnv* ev, nsIMdbHeap* ioSlotHeap) { mMap_Tag = 0; mMap_Seed = 0; mMap_Slots = 0; mMap_Fill = 0; mMap_Keys = 0; mMap_Vals = 0; mMap_Assocs = 0; mMap_Changes = 0; mMap_Buckets = 0; mMap_FreeList = 0; MORK_MEMSET(&mMap_Form, 0, sizeof(morkMapForm)); mMap_Heap = 0; if (ioSlotHeap) { nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mMap_Heap); } else ev->NilPointerError(); } morkMap::morkMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, mork_size inKeySize, mork_size inValSize, mork_size inSlots, nsIMdbHeap* ioSlotHeap, mork_bool inHoldChanges) : morkNode(ev, inUsage, ioHeap), mMap_Heap(0) { if (ev->Good()) { this->clear_map(ev, ioSlotHeap); if (ev->Good()) { mMap_Form.mMapForm_HoldChanges = inHoldChanges; mMap_Form.mMapForm_KeySize = inKeySize; mMap_Form.mMapForm_ValSize = inValSize; mMap_Form.mMapForm_KeyIsIP = (inKeySize == sizeof(mork_ip)); mMap_Form.mMapForm_ValIsIP = (inValSize == sizeof(mork_ip)); this->InitMap(ev, inSlots); if (ev->Good()) mNode_Derived = morkDerived_kMap; } } } void morkMap::NewIterOutOfSyncError(morkEnv* ev) { ev->NewError("map iter out of sync"); } void morkMap::NewBadMapError(morkEnv* ev) { ev->NewError("bad morkMap tag"); } void morkMap::NewSlotsUnderflowWarning(morkEnv* ev) { ev->NewWarning("member count underflow"); } void morkMap::InitMap(morkEnv* ev, mork_size inSlots) { if (ev->Good()) { morkHashArrays old; // MORK_MEMCPY(&mMap_Form, &inForm, sizeof(morkMapForm)); if (inSlots < 3) /* requested capacity absurdly small? */ inSlots = 3; /* bump it up to a minimum practical level */ else if (inSlots > (128 * 1024)) /* requested slots absurdly big? */ inSlots = (128 * 1024); /* decrease it to a maximum practical level */ if (this->new_arrays(ev, &old, inSlots)) mMap_Tag = morkMap_kTag; MORK_MEMSET(&old, 0, sizeof(morkHashArrays)); // do NOT finalize } } morkAssoc** morkMap::find(morkEnv* ev, const void* inKey, mork_u4 inHash) const { mork_u1* keys = mMap_Keys; mork_num keySize = this->FormKeySize(); // morkMap_mEqual equal = this->FormEqual(); morkAssoc** ref = mMap_Buckets + (inHash % mMap_Slots); morkAssoc* assoc = *ref; while (assoc) /* look at another assoc in the bucket? */ { mork_pos i = assoc - mMap_Assocs; /* index of this assoc */ if (this->Equal(ev, keys + (i * keySize), inKey)) /* found? */ return ref; ref = &assoc->mAssoc_Next; /* consider next assoc slot in bucket */ assoc = *ref; /* if this is null, then we are done */ } return 0; } /*| get_assoc: read the key and/or value at index inPos |*/ void morkMap::get_assoc(void* outKey, void* outVal, mork_pos inPos) const { mork_num valSize = this->FormValSize(); if (valSize && outVal) /* map holds values? caller wants the value? */ { const mork_u1* value = mMap_Vals + (valSize * inPos); if (valSize == sizeof(mork_ip) && this->FormValIsIP()) /* ip case? */ *((mork_ip*)outVal) = *((const mork_ip*)value); else MORK_MEMCPY(outVal, value, valSize); } if (outKey) /* caller wants the key? */ { mork_num keySize = this->FormKeySize(); const mork_u1* key = mMap_Keys + (keySize * inPos); if (keySize == sizeof(mork_ip) && this->FormKeyIsIP()) /* ip case? */ *((mork_ip*)outKey) = *((const mork_ip*)key); else MORK_MEMCPY(outKey, key, keySize); } } /*| put_assoc: write the key and/or value at index inPos |*/ void morkMap::put_assoc(const void* inKey, const void* inVal, mork_pos inPos) const { mork_num valSize = this->FormValSize(); if (valSize && inVal) /* map holds values? caller sends a value? */ { mork_u1* value = mMap_Vals + (valSize * inPos); if (valSize == sizeof(mork_ip) && this->FormValIsIP()) /* ip case? */ *((mork_ip*)value) = *((const mork_ip*)inVal); else MORK_MEMCPY(value, inVal, valSize); } if (inKey) /* caller sends a key? */ { mork_num keySize = this->FormKeySize(); mork_u1* key = mMap_Keys + (keySize * inPos); if (keySize == sizeof(mork_ip) && this->FormKeyIsIP()) /* ip case? */ *((mork_ip*)key) = *((const mork_ip*)inKey); else MORK_MEMCPY(key, inKey, keySize); } } void* morkMap::clear_alloc(morkEnv* ev, mork_size inSize) { void* p = 0; nsIMdbHeap* heap = mMap_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; } void* morkMap::alloc(morkEnv* ev, mork_size inSize) { void* p = 0; nsIMdbHeap* heap = mMap_Heap; if (heap) { if (NS_SUCCEEDED(heap->Alloc(ev->AsMdbEnv(), inSize, (void**)&p)) && p) return p; } else ev->NilPointerError(); return (void*)0; } /*| new_keys: allocate an array of inSlots new keys filled with zero. |*/ mork_u1* morkMap::new_keys(morkEnv* ev, mork_num inSlots) { mork_num size = inSlots * this->FormKeySize(); return (mork_u1*)this->clear_alloc(ev, size); } /*| new_values: allocate an array of inSlots new values filled with zero. **| When values are zero sized, we just return a null pointer. |*/ mork_u1* morkMap::new_values(morkEnv* ev, mork_num inSlots) { mork_u1* values = 0; mork_num size = inSlots * this->FormValSize(); if (size) values = (mork_u1*)this->clear_alloc(ev, size); return values; } mork_change* morkMap::new_changes(morkEnv* ev, mork_num inSlots) { mork_change* changes = 0; mork_num size = inSlots * sizeof(mork_change); if (size && mMap_Form.mMapForm_HoldChanges) changes = (mork_change*)this->clear_alloc(ev, size); return changes; } /*| new_buckets: allocate an array of inSlots new buckets filled with zero. |*/ morkAssoc** morkMap::new_buckets(morkEnv* ev, mork_num inSlots) { mork_num size = inSlots * sizeof(morkAssoc*); return (morkAssoc**)this->clear_alloc(ev, size); } /*| new_assocs: allocate an array of inSlots new assocs, with each assoc **| linked together in a list with the first array element at the list head **| and the last element at the list tail. (morkMap::grow() needs this.) |*/ morkAssoc* morkMap::new_assocs(morkEnv* ev, mork_num inSlots) { mork_num size = inSlots * sizeof(morkAssoc); morkAssoc* assocs = (morkAssoc*)this->alloc(ev, size); if (assocs) /* able to allocate the array? */ { morkAssoc* a = assocs + (inSlots - 1); /* the last array element */ a->mAssoc_Next = 0; /* terminate tail element of the list with null */ while (--a >= assocs) /* another assoc to link into the list? */ a->mAssoc_Next = a + 1; /* each points to the following assoc */ } return assocs; } mork_bool morkMap::new_arrays(morkEnv* ev, morkHashArrays* old, mork_num inSlots) { mork_bool outNew = morkBool_kFalse; /* see if we can allocate all the new arrays before we go any further: */ morkAssoc** newBuckets = this->new_buckets(ev, inSlots); morkAssoc* newAssocs = this->new_assocs(ev, inSlots); mork_u1* newKeys = this->new_keys(ev, inSlots); mork_u1* newValues = this->new_values(ev, inSlots); mork_change* newChanges = this->new_changes(ev, inSlots); /* it is okay for newChanges to be null when changes are not held: */ mork_bool okayChanges = (newChanges || !this->FormHoldChanges()); /* it is okay for newValues to be null when values are zero sized: */ mork_bool okayValues = (newValues || !this->FormValSize()); if (newBuckets && newAssocs && newKeys && okayChanges && okayValues) { outNew = morkBool_kTrue; /* yes, we created all the arrays we need */ /* init the old hashArrays with slots from this hash table: */ old->mHashArrays_Heap = mMap_Heap; old->mHashArrays_Slots = mMap_Slots; old->mHashArrays_Keys = mMap_Keys; old->mHashArrays_Vals = mMap_Vals; old->mHashArrays_Assocs = mMap_Assocs; old->mHashArrays_Buckets = mMap_Buckets; old->mHashArrays_Changes = mMap_Changes; /* now replace all our array slots with the new structures: */ ++mMap_Seed; /* note the map is now changed */ mMap_Keys = newKeys; mMap_Vals = newValues; mMap_Buckets = newBuckets; mMap_Assocs = newAssocs; mMap_FreeList = newAssocs; /* all are free to start with */ mMap_Changes = newChanges; mMap_Slots = inSlots; } else /* free the partial set of arrays that were actually allocated */ { nsIMdbEnv* menv = ev->AsMdbEnv(); nsIMdbHeap* heap = mMap_Heap; if (newBuckets) heap->Free(menv, newBuckets); if (newAssocs) heap->Free(menv, newAssocs); if (newKeys) heap->Free(menv, newKeys); if (newValues) heap->Free(menv, newValues); if (newChanges) heap->Free(menv, newChanges); MORK_MEMSET(old, 0, sizeof(morkHashArrays)); } return outNew; } /*| grow: make the map arrays bigger by 33%. The old map is completely **| full, or else we would not have called grow() to get more space. This **| means the free list is empty, and also means every old key and value is in **| use in the old arrays. So every key and value must be copied to the new **| arrays, and since they are contiguous in the old arrays, we can efficiently **| bitwise copy them in bulk from the old arrays to the new arrays, without **| paying any attention to the structure of the old arrays. **| **|| The content of the old bucket and assoc arrays need not be copied because **| this information is going to be completely rebuilt by rehashing all the **| keys into their new buckets, given the new larger map capacity. The new **| bucket array from new_arrays() is assumed to contain all zeroes, which is **| necessary to ensure the lists in each bucket stay null terminated as we **| build the new linked lists. (Note no old bucket ordering is preserved.) **| **|| If the old capacity is N, then in the new arrays the assocs with indexes **| from zero to N-1 are still allocated and must be rehashed into the map. **| The new free list contains all the following assocs, starting with the new **| assoc link at index N. (We call the links in the link array "assocs" **| because allocating a link is the same as allocating the key/value pair **| with the same index as the link.) **| **|| The new free list is initialized simply by pointing at the first new link **| in the assoc array after the size of the old assoc array. This assumes **| that FeHashTable_new_arrays() has already linked all the new assocs into a **| list with the first at the head of the list and the last at the tail of the **| list. So by making the free list point to the first of the new uncopied **| assocs, the list is already well-formed. |*/ mork_bool morkMap::grow(morkEnv* ev) { if (mMap_Heap) /* can we grow the map? */ { mork_num newSlots = (mMap_Slots * 2); /* +100% */ morkHashArrays old; /* a place to temporarily hold all the old arrays */ if (this->new_arrays(ev, &old, newSlots)) /* have more? */ { // morkMap_mHash hash = this->FormHash(); /* for terse loop use */ /* figure out the bulk volume sizes of old keys and values to move: */ mork_num oldSlots = old.mHashArrays_Slots; /* number of old assocs */ mork_num keyBulk = oldSlots * this->FormKeySize(); /* key volume */ mork_num valBulk = oldSlots * this->FormValSize(); /* values */ /* convenient variables for new arrays that need to be rehashed: */ morkAssoc** newBuckets = mMap_Buckets; /* new all zeroes */ morkAssoc* newAssocs = mMap_Assocs; /* hash into buckets */ morkAssoc* newFreeList = newAssocs + oldSlots; /* new room is free */ mork_u1* key = mMap_Keys; /* the first key to rehash */ --newAssocs; /* back up one before preincrement used in while loop */ /* move all old keys and values to the new arrays: */ MORK_MEMCPY(mMap_Keys, old.mHashArrays_Keys, keyBulk); if (valBulk) /* are values nonzero sized? */ MORK_MEMCPY(mMap_Vals, old.mHashArrays_Vals, valBulk); mMap_FreeList = newFreeList; /* remaining assocs are free */ while (++newAssocs < newFreeList) /* rehash another old assoc? */ { morkAssoc** top = newBuckets + (this->Hash(ev, key) % newSlots); key += this->FormKeySize(); /* the next key to rehash */ newAssocs->mAssoc_Next = *top; /* link to prev bucket top */ *top = newAssocs; /* push assoc on top of bucket */ } ++mMap_Seed; /* note the map has changed */ old.finalize(ev); /* remember to free the old arrays */ } } else ev->OutOfMemoryError(); return ev->Good(); } mork_bool morkMap::Put(morkEnv* ev, const void* inKey, const void* inVal, void* outKey, void* outVal, mork_change** outChange) { mork_bool outPut = morkBool_kFalse; if (this->GoodMap()) /* looks good? */ { mork_u4 hash = this->Hash(ev, inKey); morkAssoc** ref = this->find(ev, inKey, hash); if (ref) /* assoc was found? reuse an existing assoc slot? */ { outPut = morkBool_kTrue; /* inKey was indeed already inside the map */ } else /* assoc not found -- need to allocate a new assoc slot */ { morkAssoc* assoc = this->pop_free_assoc(); if (!assoc) /* no slots remaining in free list? must grow map? */ { if (this->grow(ev)) /* successfully made map larger? */ assoc = this->pop_free_assoc(); } if (assoc) /* allocated new assoc without error? */ { ref = mMap_Buckets + (hash % mMap_Slots); assoc->mAssoc_Next = *ref; /* link to prev bucket top */ *ref = assoc; /* push assoc on top of bucket */ ++mMap_Fill; /* one more member in the collection */ ++mMap_Seed; /* note the map has changed */ } } if (ref) /* did not have an error during possible growth? */ { mork_pos i = (*ref) - mMap_Assocs; /* index of assoc */ if (outPut && (outKey || outVal)) /* copy old before cobbering? */ this->get_assoc(outKey, outVal, i); this->put_assoc(inKey, inVal, i); ++mMap_Seed; /* note the map has changed */ if (outChange) { if (mMap_Changes) *outChange = mMap_Changes + i; else *outChange = this->FormDummyChange(); } } } else this->NewBadMapError(ev); return outPut; } mork_num morkMap::CutAll(morkEnv* ev) { mork_num outCutAll = 0; if (this->GoodMap()) /* map looks good? */ { mork_num slots = mMap_Slots; morkAssoc* before = mMap_Assocs - 1; /* before first member */ morkAssoc* assoc = before + slots; /* the very last member */ ++mMap_Seed; /* note the map is changed */ /* make the assoc array a linked list headed by first & tailed by last: */ assoc->mAssoc_Next = 0; /* null terminate the new free list */ while (--assoc > before) /* another assoc to link into the list? */ assoc->mAssoc_Next = assoc + 1; mMap_FreeList = mMap_Assocs; /* all are free */ outCutAll = mMap_Fill; /* we'll cut all of them of course */ mMap_Fill = 0; /* the map is completely empty */ } else this->NewBadMapError(ev); return outCutAll; } mork_bool morkMap::Cut(morkEnv* ev, const void* inKey, void* outKey, void* outVal, mork_change** outChange) { mork_bool outCut = morkBool_kFalse; if (this->GoodMap()) /* looks good? */ { mork_u4 hash = this->Hash(ev, inKey); morkAssoc** ref = this->find(ev, inKey, hash); if (ref) /* found an assoc for key? */ { outCut = morkBool_kTrue; morkAssoc* assoc = *ref; mork_pos i = assoc - mMap_Assocs; /* index of assoc */ if (outKey || outVal) this->get_assoc(outKey, outVal, i); *ref = assoc->mAssoc_Next; /* unlink the found assoc */ this->push_free_assoc(assoc); /* and put it in free list */ if (outChange) { if (mMap_Changes) *outChange = mMap_Changes + i; else *outChange = this->FormDummyChange(); } ++mMap_Seed; /* note the map has changed */ if (mMap_Fill) /* the count shows nonzero members? */ --mMap_Fill; /* one less member in the collection */ else this->NewSlotsUnderflowWarning(ev); } } else this->NewBadMapError(ev); return outCut; } mork_bool morkMap::Get(morkEnv* ev, const void* inKey, void* outKey, void* outVal, mork_change** outChange) { mork_bool outGet = morkBool_kFalse; if (this->GoodMap()) /* looks good? */ { mork_u4 hash = this->Hash(ev, inKey); morkAssoc** ref = this->find(ev, inKey, hash); if (ref) /* found an assoc for inKey? */ { mork_pos i = (*ref) - mMap_Assocs; /* index of assoc */ outGet = morkBool_kTrue; this->get_assoc(outKey, outVal, i); if (outChange) { if (mMap_Changes) *outChange = mMap_Changes + i; else *outChange = this->FormDummyChange(); } } } else this->NewBadMapError(ev); return outGet; } morkMapIter::morkMapIter() : mMapIter_Map(0), mMapIter_Seed(0) , mMapIter_Bucket(0), mMapIter_AssocRef(0), mMapIter_Assoc(0), mMapIter_Next(0) {} void morkMapIter::InitMapIter(morkEnv* ev, morkMap* ioMap) { mMapIter_Map = 0; mMapIter_Seed = 0; mMapIter_Bucket = 0; mMapIter_AssocRef = 0; mMapIter_Assoc = 0; mMapIter_Next = 0; if (ioMap) { if (ioMap->GoodMap()) { mMapIter_Map = ioMap; mMapIter_Seed = ioMap->mMap_Seed; } else ioMap->NewBadMapError(ev); } else ev->NilPointerError(); } morkMapIter::morkMapIter(morkEnv* ev, morkMap* ioMap) : mMapIter_Map(0), mMapIter_Seed(0) , mMapIter_Bucket(0), mMapIter_AssocRef(0), mMapIter_Assoc(0), mMapIter_Next(0) { if (ioMap) { if (ioMap->GoodMap()) { mMapIter_Map = ioMap; mMapIter_Seed = ioMap->mMap_Seed; } else ioMap->NewBadMapError(ev); } else ev->NilPointerError(); } void morkMapIter::CloseMapIter(morkEnv* ev) { MORK_USED_1(ev); mMapIter_Map = 0; mMapIter_Seed = 0; mMapIter_Bucket = 0; mMapIter_AssocRef = 0; mMapIter_Assoc = 0; mMapIter_Next = 0; } mork_change* morkMapIter::First(morkEnv* ev, void* outKey, void* outVal) { mork_change* outFirst = 0; morkMap* map = mMapIter_Map; if (map && map->GoodMap()) /* map looks good? */ { morkAssoc** bucket = map->mMap_Buckets; morkAssoc** end = bucket + map->mMap_Slots; /* one past last */ mMapIter_Seed = map->mMap_Seed; /* sync the seeds */ while (bucket < end) /* another bucket in which to look for assocs? */ { morkAssoc* assoc = *bucket++; if (assoc) /* found the first map assoc in use? */ { mork_pos i = assoc - map->mMap_Assocs; mork_change* c = map->mMap_Changes; outFirst = (c) ? (c + i) : map->FormDummyChange(); mMapIter_Assoc = assoc; /* current assoc in iteration */ mMapIter_Next = assoc->mAssoc_Next; /* more in bucket */ mMapIter_Bucket = --bucket; /* bucket for this assoc */ mMapIter_AssocRef = bucket; /* slot referencing assoc */ map->get_assoc(outKey, outVal, i); break; /* end while loop */ } } } else map->NewBadMapError(ev); return outFirst; } mork_change* morkMapIter::Next(morkEnv* ev, void* outKey, void* outVal) { mork_change* outNext = 0; morkMap* map = mMapIter_Map; if (map && map->GoodMap()) /* map looks good? */ { if (mMapIter_Seed == map->mMap_Seed) /* in sync? */ { morkAssoc* here = mMapIter_Assoc; /* current assoc */ if (here) /* iteration is not yet concluded? */ { morkAssoc* next = mMapIter_Next; morkAssoc* assoc = next; /* default new mMapIter_Assoc */ if (next) /* there are more assocs in the same bucket after Here? */ { morkAssoc** ref = mMapIter_AssocRef; /* (*HereRef) equals Here, except when Here has been cut, after ** which (*HereRef) always equals Next. So if (*HereRef) is not ** equal to Next, then HereRef still needs to be updated to point ** somewhere else other than Here. Otherwise it is fine. */ if (*ref != next) /* Here was not cut? must update HereRef? */ mMapIter_AssocRef = &here->mAssoc_Next; mMapIter_Next = next->mAssoc_Next; } else /* look for the next assoc in the next nonempty bucket */ { morkAssoc** bucket = map->mMap_Buckets; morkAssoc** end = bucket + map->mMap_Slots; /* beyond */ mMapIter_Assoc = 0; /* default to no more assocs */ bucket = mMapIter_Bucket; /* last exhausted bucket */ mMapIter_Assoc = 0; /* default to iteration ended */ while (++bucket < end) /* another bucket to search for assocs? */ { assoc = *bucket; if (assoc) /* found another map assoc in use? */ { mMapIter_Bucket = bucket; mMapIter_AssocRef = bucket; /* ref to assoc */ mMapIter_Next = assoc->mAssoc_Next; /* more */ break; /* end while loop */ } } } if (assoc) /* did we find another assoc in the iteration? */ { mMapIter_Assoc = assoc; /* current assoc */ mork_pos i = assoc - map->mMap_Assocs; mork_change* c = map->mMap_Changes; outNext = (c) ? (c + i) : map->FormDummyChange(); map->get_assoc(outKey, outVal, i); } } } else map->NewIterOutOfSyncError(ev); } else map->NewBadMapError(ev); return outNext; } mork_change* morkMapIter::Here(morkEnv* ev, void* outKey, void* outVal) { mork_change* outHere = 0; morkMap* map = mMapIter_Map; if (map && map->GoodMap()) /* map looks good? */ { if (mMapIter_Seed == map->mMap_Seed) /* in sync? */ { morkAssoc* here = mMapIter_Assoc; /* current assoc */ if (here) /* iteration is not yet concluded? */ { mork_pos i = here - map->mMap_Assocs; mork_change* c = map->mMap_Changes; outHere = (c) ? (c + i) : map->FormDummyChange(); map->get_assoc(outKey, outVal, i); } } else map->NewIterOutOfSyncError(ev); } else map->NewBadMapError(ev); return outHere; } mork_change* morkMapIter::CutHere(morkEnv* ev, void* outKey, void* outVal) { mork_change* outCutHere = 0; morkMap* map = mMapIter_Map; if (map && map->GoodMap()) /* map looks good? */ { if (mMapIter_Seed == map->mMap_Seed) /* in sync? */ { morkAssoc* here = mMapIter_Assoc; /* current assoc */ if (here) /* iteration is not yet concluded? */ { morkAssoc** ref = mMapIter_AssocRef; if (*ref != mMapIter_Next) /* not already cut? */ { mork_pos i = here - map->mMap_Assocs; mork_change* c = map->mMap_Changes; outCutHere = (c) ? (c + i) : map->FormDummyChange(); if (outKey || outVal) map->get_assoc(outKey, outVal, i); map->push_free_assoc(here); /* add to free list */ *ref = mMapIter_Next; /* unlink here from bucket list */ /* note the map has changed, but we are still in sync: */ mMapIter_Seed = ++map->mMap_Seed; /* sync */ if (map->mMap_Fill) /* still has nonzero value? */ --map->mMap_Fill; /* one less member in the collection */ else map->NewSlotsUnderflowWarning(ev); } } } else map->NewIterOutOfSyncError(ev); } else map->NewBadMapError(ev); return outCutHere; } // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789