зеркало из https://github.com/mozilla/pjs.git
432 строки
18 KiB
C++
432 строки
18 KiB
C++
/* -*- 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.
|
|
|
|
#ifndef _MORKPROBEMAP_
|
|
#define _MORKPROBEMAP_ 1
|
|
|
|
#ifndef _MORK_
|
|
#include "mork.h"
|
|
#endif
|
|
|
|
#ifndef _MORKNODE_
|
|
#include "morkNode.h"
|
|
#endif
|
|
|
|
//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
|
|
|
|
class morkMapScratch { // utility class used by map subclasses
|
|
public:
|
|
nsIMdbHeap* sMapScratch_Heap; // cached sMap_Heap
|
|
mork_count sMapScratch_Slots; // cached sMap_Slots
|
|
|
|
mork_u1* sMapScratch_Keys; // cached sMap_Keys
|
|
mork_u1* sMapScratch_Vals; // cached sMap_Vals
|
|
|
|
public:
|
|
void halt_map_scratch(morkEnv* ev);
|
|
};
|
|
|
|
//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
|
|
|
|
#define morkDerived_kProbeMap 0x7072 /* ascii 'pr' */
|
|
#define morkProbeMap_kTag 0x70724D50 /* ascii 'prMP' */
|
|
|
|
#define morkProbeMap_kLazyClearOnAdd ((mork_u1) 'c')
|
|
|
|
class morkProbeMap: public morkNode {
|
|
|
|
protected:
|
|
|
|
// public: // slots inherited from morkNode (meant to inform only)
|
|
// nsIMdbHeap* mNode_Heap;
|
|
|
|
// mork_base mNode_Base; // must equal morkBase_kNode
|
|
// mork_derived mNode_Derived; // depends on specific node subclass
|
|
|
|
// mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
|
|
// mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
|
|
// mork_able mNode_Mutable; // can this node be modified?
|
|
// mork_load mNode_Load; // is this node clean or dirty?
|
|
|
|
// mork_uses mNode_Uses; // refcount for strong refs
|
|
// mork_refs mNode_Refs; // refcount for strong refs + weak refs
|
|
|
|
protected:
|
|
// { begin morkMap slots
|
|
nsIMdbHeap* sMap_Heap; // strong ref to heap allocating all space
|
|
|
|
mork_u1* sMap_Keys;
|
|
mork_u1* sMap_Vals;
|
|
|
|
mork_count sMap_Seed; // change count of members or structure
|
|
|
|
mork_count sMap_Slots; // count of slots in the hash table
|
|
mork_fill sMap_Fill; // number of used slots in the hash table
|
|
|
|
mork_size sMap_KeySize; // size of each key (cannot be zero)
|
|
mork_size sMap_ValSize; // size of each val (zero allowed)
|
|
|
|
mork_bool sMap_KeyIsIP; // sMap_KeySize == sizeof(mork_ip)
|
|
mork_bool sMap_ValIsIP; // sMap_ValSize == sizeof(mork_ip)
|
|
mork_u1 sMap_Pad[ 2 ]; // for u4 alignment
|
|
// } end morkMap slots
|
|
|
|
friend class morkProbeMapIter; // for access to protected slots
|
|
|
|
public: // getters
|
|
mork_count MapSeed() const { return sMap_Seed; }
|
|
|
|
mork_count MapSlots() const { return sMap_Slots; }
|
|
mork_fill MapFill() const { return sMap_Fill; }
|
|
|
|
mork_size MapKeySize() const { return sMap_KeySize; }
|
|
mork_size MapValSize() const { return sMap_ValSize; }
|
|
|
|
mork_bool MapKeyIsIP() const { return sMap_KeyIsIP; }
|
|
mork_bool MapValIsIP() const { return sMap_ValIsIP; }
|
|
|
|
protected: // slots
|
|
// { begin morkProbeMap slots
|
|
|
|
mork_fill sProbeMap_MaxFill; // max sMap_Fill before map must grow
|
|
|
|
mork_u1 sProbeMap_LazyClearOnAdd; // true if kLazyClearOnAdd
|
|
mork_bool sProbeMap_ZeroIsClearKey; // zero is adequate to clear keys
|
|
mork_u1 sProbeMap_Pad[ 2 ]; // for u4 alignment
|
|
|
|
mork_u4 sProbeMap_Tag;
|
|
|
|
// } end morkProbeMap slots
|
|
|
|
public: // lazy clear on add
|
|
|
|
mork_bool need_lazy_init() const
|
|
{ return sProbeMap_LazyClearOnAdd == morkProbeMap_kLazyClearOnAdd; }
|
|
|
|
public: // typing
|
|
mork_bool GoodProbeMap() const
|
|
{ return sProbeMap_Tag == morkProbeMap_kTag; }
|
|
|
|
protected: // utilities
|
|
|
|
void* clear_alloc(morkEnv* ev, mork_size inSize);
|
|
|
|
mork_u1* map_new_vals(morkEnv* ev, mork_num inSlots);
|
|
mork_u1* map_new_keys(morkEnv* ev, mork_num inSlots);
|
|
|
|
void clear_probe_map(morkEnv* ev, nsIMdbHeap* ioMapHeap);
|
|
void init_probe_map(morkEnv* ev, mork_size inSlots);
|
|
void probe_map_lazy_init(morkEnv* ev);
|
|
|
|
mork_bool new_slots(morkEnv* ev, morkMapScratch* old, mork_num inSlots);
|
|
|
|
mork_test find_key_pos(morkEnv* ev, const void* inAppKey,
|
|
mork_u4 inHash, mork_pos* outPos) const;
|
|
|
|
void put_probe_kv(morkEnv* ev,
|
|
const void* inAppKey, const void* inAppVal, mork_pos inPos);
|
|
void get_probe_kv(morkEnv* ev,
|
|
void* outAppKey, void* outAppVal, mork_pos inPos) const;
|
|
|
|
mork_bool grow_probe_map(morkEnv* ev);
|
|
void rehash_old_map(morkEnv* ev, morkMapScratch* ioScratch);
|
|
void revert_map(morkEnv* ev, morkMapScratch* ioScratch);
|
|
|
|
public: // errors
|
|
void ProbeMapBadTagError(morkEnv* ev) const;
|
|
void WrapWithNoVoidSlotError(morkEnv* ev) const;
|
|
void GrowFailsMaxFillError(morkEnv* ev) const;
|
|
void MapKeyIsNotIPError(morkEnv* ev) const;
|
|
void MapValIsNotIPError(morkEnv* ev) const;
|
|
|
|
void MapNilKeysError(morkEnv* ev);
|
|
void MapZeroKeySizeError(morkEnv* ev);
|
|
|
|
void MapSeedOutOfSyncError(morkEnv* ev);
|
|
void MapFillUnderflowWarning(morkEnv* ev);
|
|
|
|
static void ProbeMapCutError(morkEnv* ev);
|
|
|
|
// { ===== begin morkMap methods =====
|
|
public:
|
|
|
|
virtual mork_test // hit(a,b) implies hash(a) == hash(b)
|
|
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.
|
|
|
|
virtual mork_u4 // hit(a,b) implies hash(a) == hash(b)
|
|
MapHash(morkEnv* ev, const void* inAppKey) const;
|
|
|
|
virtual mork_bool
|
|
MapAtPut(morkEnv* ev, const void* inAppKey, const void* inAppVal,
|
|
void* outAppKey, void* outAppVal);
|
|
|
|
virtual mork_bool
|
|
MapAt(morkEnv* ev, const void* inAppKey, void* outAppKey, void* outAppVal);
|
|
|
|
virtual mork_num
|
|
MapCutAll(morkEnv* ev);
|
|
// } ===== end morkMap methods =====
|
|
|
|
|
|
// { ===== begin morkProbeMap methods =====
|
|
public:
|
|
|
|
virtual mork_u4
|
|
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.
|
|
|
|
virtual mork_bool
|
|
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.
|
|
|
|
virtual void
|
|
ProbeMapClearKey(morkEnv* ev, // put 'nil' alls 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.
|
|
|
|
virtual void
|
|
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
|
|
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.
|
|
|
|
// } ===== end morkProbeMap methods =====
|
|
|
|
|
|
// { ===== begin morkNode interface =====
|
|
public: // morkNode virtual methods
|
|
virtual void CloseMorkNode(morkEnv* ev); // CloseProbeMap() only if open
|
|
virtual ~morkProbeMap(); // assert that CloseProbeMap() executed earlier
|
|
|
|
public: // morkProbeMap construction & destruction
|
|
morkProbeMap(morkEnv* ev, const morkUsage& inUsage,
|
|
nsIMdbHeap* ioNodeHeap,
|
|
mork_size inKeySize, mork_size inValSize,
|
|
nsIMdbHeap* ioMapHeap, mork_size inSlots,
|
|
mork_bool inZeroIsClearKey);
|
|
|
|
void CloseProbeMap(morkEnv* ev); // called by
|
|
|
|
public: // dynamic type identification
|
|
mork_bool IsProbeMap() const
|
|
{ return IsNode() && mNode_Derived == morkDerived_kProbeMap; }
|
|
// } ===== end morkNode methods =====
|
|
|
|
public: // typesafe refcounting inlines calling inherited morkNode methods
|
|
static void SlotWeakMap(morkMap* me,
|
|
morkEnv* ev, morkMap** ioSlot)
|
|
{ morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
|
|
|
|
static void SlotStrongMap(morkMap* me,
|
|
morkEnv* ev, morkMap** ioSlot)
|
|
{ morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
|
|
};
|
|
|
|
/*============================================================================*/
|
|
/* morkProbeMapIter */
|
|
|
|
#define morkProbeMapIter_kBeforeIx ((mork_i4) -1) /* before first member */
|
|
#define morkProbeMapIter_kAfterIx ((mork_i4) -2) /* after last member */
|
|
|
|
class morkProbeMapIter {
|
|
|
|
protected:
|
|
morkProbeMap* sProbeMapIter_Map; // nonref
|
|
mork_num sProbeMapIter_Seed; // iter's cached copy of map's seed
|
|
|
|
mork_i4 sProbeMapIter_HereIx;
|
|
|
|
mork_change sProbeMapIter_Change; // morkMapIter API simulation dummy
|
|
mork_u1 sProbeMapIter_Pad[ 3 ]; // for u4 alignment
|
|
|
|
public:
|
|
morkProbeMapIter(morkEnv* ev, morkProbeMap* ioMap);
|
|
void CloseMapIter(morkEnv* ev);
|
|
|
|
morkProbeMapIter( ); // zero most slots; caller must call InitProbeMapIter()
|
|
|
|
protected: // protected so subclasses must provide suitable typesafe inlines:
|
|
|
|
void InitProbeMapIter(morkEnv* ev, morkProbeMap* ioMap);
|
|
|
|
void InitMapIter(morkEnv* ev, morkProbeMap* ioMap) // morkMapIter compatibility
|
|
{ this->InitProbeMapIter(ev, ioMap); }
|
|
|
|
mork_bool IterFirst(morkEnv* ev, void* outKey, void* outVal);
|
|
mork_bool IterNext(morkEnv* ev, void* outKey, void* outVal);
|
|
mork_bool IterHere(morkEnv* ev, void* outKey, void* outVal);
|
|
|
|
// NOTE: the following methods ONLY work for sMap_ValIsIP pointer values.
|
|
// (Note the implied assumption that zero is never a good value pattern.)
|
|
|
|
void* IterFirstVal(morkEnv* ev, void* outKey);
|
|
// equivalent to { void* v=0; this->IterFirst(ev, outKey, &v); return v; }
|
|
|
|
void* IterNextVal(morkEnv* ev, void* outKey);
|
|
// equivalent to { void* v=0; this->IterNext(ev, outKey, &v); return v; }
|
|
|
|
void* IterHereVal(morkEnv* ev, void* outKey);
|
|
// equivalent to { void* v=0; this->IterHere(ev, outKey, &v); return v; }
|
|
|
|
// NOTE: the following methods ONLY work for sMap_KeyIsIP pointer values.
|
|
// (Note the implied assumption that zero is never a good key pattern.)
|
|
|
|
void* IterFirstKey(morkEnv* ev);
|
|
// equivalent to { void* k=0; this->IterFirst(ev, &k, 0); return k; }
|
|
|
|
void* IterNextKey(morkEnv* ev);
|
|
// equivalent to { void* k=0; this->IterNext(ev, &k, 0); return k; }
|
|
|
|
void* IterHereKey(morkEnv* ev);
|
|
// equivalent to { void* k=0; this->IterHere(ev, &k, 0); return k; }
|
|
|
|
public: // simulation of the morkMapIter API for morkMap compatibility:
|
|
mork_change* First(morkEnv* ev, void* outKey, void* outVal);
|
|
mork_change* Next(morkEnv* ev, void* outKey, void* outVal);
|
|
mork_change* Here(morkEnv* ev, void* outKey, void* outVal);
|
|
|
|
mork_change* CutHere(morkEnv* ev, void* outKey, void* outVal);
|
|
};
|
|
|
|
//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
|
|
|
|
#endif /* _MORKPROBEMAP_ */
|