| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762 |
- //
- // System.Collections.Hashtable
- //
- // Author:
- // Sergey Chaban ([email protected])
- //
- using System;
- using System.Collections;
- // TODO: 1. Interfaces to implement: ISerializable and IDeserializationCallback;
- // Synchronized wrapper (it's really easy but requires at least
- // System.Threading.Monitor to be present, maybe just a stub for now).
- // 2. Meaningfull error messages for all exceptions.
- namespace System.Collections {
- public class Hashtable : IDictionary, ICollection,
- IEnumerable, ICloneable {
- internal struct slot {
- internal Object key;
- internal Object value;
- // Hashcode. Chains are also marked through this.
- internal int hashMix;
- }
- //
- // Private data
- //
- private readonly static int CHAIN_MARKER=~Int32.MaxValue;
- private readonly static int ALLOC_GRAIN=0x2F;
- // Used as indicator for the removed parts of a chain.
- private readonly static Object REMOVED_MARKER=new Object();
- private int inUse;
- private int modificationCount;
- private float loadFactor;
- private slot[] table;
- private int threshold;
- private IHashCodeProvider m_hcp;
- private IComparer m_comparer;
- public static int[] primeTbl={};
- // Class constructor
- static Hashtable() {
- // NOTE: Precalculate primes table.
- // This precalculated table of primes is intended
- // to speed-up allocations/resize for relatively
- // small tables.
- // I'm not sure whether it's a good idea or not.
- // Also I am in doubt as for the quality of this
- // particular implementation, probably the increment
- // shouldn't be linear? Consider this as a hack
- // or as a placeholder for future improvements.
- int size=0x2000/ALLOC_GRAIN;
- primeTbl=new int[size];
- for (int x=53,i=0;i<size;x+=ALLOC_GRAIN,i++) {
- primeTbl[i]=CalcPrime(x);
- }
- }
- //
- // Constructors
- //
- public Hashtable() : this(0,1.0f) {}
- public Hashtable(int capacity, float loadFactor, IHashCodeProvider hcp, IComparer comparer) {
- if (capacity<0)
- throw new ArgumentOutOfRangeException("negative capacity");
- if (loadFactor<0.1 || loadFactor>1)
- throw new ArgumentOutOfRangeException("load factor");
- if (capacity==0) ++capacity;
- this.loadFactor=0.75f*loadFactor;
- int size=(int)(capacity/this.loadFactor);
- size=ToPrime(size);
- this.SetTable(new slot[size]);
- this.hcp=hcp;
- this.comparer=comparer;
- this.inUse=0;
- this.modificationCount=0;
- }
- public Hashtable(int capacity, float loadFactor) :
- this(capacity,loadFactor,null,null) {}
- public Hashtable(int capacity) : this(capacity,1.0f) {}
- public Hashtable(int capacity,
- IHashCodeProvider hcp,
- IComparer comparer
- ) : this(capacity,1.0f,hcp,comparer) {}
- public Hashtable(IDictionary d, float loadFactor,
- IHashCodeProvider hcp,IComparer comparer)
- : this(d!=null?d.Count:0,
- loadFactor,hcp,comparer) {
- if (d==null)
- throw new ArgumentNullException("dictionary");
- IDictionaryEnumerator it=d.GetEnumerator();
- while (it.MoveNext()) {
- Add(it.Key,it.Value);
- }
-
- }
- public Hashtable(IDictionary d, float loadFactor)
- : this(d,loadFactor,null,null) {}
- public Hashtable(IDictionary d) : this(d,1.0f) {}
- public Hashtable(IDictionary d, IHashCodeProvider hcp,IComparer comparer)
- : this(d,1.0f,hcp,comparer) {}
- public Hashtable(IHashCodeProvider hcp,IComparer comparer)
- : this(1,1.0f,hcp,comparer) {}
- //
- // Properties
- //
- protected IComparer comparer {
- set {
- m_comparer=value;
- }
- get {
- return m_comparer;
- }
- }
- protected IHashCodeProvider hcp {
- set {
- m_hcp=value;
- }
- get {
- return m_hcp;
- }
- }
- // ICollection
- public virtual int Count {
- get {
- return inUse;
- }
- }
- public virtual bool IsSynchronized {
- get {
- return false;
- }
- }
- public virtual Object SyncRoot {
- get {
- return this;
- }
- }
- // IDictionary
- public virtual bool IsFixedSize {
- get {
- return false;
- }
- }
- public virtual bool IsReadOnly {
- get {
- return false;
- }
- }
- public virtual ICollection Keys {
- get {
- return new HashKeys(this);
- }
- }
- public virtual ICollection Values {
- get {
- return new HashValues(this);
- }
- }
- public virtual Object this[Object key] {
- get {
- return GetImpl(key);
- }
- set {
- PutImpl(key,value,true);
- }
- }
- //
- // Interface methods
- //
- // IEnumerable
- IEnumerator IEnumerable.GetEnumerator() {
- return new Enumerator(this,EnumeratorMode.KEY_MODE);
- }
- // ICollection
- public virtual void CopyTo(Array array, int arrayIndex) {
- IDictionaryEnumerator it=GetEnumerator();
- int i=arrayIndex;
- while (it.MoveNext()) {
- array.SetValue(it.Entry,i++);
- }
- }
- // IDictionary
- public virtual void Add(Object key, Object value) {
- PutImpl(key,value,false);
- }
- public virtual void Clear() {
- for (int i=0;i<table.Length;i++) {
- table[i].key=null;
- table[i].value=null;
- table[i].hashMix=0;
- }
- }
- public virtual bool Contains(Object key) {
- return (Find(key)>=0);
- }
- public virtual IDictionaryEnumerator GetEnumerator() {
- return new Enumerator(this,EnumeratorMode.KEY_MODE);
- }
- public virtual void Remove(Object key) {
- int i=Find(key);
- slot[] table=this.table;
- if (i>=0) {
- int h=table[i].hashMix;
- h&=CHAIN_MARKER;
- table[i].hashMix=h;
- table[i].key=(h!=0)
- ? REMOVED_MARKER
- : null;
- table[i].value=null;
- --inUse;
- ++modificationCount;
- }
- }
- public virtual bool ContainsKey(object key) {
- return Contains(key);
- }
- public virtual bool ContainsValue(object value) {
- int size=this.table.Length;
- slot[] table=this.table;
- for (int i=0;i<size;i++) {
- slot entry=table[i];
- if (entry.key!=null
- && entry.key!=REMOVED_MARKER
- && value.Equals(entry.value)) {
- return true;
- }
- }
- return false;
- }
- // ICloneable
- public virtual object Clone() {
- Hashtable ht=new Hashtable(Count, hcp, comparer);
- ht.modificationCount=this.modificationCount;
- ht.inUse=this.inUse;
- ht.AdjustThreshold();
- // FIXME: maybe it's faster to simply
- // copy the back-end array?
- IDictionaryEnumerator it=GetEnumerator();
- while (it.MoveNext()) {
- ht[it.Key]=it.Value;
- }
- return ht;
- }
- // TODO: public virtual void GetObjectData(SerializationInfo info, StreamingContext context) {}
- // TODO: public virtual void OnDeserialization(object sender);
- public override string ToString() {
- // FIXME: What's it supposed to do?
- // Maybe print out some internals here? Anyway.
- return "mono::System.Collections.Hashtable";
- }
- /// <summary>
- /// Returns a synchronized (thread-safe)
- /// wrapper for the Hashtable.
- /// </summary>
- public static Hashtable Synchronized(Hashtable table) {
- // TODO: implement
- return null;
- }
- //
- // Protected instance methods
- //
- /// <summary>Returns the hash code for the specified key.</summary>
- protected virtual int GetHash(Object key) {
- IHashCodeProvider hcp=this.hcp;
- return (hcp!=null)
- ? hcp.GetHashCode()
- : key.GetHashCode();
- }
- /// <summary>
- /// Compares a specific Object with a specific key
- /// in the Hashtable.
- /// </summary>
- protected virtual bool KeyEquals(Object item,Object key) {
- IComparer c=this.comparer;
- if (c!=null)
- return (c.Compare(item,key)==0);
- else
- return item.Equals(key);
- }
- //
- // Private instance methods
- //
- private void AdjustThreshold() {
- int size=table.Length;
- threshold=(int)(size*loadFactor);
- if (this.threshold>=size) threshold=size-1;
- }
- private void SetTable(slot[] table) {
- if (table==null)
- throw new ArgumentNullException("table");
- this.table=table;
- AdjustThreshold();
- }
- private Object GetImpl(Object key) {
- int i=Find(key);
- if (i>=0)
- return table[i].value;
- else
- return null;
- }
- private int Find(Object key) {
- if (key==null)
- throw new ArgumentNullException("null key");
- uint size=(uint)this.table.Length;
- int h=this.GetHash(key) & Int32.MaxValue;
- uint spot=(uint)h;
- uint step=(uint)((h>>5)+1)%(size-1)+1;
- slot[] table=this.table;
- for (int i=0;i<size;i++) {
- int indx=(int)(spot%size);
- slot entry=table[indx];
- Object k=entry.key;
- if (k==null) return -1;
- if ((entry.hashMix & Int32.MaxValue)==h
- && this.KeyEquals(key,k)) {
- return indx;
- }
- if ((entry.hashMix & CHAIN_MARKER)==0)
- return -1;
- spot+=step;
- }
- return -1;
- }
- private void Rehash() {
- int oldSize=this.table.Length;
- // From the SDK docs:
- // Hashtable is automatically increased
- // to the smallest prime number that is larger
- // than twice the current number of Hashtable buckets
- uint newSize=(uint)ToPrime((oldSize<<1)|1);
- slot[] newTable=new slot[newSize];
- slot[] table=this.table;
- for (int i=0;i<oldSize;i++) {
- slot s=table[i];
- if (s.key!=null) {
- int h=s.hashMix & Int32.MaxValue;
- uint spot=(uint)h;
- uint step=((uint)(h>>5)+1)%(newSize-1)+1;
- for (uint j=spot%newSize;;spot+=step,j=spot%newSize) {
- // No check for REMOVED_MARKER here,
- // because the table is just allocated.
- if (newTable[j].key==null) {
- newTable[j].key=s.key;
- newTable[j].value=s.value;
- newTable[j].hashMix|=h;
- break;
- } else {
- newTable[j].hashMix|=CHAIN_MARKER;
- }
- }
- }
- }
- ++this.modificationCount;
- this.SetTable(newTable);
- }
- private void PutImpl(Object key,Object value,bool overwrite) {
- if (key==null)
- throw new ArgumentNullException("null key");
- uint size=(uint)this.table.Length;
- if (this.inUse>=this.threshold) {
- this.Rehash();
- size=(uint)this.table.Length;
- }
- int h=this.GetHash(key) & Int32.MaxValue;
- uint spot=(uint)h;
- uint step=(uint)((spot>>5)+1)%(size-1)+1;
- slot[] table=this.table;
- slot entry;
- int freeIndx=-1;
- for (int i=0;i<size;i++) {
- int indx=(int)(spot%size);
- entry=table[indx];
- if (freeIndx==-1
- && entry.key==REMOVED_MARKER
- && (entry.hashMix & CHAIN_MARKER)!=0) freeIndx=indx;
- if (entry.key==null ||
- (entry.key==REMOVED_MARKER
- && (entry.hashMix & CHAIN_MARKER)!=0)) {
- if (freeIndx==-1) freeIndx=indx;
- break;
- }
- if ((entry.hashMix & Int32.MaxValue)==h
- && KeyEquals(key,entry.key)) {
- if (overwrite) {
- table[indx].value=value;
- ++this.modificationCount;
- } else {
- // Handle Add():
- // An entry with the same key already exists in the Hashtable.
- throw new ArgumentException("Key duplication");
- }
- return;
- }
- if (freeIndx==-1) {
- table[indx].hashMix|=CHAIN_MARKER;
- }
- spot+=step;
- }
- if (freeIndx!=-1) {
- table[freeIndx].key=key;
- table[freeIndx].value=value;
- table[freeIndx].hashMix|=h;
- ++this.inUse;
- ++this.modificationCount;
- }
- }
- private void CopyToArray(Array arr,int i,
- EnumeratorMode mode) {
- IEnumerator it=new Enumerator(this,mode);
- while (it.MoveNext()) {
- arr.SetValue(it.Current,i++);
- }
- }
- //
- // Private static methods
- //
- private static bool TestPrime(int x) {
- if ((x & 1)!=0) {
- for (int n=3;n<(int)Math.Sqrt(x);n+=2) {
- if (x%n==0) return false;
- }
- return true;
- }
- // There is only one even prime - 2.
- return (x==2);
- }
- private static int CalcPrime(int x) {
- for (int i=(x&(~1))-1;i<Int32.MaxValue;i+=2) {
- if (TestPrime(i)) return i;
- }
- return x;
- }
- private static int ToPrime(int x) {
- for (int i=x/ALLOC_GRAIN;i<primeTbl.Length;i++) {
- if (x<=primeTbl[i]) return primeTbl[i];
- }
- return CalcPrime(x);
- }
- //
- // Inner classes
- //
- public enum EnumeratorMode : int {KEY_MODE=0,VALUE_MODE};
- protected sealed class Enumerator : IDictionaryEnumerator, IEnumerator {
- private Hashtable host;
- private int stamp;
- private int pos;
- private int size;
- private EnumeratorMode mode;
- private Object currentKey;
- private Object currentValue;
- private readonly static string xstr="Hashtable.Enumerator: snapshot out of sync.";
- public Enumerator(Hashtable host,EnumeratorMode mode) {
- this.host=host;
- stamp=host.modificationCount;
- size=host.table.Length;
- this.mode=mode;
- Reset();
- }
- public Enumerator(Hashtable host)
- : this(host,EnumeratorMode.KEY_MODE) {}
- private void FailFast() {
- if (host.modificationCount!=stamp) {
- throw new InvalidOperationException(xstr);
- }
- }
- public void Reset() {
- FailFast();
- pos=-1;
- currentKey=null;
- currentValue=null;
- }
- public bool MoveNext() {
- FailFast();
- if (pos<size) while (++pos<size) {
- slot entry=host.table[pos];
- if (entry.key!=null && entry.key!=REMOVED_MARKER) {
- currentKey=entry.key;
- currentValue=entry.value;
- return true;
- }
- }
- currentKey=null;
- currentValue=null;
- return false;
- }
- public DictionaryEntry Entry {
- get {
- FailFast();
- return new DictionaryEntry(currentKey,currentValue);
- }
- }
- public Object Key {
- get {
- FailFast();
- return currentKey;
- }
- }
- public Object Value {
- get {
- FailFast();
- return currentValue;
- }
- }
- public Object Current {
- get {
- FailFast();
- return (mode==EnumeratorMode.KEY_MODE)
- ? currentKey
- : currentValue;
- }
- }
- }
- protected class HashKeys : ICollection, IEnumerable {
- private Hashtable host;
- private int count;
- public HashKeys(Hashtable host) {
- if (host==null)
- throw new ArgumentNullException();
- this.host=host;
- this.count=host.Count;
- }
- // ICollection
- public virtual int Count {
- get {return count;}
- }
- public virtual bool IsSynchronized {
- get {return host.IsSynchronized;}
- }
- public virtual Object SyncRoot {
- get {return host.SyncRoot;}
- }
- public virtual void CopyTo(Array array, int arrayIndex) {
- host.CopyToArray(array,arrayIndex,EnumeratorMode.KEY_MODE);
- }
- // IEnumerable
- public virtual IEnumerator GetEnumerator() {
- return new Hashtable.Enumerator(host,EnumeratorMode.KEY_MODE);
- }
- }
- protected class HashValues : ICollection, IEnumerable {
- private Hashtable host;
- private int count;
- public HashValues(Hashtable host) {
- if (host==null)
- throw new ArgumentNullException();
- this.host=host;
- this.count=host.Count;
- }
- // ICollection
- public virtual int Count {
- get {return count;}
- }
- public virtual bool IsSynchronized {
- get {return host.IsSynchronized;}
- }
- public virtual Object SyncRoot {
- get {return host.SyncRoot;}
- }
- public virtual void CopyTo(Array array, int arrayIndex) {
- host.CopyToArray(array,arrayIndex,EnumeratorMode.VALUE_MODE);
- }
- // IEnumerable
- public virtual IEnumerator GetEnumerator() {
- return new Hashtable.Enumerator(host,EnumeratorMode.VALUE_MODE);
- }
- }
- }
- }
|