1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* ***** BEGIN LICENSE BLOCK ***** 3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 4 * 5 * The contents of this file are subject to the Mozilla Public License Version 6 * 1.1 (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * http://www.mozilla.org/MPL/ 9 * 10 * Software distributed under the License is distributed on an "AS IS" basis, 11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 12 * for the specific language governing rights and limitations under the 13 * License. 14 * 15 * The Original Code is mozilla.org code. 16 * 17 * The Initial Developer of the Original Code is 18 * Netscape Communications Corporation. 19 * Portions created by the Initial Developer are Copyright (C) 1999 20 * the Initial Developer. All Rights Reserved. 21 * 22 * Contributor(s): 23 * 24 * Alternatively, the contents of this file may be used under the terms of 25 * either of the GNU General Public License Version 2 or later (the "GPL"), 26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 27 * in which case the provisions of the GPL or the LGPL are applicable instead 28 * of those above. If you wish to allow use of your version of this file only 29 * under the terms of either the GPL or the LGPL, and not to allow others to 30 * use your version of this file under the terms of the MPL, indicate your 31 * decision by deleting the provisions above and replace them with the notice 32 * and other provisions required by the GPL or the LGPL. If you do not delete 33 * the provisions above, a recipient may use your version of this file under 34 * the terms of any one of the MPL, the GPL or the LGPL. 35 * 36 * ***** END LICENSE BLOCK ***** */ 37 38 // This code is a port to NS Mork from public domain Mithril C++ sources. 39 // Note many code comments here come verbatim from cut-and-pasted Mithril. 40 // In many places, code is identical; Mithril versions stay public domain. 41 // Changes in porting are mainly class type and scalar type name changes. 42 43 #ifndef _MORKPROBEMAP_ 44 #define _MORKPROBEMAP_ 1 45 46 #ifndef _MORK_ 47 # include "mork.h" 48 #endif 49 50 #ifndef _MORKNODE_ 51 # include "morkNode.h" 52 #endif 53 54 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 55 56 class morkMapScratch { // utility class used by map subclasses 57 public: 58 nsIMdbHeap* sMapScratch_Heap; // cached sMap_Heap 59 mork_count sMapScratch_Slots; // cached sMap_Slots 60 61 mork_u1* sMapScratch_Keys; // cached sMap_Keys 62 mork_u1* sMapScratch_Vals; // cached sMap_Vals 63 64 public: 65 void halt_map_scratch(morkEnv* ev); 66 }; 67 68 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 69 70 #define morkDerived_kProbeMap 0x7072 /* ascii 'pr' */ 71 #define morkProbeMap_kTag 0x70724D50 /* ascii 'prMP' */ 72 73 #define morkProbeMap_kLazyClearOnAdd ((mork_u1)'c') 74 75 class morkProbeMap : public morkNode { 76 protected: 77 // public: // slots inherited from morkNode (meant to inform only) 78 // nsIMdbHeap* mNode_Heap; 79 80 // mork_base mNode_Base; // must equal morkBase_kNode 81 // mork_derived mNode_Derived; // depends on specific node subclass 82 83 // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead 84 // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone 85 // mork_able mNode_Mutable; // can this node be modified? 86 // mork_load mNode_Load; // is this node clean or dirty? 87 88 // mork_uses mNode_Uses; // refcount for strong refs 89 // mork_refs mNode_Refs; // refcount for strong refs + weak refs 90 91 protected: 92 // { begin morkMap slots 93 nsIMdbHeap* sMap_Heap; // strong ref to heap allocating all space 94 95 mork_u1* sMap_Keys; 96 mork_u1* sMap_Vals; 97 98 mork_count sMap_Seed; // change count of members or structure 99 100 mork_count sMap_Slots; // count of slots in the hash table 101 mork_fill sMap_Fill; // number of used slots in the hash table 102 103 mork_size sMap_KeySize; // size of each key (cannot be zero) 104 mork_size sMap_ValSize; // size of each val (zero allowed) 105 106 mork_bool sMap_KeyIsIP; // sMap_KeySize == sizeof(mork_ip) 107 mork_bool sMap_ValIsIP; // sMap_ValSize == sizeof(mork_ip) 108 mork_u1 sMap_Pad[2]; // for u4 alignment 109 // } end morkMap slots 110 111 friend class morkProbeMapIter; // for access to protected slots 112 113 public: // getters MapSeed()114 mork_count MapSeed() const { return sMap_Seed; } 115 MapSlots()116 mork_count MapSlots() const { return sMap_Slots; } MapFill()117 mork_fill MapFill() const { return sMap_Fill; } 118 MapKeySize()119 mork_size MapKeySize() const { return sMap_KeySize; } MapValSize()120 mork_size MapValSize() const { return sMap_ValSize; } 121 MapKeyIsIP()122 mork_bool MapKeyIsIP() const { return sMap_KeyIsIP; } MapValIsIP()123 mork_bool MapValIsIP() const { return sMap_ValIsIP; } 124 125 protected: // slots 126 // { begin morkProbeMap slots 127 128 mork_fill sProbeMap_MaxFill; // max sMap_Fill before map must grow 129 130 mork_u1 sProbeMap_LazyClearOnAdd; // true if kLazyClearOnAdd 131 mork_bool sProbeMap_ZeroIsClearKey; // zero is adequate to clear keys 132 mork_u1 sProbeMap_Pad[2]; // for u4 alignment 133 134 mork_u4 sProbeMap_Tag; 135 136 // } end morkProbeMap slots 137 138 public: // lazy clear on add need_lazy_init()139 mork_bool need_lazy_init() const { 140 return sProbeMap_LazyClearOnAdd == morkProbeMap_kLazyClearOnAdd; 141 } 142 143 public: // typing GoodProbeMap()144 mork_bool GoodProbeMap() const { return sProbeMap_Tag == morkProbeMap_kTag; } 145 146 protected: // utilities 147 void* clear_alloc(morkEnv* ev, mork_size inSize); 148 149 mork_u1* map_new_vals(morkEnv* ev, mork_num inSlots); 150 mork_u1* map_new_keys(morkEnv* ev, mork_num inSlots); 151 152 void clear_probe_map(morkEnv* ev, nsIMdbHeap* ioMapHeap); 153 void init_probe_map(morkEnv* ev, mork_size inSlots); 154 void probe_map_lazy_init(morkEnv* ev); 155 156 mork_bool new_slots(morkEnv* ev, morkMapScratch* old, mork_num inSlots); 157 158 mork_test find_key_pos(morkEnv* ev, const void* inAppKey, mork_u4 inHash, 159 mork_pos* outPos) const; 160 161 void put_probe_kv(morkEnv* ev, const void* inAppKey, const void* inAppVal, 162 mork_pos inPos); 163 void get_probe_kv(morkEnv* ev, void* outAppKey, void* outAppVal, 164 mork_pos inPos) const; 165 166 mork_bool grow_probe_map(morkEnv* ev); 167 void rehash_old_map(morkEnv* ev, morkMapScratch* ioScratch); 168 void revert_map(morkEnv* ev, morkMapScratch* ioScratch); 169 170 public: // errors 171 void ProbeMapBadTagError(morkEnv* ev) const; 172 void WrapWithNoVoidSlotError(morkEnv* ev) const; 173 void GrowFailsMaxFillError(morkEnv* ev) const; 174 void MapKeyIsNotIPError(morkEnv* ev) const; 175 void MapValIsNotIPError(morkEnv* ev) const; 176 177 void MapNilKeysError(morkEnv* ev); 178 void MapZeroKeySizeError(morkEnv* ev); 179 180 void MapSeedOutOfSyncError(morkEnv* ev); 181 void MapFillUnderflowWarning(morkEnv* ev); 182 183 static void ProbeMapCutError(morkEnv* ev); 184 185 // { ===== begin morkMap methods ===== 186 public: 187 virtual mork_test // hit(a,b) implies hash(a) == hash(b) 188 MapTest(morkEnv* ev, const void* inMapKey, const void* inAppKey) const; 189 // Note inMapKey is always a key already stored in the map, while inAppKey 190 // is always a method argument parameter from a client method call. 191 // This matters the most in morkProbeMap subclasses, which have the 192 // responsibility of putting 'app' keys into slots for 'map' keys, and 193 // the bit pattern representation might be different in such cases. 194 // morkTest_kHit means that inMapKey equals inAppKey (and this had better 195 // also imply that hash(inMapKey) == hash(inAppKey)). 196 // morkTest_kMiss means that inMapKey does NOT equal inAppKey (but this 197 // implies nothing at all about hash(inMapKey) and hash(inAppKey)). 198 // morkTest_kVoid means that inMapKey is not a valid key bit pattern, 199 // which means that key slot in the map is not being used. Note that 200 // kVoid is only expected as a return value in morkProbeMap subclasses, 201 // because morkProbeMap must ask whether a key slot is used or not. 202 // morkChainMap however, always knows when a key slot is used, so only 203 // key slots expected to have valid bit patterns will be presented to 204 // the MapTest() methods for morkChainMap subclasses. 205 // 206 // NOTE: it is very important that subclasses correctly return the value 207 // morkTest_kVoid whenever the slot for inMapKey contains a bit pattern 208 // that means the slot is not being used, because this is the only way a 209 // probe map can terminate an unsuccessful search for a key in the map. 210 211 virtual mork_u4 // hit(a,b) implies hash(a) == hash(b) 212 MapHash(morkEnv* ev, const void* inAppKey) const; 213 214 virtual mork_bool MapAtPut(morkEnv* ev, const void* inAppKey, 215 const void* inAppVal, void* outAppKey, 216 void* outAppVal); 217 218 virtual mork_bool MapAt(morkEnv* ev, const void* inAppKey, void* outAppKey, 219 void* outAppVal); 220 221 virtual mork_num MapCutAll(morkEnv* ev); 222 // } ===== end morkMap methods ===== 223 224 // { ===== begin morkProbeMap methods ===== 225 public: 226 virtual mork_u4 ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const; 227 // ProbeMapHashMapKey() does logically the same thing as MapHash(), and 228 // the default implementation actually calls virtual MapHash(). However, 229 // Subclasses must override this method whenever the formats of keys in 230 // the map differ from app keys outside the map, because MapHash() only 231 // works on keys in 'app' format, while ProbeMapHashMapKey() only works 232 // on keys in 'map' format. This method is called in order to rehash all 233 // map keys when a map is grown, and this causes all old map members to 234 // move into new slot locations. 235 // 236 // Note it is absolutely imperative that a hash for a key in 'map' format 237 // be exactly the same the hash of the same key in 'app' format, or else 238 // maps will seem corrupt later when keys in 'app' format cannot be found. 239 240 virtual mork_bool ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey); 241 // ProbeMapIsKeyNil() must say whether the representation of logical 'nil' 242 // is currently found inside the key at ioMapKey, for a key found within 243 // the map. The the map iterator uses this method to find map keys that 244 // are actually being used for valid map associations; otherwise the 245 // iterator cannot determine which map slots actually denote used keys. 246 // The default method version returns true if all the bits equal zero. 247 248 virtual void ProbeMapClearKey( 249 morkEnv* ev, // put 'nil' into all keys inside map 250 void* ioMapKey, mork_count inKeyCount); // array of keys inside map 251 // ProbeMapClearKey() must put some representation of logical 'nil' into 252 // every key slot in the map, such that MapTest() will later recognize 253 // that this bit pattern shows each key slot is not actually being used. 254 // 255 // This method is typically called whenever the map is either created or 256 // grown into a larger size, where ioMapKey is a pointer to an array of 257 // inKeyCount keys, where each key is this->MapKeySize() bytes in size. 258 // Note that keys are assumed immediately adjacent with no padding, so 259 // if any alignment requirements must be met, then subclasses should have 260 // already accounted for this when specifying a key size in the map. 261 // 262 // Since this method will be called when a map is being grown in size, 263 // nothing should be assumed about the state slots of the map, since the 264 // ioMapKey array might not yet live in sMap_Keys, and the array length 265 // inKeyCount might not yet live in sMap_Slots. However, the value kept 266 // in sMap_KeySize never changes, so this->MapKeySize() is always correct. 267 268 virtual void ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map 269 const void* inAppKey, 270 const void* inAppVal, // (key,val) outside map 271 void* outMapKey, 272 void* outMapVal); // (key,val) inside map 273 // This method actually puts keys and vals in the map in suitable format. 274 // 275 // ProbeMapPushIn() must copy a caller key and value in 'app' format 276 // into the map slots provided, which are in 'map' format. When the 277 // 'app' and 'map' formats are identical, then this is just a bitwise 278 // copy of this->MapKeySize() key bytes and this->MapValSize() val bytes, 279 // and this is exactly what the default implementation performs. However, 280 // if 'app' and 'map' formats are different, and MapTest() depends on this 281 // difference in format, then subclasses must override this method to do 282 // whatever is necessary to store the input app key in output map format. 283 // 284 // Do NOT write more than this->MapKeySize() bytes of a map key, or more 285 // than this->MapValSize() bytes of a map val, or corruption might ensue. 286 // 287 // The inAppKey and inAppVal parameters are the same ones passed into a 288 // call to MapAtPut(), and the outMapKey and outMapVal parameters are ones 289 // determined by how the map currently positions key inAppKey in the map. 290 // 291 // Note any key or val parameter can be a null pointer, in which case 292 // this method must do nothing with those parameters. In particular, do 293 // no key move at all when either inAppKey or outMapKey is nil, and do 294 // no val move at all when either inAppVal or outMapVal is nil. Note that 295 // outMapVal should always be nil when this->MapValSize() is nil. 296 297 virtual void ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map 298 const void* inMapKey, 299 const void* inMapVal, // (key,val) inside map 300 void* outAppKey, 301 void* outAppVal) const; // (key,val) outside map 302 // This method actually gets keys and vals from the map in suitable format. 303 // 304 // ProbeMapPullOut() must copy a key and val in 'map' format into the 305 // caller key and val slots provided, which are in 'app' format. When the 306 // 'app' and 'map' formats are identical, then this is just a bitwise 307 // copy of this->MapKeySize() key bytes and this->MapValSize() val bytes, 308 // and this is exactly what the default implementation performs. However, 309 // if 'app' and 'map' formats are different, and MapTest() depends on this 310 // difference in format, then subclasses must override this method to do 311 // whatever is necessary to store the input map key in output app format. 312 // 313 // The outAppKey and outAppVal parameters are the same ones passed into a 314 // call to either MapAtPut() or MapAt(), while inMapKey and inMapVal are 315 // determined by how the map currently positions the target key in the map. 316 // 317 // Note any key or val parameter can be a null pointer, in which case 318 // this method must do nothing with those parameters. In particular, do 319 // no key move at all when either inMapKey or outAppKey is nil, and do 320 // no val move at all when either inMapVal or outAppVal is nil. Note that 321 // inMapVal should always be nil when this->MapValSize() is nil. 322 323 // } ===== end morkProbeMap methods ===== 324 325 // { ===== begin morkNode interface ===== 326 public: // morkNode virtual methods 327 virtual void CloseMorkNode( 328 morkEnv* ev) override; // CloseProbeMap() only if open 329 virtual ~morkProbeMap(); // assert that CloseProbeMap() executed earlier 330 331 public: // morkProbeMap construction & destruction 332 morkProbeMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioNodeHeap, 333 mork_size inKeySize, mork_size inValSize, nsIMdbHeap* ioMapHeap, 334 mork_size inSlots, mork_bool inZeroIsClearKey); 335 336 void CloseProbeMap(morkEnv* ev); // called by 337 338 public: // dynamic type identification IsProbeMap()339 mork_bool IsProbeMap() const { 340 return IsNode() && mNode_Derived == morkDerived_kProbeMap; 341 } 342 // } ===== end morkNode methods ===== 343 344 public: // typesafe refcounting inlines calling inherited morkNode methods SlotWeakMap(morkMap * me,morkEnv * ev,morkMap ** ioSlot)345 static void SlotWeakMap(morkMap* me, morkEnv* ev, morkMap** ioSlot) { 346 morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); 347 } 348 SlotStrongMap(morkMap * me,morkEnv * ev,morkMap ** ioSlot)349 static void SlotStrongMap(morkMap* me, morkEnv* ev, morkMap** ioSlot) { 350 morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); 351 } 352 }; 353 354 /*============================================================================*/ 355 /* morkProbeMapIter */ 356 357 #define morkProbeMapIter_kBeforeIx ((mork_i4)-1) /* before first member */ 358 #define morkProbeMapIter_kAfterIx ((mork_i4)-2) /* after last member */ 359 360 class morkProbeMapIter { 361 protected: 362 morkProbeMap* sProbeMapIter_Map; // nonref 363 mork_num sProbeMapIter_Seed; // iter's cached copy of map's seed 364 365 mork_i4 sProbeMapIter_HereIx; 366 367 mork_change sProbeMapIter_Change; // morkMapIter API simulation dummy 368 mork_u1 sProbeMapIter_Pad[3]; // for u4 alignment 369 370 public: 371 morkProbeMapIter(morkEnv* ev, morkProbeMap* ioMap); 372 void CloseMapIter(morkEnv* ev); 373 374 morkProbeMapIter(); // zero most slots; caller must call InitProbeMapIter() 375 376 protected: // protected so subclasses must provide suitable typesafe inlines: 377 void InitProbeMapIter(morkEnv* ev, morkProbeMap* ioMap); 378 InitMapIter(morkEnv * ev,morkProbeMap * ioMap)379 void InitMapIter(morkEnv* ev, 380 morkProbeMap* ioMap) // morkMapIter compatibility 381 { 382 this->InitProbeMapIter(ev, ioMap); 383 } 384 385 mork_bool IterFirst(morkEnv* ev, void* outKey, void* outVal); 386 mork_bool IterNext(morkEnv* ev, void* outKey, void* outVal); 387 mork_bool IterHere(morkEnv* ev, void* outKey, void* outVal); 388 389 // NOTE: the following methods ONLY work for sMap_ValIsIP pointer values. 390 // (Note the implied assumption that zero is never a good value pattern.) 391 392 void* IterFirstVal(morkEnv* ev, void* outKey); 393 // equivalent to { void* v=0; this->IterFirst(ev, outKey, &v); return v; } 394 395 void* IterNextVal(morkEnv* ev, void* outKey); 396 // equivalent to { void* v=0; this->IterNext(ev, outKey, &v); return v; } 397 398 void* IterHereVal(morkEnv* ev, void* outKey); 399 // equivalent to { void* v=0; this->IterHere(ev, outKey, &v); return v; } 400 401 // NOTE: the following methods ONLY work for sMap_KeyIsIP pointer values. 402 // (Note the implied assumption that zero is never a good key pattern.) 403 404 void* IterFirstKey(morkEnv* ev); 405 // equivalent to { void* k=0; this->IterFirst(ev, &k, 0); return k; } 406 407 void* IterNextKey(morkEnv* ev); 408 // equivalent to { void* k=0; this->IterNext(ev, &k, 0); return k; } 409 410 void* IterHereKey(morkEnv* ev); 411 // equivalent to { void* k=0; this->IterHere(ev, &k, 0); return k; } 412 413 public: // simulation of the morkMapIter API for morkMap compatibility: 414 mork_change* First(morkEnv* ev, void* outKey, void* outVal); 415 mork_change* Next(morkEnv* ev, void* outKey, void* outVal); 416 mork_change* Here(morkEnv* ev, void* outKey, void* outVal); 417 418 mork_change* CutHere(morkEnv* ev, void* outKey, void* outVal); 419 }; 420 421 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 422 423 #endif /* _MORKPROBEMAP_ */ 424