2 // System.Collections.SortedList.cs
5 // Sergey Chaban (serge@wildwestsoftware.com)
6 // Duncan Mak (duncan@ximian.com)
7 // Herve Poussineau (hpoussineau@fr.st
11 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Globalization;
34 using System.Runtime.InteropServices;
35 using System.Diagnostics;
37 namespace System.Collections {
40 /// Represents a collection of associated keys and values
41 /// that are sorted by the keys and are accessible by key
46 [DebuggerDisplay ("Count={Count}")]
47 [DebuggerTypeProxy (typeof (CollectionDebuggerView))]
48 public class SortedList : IDictionary, ICollection,
49 IEnumerable, ICloneable {
53 internal struct Slot {
55 internal Object value;
58 const int INITIAL_SIZE = 16;
60 private enum EnumeratorMode : int { KEY_MODE = 0, VALUE_MODE, ENTRY_MODE }
63 private IComparer comparer;
65 private int modificationCount;
66 private int defaultCapacity;
72 : this (null, INITIAL_SIZE)
76 public SortedList (int initialCapacity)
77 : this (null, initialCapacity)
81 public SortedList (IComparer comparer, int capacity)
84 throw new ArgumentOutOfRangeException ("capacity");
89 defaultCapacity = INITIAL_SIZE;
91 this.comparer = comparer;
92 InitTable (capacity, true);
95 public SortedList (IComparer comparer)
97 this.comparer = comparer;
98 InitTable (INITIAL_SIZE, true);
102 public SortedList (IDictionary d) : this (d, null)
106 // LAMESPEC: MSDN docs talk about an InvalidCastException but
107 // I wasn't able to duplicate such a case in the unit tests.
108 public SortedList (IDictionary d, IComparer comparer)
111 throw new ArgumentNullException ("dictionary");
113 InitTable (d.Count, true);
114 this.comparer = comparer;
116 IDictionaryEnumerator it = d.GetEnumerator ();
117 while (it.MoveNext ()) {
118 Add (it.Key, it.Value);
128 public virtual int Count {
134 public virtual bool IsSynchronized {
140 public virtual Object SyncRoot {
149 public virtual bool IsFixedSize {
156 public virtual bool IsReadOnly {
162 public virtual ICollection Keys {
164 return new ListKeys (this);
168 public virtual ICollection Values {
170 return new ListValues (this);
176 public virtual Object this [Object key] {
179 throw new ArgumentNullException();
180 return GetImpl (key);
184 throw new ArgumentNullException();
186 throw new NotSupportedException("SortedList is Read Only.");
187 if (Find(key) < 0 && IsFixedSize)
188 throw new NotSupportedException("Key not found and SortedList is fixed size.");
190 PutImpl (key, value, true);
194 public virtual int Capacity {
200 int current = this.table.Length;
203 throw new ArgumentOutOfRangeException("capacity too small");
205 else if (value == 0) {
206 // return to default size
207 Slot [] newTable = new Slot [defaultCapacity];
208 Array.Copy (table, newTable, inUse);
209 this.table = newTable;
211 else if (value > inUse) {
212 Slot [] newTable = new Slot [value];
213 Array.Copy (table, newTable, inUse);
214 this.table = newTable;
216 else if (value > current) {
217 Slot [] newTable = new Slot [value];
218 Array.Copy (table, newTable, current);
219 this.table = newTable;
225 // Public instance methods.
230 IEnumerator IEnumerable.GetEnumerator ()
232 return new Enumerator (this, EnumeratorMode.ENTRY_MODE);
238 public virtual void Add (object key, object value)
240 PutImpl (key, value, false);
244 public virtual void Clear ()
246 Array.Clear (table, 0, table.Length);
251 public virtual bool Contains (object key)
254 throw new ArgumentNullException();
257 return (Find (key) >= 0);
258 } catch (Exception) {
259 throw new InvalidOperationException();
264 public virtual IDictionaryEnumerator GetEnumerator ()
266 return new Enumerator (this, EnumeratorMode.ENTRY_MODE);
269 public virtual void Remove (object key)
271 int i = IndexOfKey (key);
272 if (i >= 0) RemoveAt (i);
278 public virtual void CopyTo (Array array, int arrayIndex)
281 throw new ArgumentNullException();
284 throw new ArgumentOutOfRangeException();
287 throw new ArgumentException("array is multi-dimensional");
288 if (arrayIndex >= array.Length)
289 throw new ArgumentNullException("arrayIndex is greater than or equal to array.Length");
290 if (Count > (array.Length - arrayIndex))
291 throw new ArgumentNullException("Not enough space in array from arrayIndex to end of array");
293 IDictionaryEnumerator it = GetEnumerator ();
296 while (it.MoveNext ()) {
297 array.SetValue (it.Entry, i++);
305 public virtual object Clone ()
307 SortedList sl = new SortedList (this, comparer);
308 sl.modificationCount = this.modificationCount;
319 public virtual IList GetKeyList ()
321 return new ListKeys (this);
325 public virtual IList GetValueList ()
327 return new ListValues (this);
331 public virtual void RemoveAt (int index)
333 Slot [] table = this.table;
335 if (index >= 0 && index < cnt) {
336 if (index != cnt - 1) {
337 Array.Copy (table, index+1, table, index, cnt-1-index);
339 table [index].key = null;
340 table [index].value = null;
345 throw new ArgumentOutOfRangeException("index out of range");
349 public virtual int IndexOfKey (object key)
352 throw new ArgumentNullException();
357 } catch (Exception) {
358 throw new InvalidOperationException();
361 return (indx | (indx >> 31));
365 public virtual int IndexOfValue (object value)
370 for (int i = 0; i < inUse; i ++) {
371 Slot current = this.table [i];
373 if (Equals (value, current.value))
381 public virtual bool ContainsKey (object key)
384 throw new ArgumentNullException();
387 return Contains (key);
388 } catch (Exception) {
389 throw new InvalidOperationException();
394 public virtual bool ContainsValue (object value)
396 return IndexOfValue (value) >= 0;
400 public virtual object GetByIndex (int index)
402 if (index >= 0 && index < Count)
403 return table [index].value;
406 throw new ArgumentOutOfRangeException("index out of range");
410 public virtual void SetByIndex (int index, object value)
412 if (index >= 0 && index < Count)
413 table [index].value = value;
416 throw new ArgumentOutOfRangeException("index out of range");
420 public virtual object GetKey (int index)
422 if (index >= 0 && index < Count)
423 return table [index].key;
426 throw new ArgumentOutOfRangeException("index out of range");
429 public static SortedList Synchronized (SortedList list)
432 throw new ArgumentNullException (Locale.GetText ("Base list is null."));
434 return new SynchedSortedList (list);
437 public virtual void TrimToSize ()
440 // Trimming an empty SortedList sets the capacity
441 // of the SortedList to the default capacity,
444 Resize (defaultCapacity, false);
446 Resize (Count, true);
455 private void Resize (int n, bool copy)
457 Slot [] table = this.table;
458 Slot [] newTable = new Slot [n];
459 if (copy) Array.Copy (table, 0, newTable, 0, n);
460 this.table = newTable;
464 private void EnsureCapacity (int n, int free)
466 Slot [] table = this.table;
467 Slot [] newTable = null;
469 bool gap = (free >=0 && free < Count);
472 newTable = new Slot [n << 1];
475 if (newTable != null) {
479 Array.Copy (table, 0, newTable, 0, copyLen);
481 copyLen = Count - free;
483 Array.Copy (table, free, newTable, free+1, copyLen);
486 // Just a resizing, copy the entire table.
487 Array.Copy (table, newTable, Count);
489 this.table = newTable;
491 Array.Copy (table, free, table, free+1, Count - free);
496 private void PutImpl (object key, object value, bool overwrite)
499 throw new ArgumentNullException ("null key");
501 Slot [] table = this.table;
506 freeIndx = Find (key);
507 } catch (Exception) {
508 throw new InvalidOperationException();
513 string msg = Locale.GetText ("Key '{0}' already exists in list.", key);
514 throw new ArgumentException (msg);
517 table [freeIndx].value = value;
522 freeIndx = ~freeIndx;
524 if (freeIndx > Capacity + 1)
525 throw new Exception ("SortedList::internal error ("+key+", "+value+") at ["+freeIndx+"]");
528 EnsureCapacity (Count+1, freeIndx);
531 table [freeIndx].key = key;
532 table [freeIndx].value = value;
540 private object GetImpl (object key)
545 return table [i].value;
550 private void InitTable (int capacity, bool forceSize)
552 if (!forceSize && (capacity < defaultCapacity))
553 capacity = defaultCapacity;
554 this.table = new Slot [capacity];
556 this.modificationCount = 0;
559 private void CopyToArray (Array arr, int i,
563 throw new ArgumentNullException ("arr");
565 if (i < 0 || i + this.Count > arr.Length)
566 throw new ArgumentOutOfRangeException ("i");
568 IEnumerator it = new Enumerator (this, mode);
570 while (it.MoveNext ()) {
571 arr.SetValue (it.Current, i++);
576 private int Find (object key)
578 Slot [] table = this.table;
581 if (len == 0) return ~0;
583 IComparer comparer = (this.comparer == null)
590 while (left <= right) {
591 int guess = (left + right) >> 1;
593 int cmp = comparer.Compare (table[guess].key, key);
594 if (cmp == 0) return guess;
596 if (cmp < 0) left = guess+1;
597 else right = guess-1;
610 private sealed class Enumerator : ICloneable, IDictionaryEnumerator, IEnumerator {
612 private SortedList host;
613 private object currentKey;
614 private object currentValue;
619 private EnumeratorMode mode;
623 const string xstr = "SortedList.Enumerator: snapshot out of sync.";
625 public Enumerator (SortedList host, EnumeratorMode mode)
628 stamp = host.modificationCount;
634 public Enumerator (SortedList host)
635 : this (host, EnumeratorMode.ENTRY_MODE)
641 if (host.modificationCount != stamp || invalid)
642 throw new InvalidOperationException (xstr);
649 public bool MoveNext ()
651 if (host.modificationCount != stamp || invalid)
652 throw new InvalidOperationException (xstr);
654 Slot [] table = host.table;
657 Slot entry = table [pos];
659 currentKey = entry.key;
660 currentValue = entry.value;
669 public DictionaryEntry Entry
672 if (invalid || pos >= size || pos == -1)
673 throw new InvalidOperationException (xstr);
675 return new DictionaryEntry (currentKey,
682 if (invalid || pos >= size || pos == -1)
683 throw new InvalidOperationException (xstr);
688 public Object Value {
690 if (invalid || pos >= size || pos == -1)
691 throw new InvalidOperationException (xstr);
696 public Object Current {
698 if (invalid || pos >= size || pos == -1)
699 throw new InvalidOperationException (xstr);
702 case EnumeratorMode.KEY_MODE:
704 case EnumeratorMode.VALUE_MODE:
706 case EnumeratorMode.ENTRY_MODE:
710 throw new NotSupportedException (mode + " is not a supported mode.");
717 public object Clone ()
719 Enumerator e = new Enumerator (host, mode);
723 e.currentKey = currentKey;
724 e.currentValue = currentValue;
731 private class ListKeys : IList, IEnumerable {
733 private SortedList host;
736 public ListKeys (SortedList host)
739 throw new ArgumentNullException ();
748 public virtual int Count {
754 public virtual bool IsSynchronized {
756 return host.IsSynchronized;
760 public virtual Object SyncRoot {
762 return host.SyncRoot;
766 public virtual void CopyTo (Array array, int arrayIndex)
768 host.CopyToArray (array, arrayIndex, EnumeratorMode.KEY_MODE);
776 public virtual bool IsFixedSize {
782 public virtual bool IsReadOnly {
789 public virtual object this [int index] {
791 return host.GetKey (index);
794 throw new NotSupportedException("attempt to modify a key");
798 public virtual int Add (object value)
800 throw new NotSupportedException("IList::Add not supported");
803 public virtual void Clear ()
805 throw new NotSupportedException("IList::Clear not supported");
808 public virtual bool Contains (object key)
810 return host.Contains (key);
814 public virtual int IndexOf (object key)
816 return host.IndexOfKey (key);
820 public virtual void Insert (int index, object value)
822 throw new NotSupportedException("IList::Insert not supported");
826 public virtual void Remove (object value)
828 throw new NotSupportedException("IList::Remove not supported");
832 public virtual void RemoveAt (int index)
834 throw new NotSupportedException("IList::RemoveAt not supported");
842 public virtual IEnumerator GetEnumerator ()
844 return new SortedList.Enumerator (host, EnumeratorMode.KEY_MODE);
851 private class ListValues : IList, IEnumerable {
853 private SortedList host;
856 public ListValues (SortedList host)
859 throw new ArgumentNullException ();
868 public virtual int Count {
874 public virtual bool IsSynchronized {
876 return host.IsSynchronized;
880 public virtual Object SyncRoot {
882 return host.SyncRoot;
886 public virtual void CopyTo (Array array, int arrayIndex)
888 host.CopyToArray (array, arrayIndex, EnumeratorMode.VALUE_MODE);
896 public virtual bool IsFixedSize {
902 public virtual bool IsReadOnly {
909 public virtual object this [int index] {
911 return host.GetByIndex (index);
914 throw new NotSupportedException("This operation is not supported on GetValueList return");
918 public virtual int Add (object value)
920 throw new NotSupportedException("IList::Add not supported");
923 public virtual void Clear ()
925 throw new NotSupportedException("IList::Clear not supported");
928 public virtual bool Contains (object value)
930 return host.ContainsValue (value);
934 public virtual int IndexOf (object value)
936 return host.IndexOfValue (value);
940 public virtual void Insert (int index, object value)
942 throw new NotSupportedException("IList::Insert not supported");
946 public virtual void Remove (object value)
948 throw new NotSupportedException("IList::Remove not supported");
952 public virtual void RemoveAt (int index)
954 throw new NotSupportedException("IList::RemoveAt not supported");
962 public virtual IEnumerator GetEnumerator ()
964 return new SortedList.Enumerator (host, EnumeratorMode.VALUE_MODE);
969 private class SynchedSortedList : SortedList {
971 private SortedList host;
973 public SynchedSortedList (SortedList host)
976 throw new ArgumentNullException ();
980 public override int Capacity {
982 lock (host.SyncRoot) {
983 return host.Capacity;
987 lock (host.SyncRoot) {
988 host.Capacity = value;
995 public override int Count {
1001 public override bool IsSynchronized {
1007 public override Object SyncRoot {
1009 return host.SyncRoot;
1017 public override bool IsFixedSize {
1019 return host.IsFixedSize;
1024 public override bool IsReadOnly {
1026 return host.IsReadOnly;
1030 public override ICollection Keys {
1032 ICollection keys = null;
1033 lock (host.SyncRoot) {
1040 public override ICollection Values {
1042 ICollection vals = null;
1043 lock (host.SyncRoot) {
1052 public override Object this [object key] {
1054 lock (host.SyncRoot) {
1055 return host.GetImpl (key);
1059 lock (host.SyncRoot) {
1060 host.PutImpl (key, value, true);
1069 public override void CopyTo (Array array, int arrayIndex)
1071 lock (host.SyncRoot) {
1072 host.CopyTo (array, arrayIndex);
1079 public override void Add (object key, object value)
1081 lock (host.SyncRoot) {
1082 host.PutImpl (key, value, false);
1086 public override void Clear ()
1088 lock (host.SyncRoot) {
1093 public override bool Contains (object key)
1095 lock (host.SyncRoot) {
1096 return (host.Find (key) >= 0);
1100 public override IDictionaryEnumerator GetEnumerator ()
1102 lock (host.SyncRoot) {
1103 return host.GetEnumerator();
1107 public override void Remove (object key)
1109 lock (host.SyncRoot) {
1116 public override bool ContainsKey (object key)
1118 lock (host.SyncRoot) {
1119 return host.Contains (key);
1123 public override bool ContainsValue (object value)
1125 lock (host.SyncRoot) {
1126 return host.ContainsValue (value);
1133 public override object Clone ()
1135 lock (host.SyncRoot) {
1136 return (host.Clone () as SortedList);
1143 // SortedList overrides
1146 public override Object GetByIndex (int index)
1148 lock (host.SyncRoot) {
1149 return host.GetByIndex (index);
1153 public override Object GetKey (int index)
1155 lock (host.SyncRoot) {
1156 return host.GetKey (index);
1160 public override IList GetKeyList ()
1162 lock (host.SyncRoot) {
1163 return new ListKeys (host);
1168 public override IList GetValueList ()
1170 lock (host.SyncRoot) {
1171 return new ListValues (host);
1175 public override void RemoveAt (int index)
1177 lock (host.SyncRoot) {
1178 host.RemoveAt (index);
1182 public override int IndexOfKey (object key)
1184 lock (host.SyncRoot) {
1185 return host.IndexOfKey (key);
1189 public override int IndexOfValue (Object val)
1191 lock (host.SyncRoot) {
1192 return host.IndexOfValue (val);
1196 public override void SetByIndex (int index, object value)
1198 lock (host.SyncRoot) {
1199 host.SetByIndex (index, value);
1203 public override void TrimToSize()
1205 lock (host.SyncRoot) {
1211 } // SynchedSortedList
1215 } // System.Collections