// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Diagnostics; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.Contracts; using System.Runtime.InteropServices; namespace System.Runtime.InteropServices.WindowsRuntime { /// /// This is a constant map aimed to efficiently support a Split operation (map decomposition). /// A Split operation returns two non-overlapping, non-empty views of the existing map (or both /// values are set to NULL). The two views returned should contain roughly the same number of elements. /// This map is backed by a sorted array. Thus, split operations are O(1) and enumerations are fast; /// however, look-up in the map are O(log n). /// /// Type of objects that act as keys. /// Type of objects that act as entries / values. [Serializable] [DebuggerDisplay("Count = {Count}")] internal sealed class ConstantSplittableMap : IMapView { private class KeyValuePairComparator : IComparer> { private static readonly IComparer keyComparator = Comparer.Default; public Int32 Compare(KeyValuePair x, KeyValuePair y) { return keyComparator.Compare(x.Key, y.Key); } } // private class KeyValuePairComparator private static readonly KeyValuePairComparator keyValuePairComparator = new KeyValuePairComparator(); private readonly KeyValuePair[] items; private readonly int firstItemIndex; private readonly int lastItemIndex; internal ConstantSplittableMap(IReadOnlyDictionary data) { if (data == null) throw new ArgumentNullException("data"); Contract.EndContractBlock(); this.firstItemIndex = 0; this.lastItemIndex = data.Count - 1; this.items = CreateKeyValueArray(data.Count, data.GetEnumerator()); } internal ConstantSplittableMap(IMapView data) { if (data == null) throw new ArgumentNullException("data"); if (((UInt32)Int32.MaxValue) < data.Size) { Exception e = new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CollectionBackingDictionaryTooLarge")); e.SetErrorCode(__HResults.E_BOUNDS); throw e; } int size = (int)data.Size; this.firstItemIndex = 0; this.lastItemIndex = size - 1; this.items = CreateKeyValueArray(size, data.GetEnumerator()); } private ConstantSplittableMap(KeyValuePair[] items, Int32 firstItemIndex, Int32 lastItemIndex) { this.items = items; this.firstItemIndex = firstItemIndex; this.lastItemIndex = lastItemIndex; } private KeyValuePair[] CreateKeyValueArray(Int32 count, IEnumerator> data) { KeyValuePair[] kvArray = new KeyValuePair[count]; Int32 i = 0; while (data.MoveNext()) kvArray[i++] = data.Current; Array.Sort(kvArray, keyValuePairComparator); return kvArray; } private KeyValuePair[] CreateKeyValueArray(Int32 count, IEnumerator> data) { KeyValuePair[] kvArray = new KeyValuePair[count]; Int32 i = 0; while (data.MoveNext()) { IKeyValuePair current = data.Current; kvArray[i++] = new KeyValuePair(current.Key, current.Value); } Array.Sort(kvArray, keyValuePairComparator); return kvArray; } public int Count { get { return lastItemIndex - firstItemIndex + 1; } } // [CLSCompliant(false)] public UInt32 Size { get { return (UInt32)(lastItemIndex - firstItemIndex + 1); } } public TValue Lookup(TKey key) { TValue value; bool found = TryGetValue(key, out value); if (!found) { Exception e = new KeyNotFoundException(Environment.GetResourceString("Arg_KeyNotFound")); e.SetErrorCode(__HResults.E_BOUNDS); throw e; } return value; } public bool HasKey(TKey key) { TValue value; bool hasKey = TryGetValue(key, out value); return hasKey; } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable>)this).GetEnumerator(); } public IIterator> First() { return new EnumeratorToIteratorAdapter>(GetEnumerator()); } public IEnumerator> GetEnumerator() { return new IKeyValuePairEnumerator(items, firstItemIndex, lastItemIndex); } public void Split(out IMapView firstPartition, out IMapView secondPartition) { if (Count < 2) { firstPartition = null; secondPartition = null; return; } int pivot = (Int32)(((Int64)firstItemIndex + (Int64)lastItemIndex) / (Int64)2); firstPartition = new ConstantSplittableMap(items, firstItemIndex, pivot); secondPartition = new ConstantSplittableMap(items, pivot + 1, lastItemIndex); } #region IReadOnlyDictionary members public bool ContainsKey(TKey key) { KeyValuePair searchKey = new KeyValuePair(key, default(TValue)); int index = Array.BinarySearch(items, firstItemIndex, Count, searchKey, keyValuePairComparator); return index >= 0; } public bool TryGetValue(TKey key, out TValue value) { KeyValuePair searchKey = new KeyValuePair(key, default(TValue)); int index = Array.BinarySearch(items, firstItemIndex, Count, searchKey, keyValuePairComparator); if (index < 0) { value = default(TValue); return false; } value = items[index].Value; return true; } public TValue this[TKey key] { get { return Lookup(key); } } public IEnumerable Keys { get { throw new NotImplementedException("NYI"); } } public IEnumerable Values { get { throw new NotImplementedException("NYI"); } } #endregion IReadOnlyDictionary members #region IKeyValuePair Enumerator [Serializable] internal struct IKeyValuePairEnumerator : IEnumerator> { private KeyValuePair[] _array; private int _start; private int _end; private int _current; internal IKeyValuePairEnumerator(KeyValuePair[] items, int first, int end) { Contract.Requires(items != null); Contract.Requires(first >= 0); Contract.Requires(end >= 0); Contract.Requires(first < items.Length); Contract.Requires(end < items.Length); _array = items; _start = first; _end = end; _current = _start - 1; } public bool MoveNext() { if (_current < _end) { _current++; return true; } return false; } public IKeyValuePair Current { get { if (_current < _start) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted)); if (_current > _end) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded)); return new CLRIKeyValuePairImpl(ref _array[_current]); } } Object IEnumerator.Current { get { return Current; } } void IEnumerator.Reset() { _current = _start - 1; } public void Dispose() { } } #endregion IKeyValuePair Enumerator } // internal ConstantSplittableMap } // namespace System.Runtime.InteropServices.WindowsRuntime