summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Collections/Concurrent/OrderablePartitioner.cs
blob: 33e3c88e9a6deab9c5d06479c53d707a99bb7b1e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
// 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;
using System.Collections.Generic;
using System.Security.Permissions;
using System.Threading;

namespace System.Collections.Concurrent
{

    /// <summary>
    /// Represents a particular manner of splitting an orderable data source into multiple partitions.
    /// </summary>
    /// <typeparam name="TSource">Type of the elements in the collection.</typeparam>
    /// <remarks>
    /// <para>
    /// Each element in each partition has an integer index associated with it, which determines the relative
    /// order of that element against elements in other partitions.
    /// </para>
    /// <para>
    /// Inheritors of <see cref="OrderablePartitioner{TSource}"/> must adhere to the following rules:
    /// <ol>
    /// <li>All indices must be unique, such that there may not be duplicate indices. If all indices are not
    /// unique, the output ordering may be scrambled.</li>
    /// <li>All indices must be non-negative. If any indices are negative, consumers of the implementation
    /// may throw exceptions.</li>
    /// <li><see cref="GetPartitions"/> and <see cref="GetOrderablePartitions"/> should throw a
    /// <see cref="T:System.ArgumentOutOfRangeException"/> if the requested partition count is less than or
    /// equal to zero.</li>
    /// <li><see cref="GetPartitions"/> and <see cref="GetOrderablePartitions"/> should always return a number
    /// of enumerables equal to the requested partition count. If the partitioner runs out of data and cannot
    /// create as many partitions as requested, an empty enumerator should be returned for each of the
    /// remaining partitions. If this rule is not followed, consumers of the implementation may throw a <see
    /// cref="T:System.InvalidOperationException"/>.</li>
    /// <li><see cref="GetPartitions"/>, <see cref="GetOrderablePartitions"/>,
    /// <see cref="GetDynamicPartitions"/>, and <see cref="GetOrderableDynamicPartitions"/>
    /// should never return null. If null is returned, a consumer of the implementation may throw a
    /// <see cref="T:System.InvalidOperationException"/>.</li>
    /// <li><see cref="GetPartitions"/>, <see cref="GetOrderablePartitions"/>,
    /// <see cref="GetDynamicPartitions"/>, and <see cref="GetOrderableDynamicPartitions"/>
    /// should always return partitions that can fully and uniquely enumerate the input data source. All of
    /// the data and only the data contained in the input source should be enumerated, with no duplication
    /// that was not already in the input, unless specifically required by the particular partitioner's
    /// design. If this is not followed, the output ordering may be scrambled.</li>
    /// <li>If <see cref="KeysOrderedInEachPartition"/> returns true, each partition must return elements
    /// with increasing key indices.</li>
    /// <li>If <see cref="KeysOrderedAcrossPartitions"/> returns true, all the keys in partition numbered N
    /// must be larger than all the keys in partition numbered N-1.</li>
    /// <li>If <see cref="KeysNormalized"/> returns true, all indices must be monotonically increasing from
    /// 0, though not necessarily within a single partition.</li>
    /// </ol>
    /// </para>
    /// </remarks>
    public abstract class OrderablePartitioner<TSource> : Partitioner<TSource>
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="OrderablePartitioner{TSource}"/> class with the
        /// specified constraints on the index keys.
        /// </summary>
        /// <param name="keysOrderedInEachPartition">
        /// Indicates whether the elements in each partition are yielded in the order of
        /// increasing keys.
        /// </param>
        /// <param name="keysOrderedAcrossPartitions">
        /// Indicates whether elements in an earlier partition always come before
        /// elements in a later partition. If true, each element in partition 0 has a smaller order key than
        /// any element in partition 1, each element in partition 1 has a smaller order key than any element
        /// in partition 2, and so on.
        /// </param>
        /// <param name="keysNormalized">
        /// Indicates whether keys are normalized. If true, all order keys are distinct
        /// integers in the range [0 .. numberOfElements-1]. If false, order keys must still be dictinct, but
        /// only their relative order is considered, not their absolute values.
        /// </param>
        protected OrderablePartitioner(bool keysOrderedInEachPartition, bool keysOrderedAcrossPartitions, bool keysNormalized)
        {
            KeysOrderedInEachPartition = keysOrderedInEachPartition;
            KeysOrderedAcrossPartitions = keysOrderedAcrossPartitions;
            KeysNormalized = keysNormalized;
        }

        /// <summary>
        /// Partitions the underlying collection into the specified number of orderable partitions.
        /// </summary>
        /// <remarks>
        /// Each partition is represented as an enumerator over key-value pairs.
        /// The value of the pair is the element itself, and the key is an integer which determines
        /// the relative ordering of this element against other elements in the data source.
        /// </remarks>
        /// <param name="partitionCount">The number of partitions to create.</param>
        /// <returns>A list containing <paramref name="partitionCount"/> enumerators.</returns>
        public abstract IList<IEnumerator<KeyValuePair<long, TSource>>> GetOrderablePartitions(int partitionCount);

        /// <summary>
        /// Creates an object that can partition the underlying collection into a variable number of
        /// partitions.
        /// </summary>
        /// <remarks>
        /// <para>
        /// The returned object implements the <see
        /// cref="T:System.Collections.Generic.IEnumerable{TSource}"/> interface. Calling <see
        /// cref="System.Collections.Generic.IEnumerable{TSource}.GetEnumerator">GetEnumerator</see> on the
        /// object creates another partition over the sequence.
        /// </para>
        /// <para>
        /// Each partition is represented as an enumerator over key-value pairs. The value in the pair is the element
        /// itself, and the key is an integer which determines the relative ordering of this element against
        /// other elements.
        /// </para>
        /// <para>
        /// The <see cref="GetOrderableDynamicPartitions"/> method is only supported if the <see
        /// cref="System.Collections.Concurrent.Partitioner{TSource}.SupportsDynamicPartitions">SupportsDynamicPartitions</see>
        /// property returns true.
        /// </para>
        /// </remarks>
        /// <returns>An object that can create partitions over the underlying data source.</returns>
        /// <exception cref="NotSupportedException">Dynamic partitioning is not supported by this
        /// partitioner.</exception>
        public virtual IEnumerable<KeyValuePair<long, TSource>> GetOrderableDynamicPartitions()
        {
            throw new NotSupportedException(Environment.GetResourceString("Partitioner_DynamicPartitionsNotSupported"));
        }

        /// <summary>
        /// Gets whether elements in each partition are yielded in the order of increasing keys.
        /// </summary>
        public bool KeysOrderedInEachPartition { get; private set; }

        /// <summary>
        /// Gets whether elements in an earlier partition always come before elements in a later partition.
        /// </summary>
        /// <remarks>
        /// If <see cref="KeysOrderedAcrossPartitions"/> returns true, each element in partition 0 has a
        /// smaller order key than any element in partition 1, each element in partition 1 has a smaller
        /// order key than any element in partition 2, and so on.
        /// </remarks>
        public bool KeysOrderedAcrossPartitions { get; private set; }

        /// <summary>
        /// Gets whether order keys are normalized.
        /// </summary>
        /// <remarks>
        /// If <see cref="KeysNormalized"/> returns true, all order keys are distinct integers in the range
        /// [0 .. numberOfElements-1]. If the property returns false, order keys must still be dictinct, but
        /// only their relative order is considered, not their absolute values.
        /// </remarks>
        public bool KeysNormalized { get; private set; }

        /// <summary>
        /// Partitions the underlying collection into the given number of ordered partitions.
        /// </summary>
        /// <remarks>
        /// The default implementation provides the same behavior as <see cref="GetOrderablePartitions"/> except
        /// that the returned set of partitions does not provide the keys for the elements.
        /// </remarks>
        /// <param name="partitionCount">The number of partitions to create.</param>
        /// <returns>A list containing <paramref name="partitionCount"/> enumerators.</returns>
        public override IList<IEnumerator<TSource>> GetPartitions(int partitionCount)
        {
            IList<IEnumerator<KeyValuePair<long, TSource>>> orderablePartitions = GetOrderablePartitions(partitionCount);

            if (orderablePartitions.Count != partitionCount)
            {
                throw new InvalidOperationException("OrderablePartitioner_GetPartitions_WrongNumberOfPartitions");
            }

            IEnumerator<TSource>[] partitions = new IEnumerator<TSource>[partitionCount];
            for (int i = 0; i < partitionCount; i++)
            {
                partitions[i] = new EnumeratorDropIndices(orderablePartitions[i]);
            }
            return partitions;
        }

        /// <summary>
        /// Creates an object that can partition the underlying collection into a variable number of
        /// partitions.
        /// </summary>
        /// <remarks>
        /// <para>
        /// The returned object implements the <see
        /// cref="T:System.Collections.Generic.IEnumerable{TSource}"/> interface. Calling <see
        /// cref="System.Collections.Generic.IEnumerable{TSource}.GetEnumerator">GetEnumerator</see> on the
        /// object creates another partition over the sequence.
        /// </para>
        /// <para>
        /// The default implementation provides the same behavior as <see cref="GetOrderableDynamicPartitions"/> except
        /// that the returned set of partitions does not provide the keys for the elements.
        /// </para>
        /// <para>
        /// The <see cref="GetDynamicPartitions"/> method is only supported if the <see
        /// cref="System.Collections.Concurrent.Partitioner{TSource}.SupportsDynamicPartitions"/>
        /// property returns true.
        /// </para>
        /// </remarks>
        /// <returns>An object that can create partitions over the underlying data source.</returns>
        /// <exception cref="NotSupportedException">Dynamic partitioning is not supported by this
        /// partitioner.</exception>
        public override IEnumerable<TSource> GetDynamicPartitions()
        {
            IEnumerable<KeyValuePair<long, TSource>> orderablePartitions = GetOrderableDynamicPartitions();
            return new EnumerableDropIndices(orderablePartitions);
        }

        /// <summary>
        /// Converts an enumerable over key-value pairs to an enumerable over values.
        /// </summary>
        private class EnumerableDropIndices : IEnumerable<TSource>, IDisposable
        {
            private readonly IEnumerable<KeyValuePair<long, TSource>> m_source;
            public EnumerableDropIndices(IEnumerable<KeyValuePair<long, TSource>> source)
            {
                m_source = source;
            }
            public IEnumerator<TSource> GetEnumerator()
            {
                return new EnumeratorDropIndices(m_source.GetEnumerator());
            }
            IEnumerator IEnumerable.GetEnumerator()
            {
                return ((EnumerableDropIndices)this).GetEnumerator();
            }
            public void Dispose()
            {
                IDisposable d = m_source as IDisposable;
                if (d != null)
                {
                    d.Dispose();
                }
            }
        }

        private class EnumeratorDropIndices : IEnumerator<TSource>
        {
            private readonly IEnumerator<KeyValuePair<long, TSource>> m_source;
            public EnumeratorDropIndices(IEnumerator<KeyValuePair<long, TSource>> source)
            {
                m_source = source;
            }
            public bool MoveNext()
            {
                return m_source.MoveNext();
            }
            public TSource Current
            {
                get
                {
                    return m_source.Current.Value;
                }
            }
            Object IEnumerator.Current
            {
                get
                {
                    return ((EnumeratorDropIndices)this).Current;
                }
            }
            public void Dispose()
            {
                m_source.Dispose();
            }
            public void Reset()
            {
                m_source.Reset();
            }
        }

    }

}