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