Merge remote branch 'upstream/master'
[mono.git] / mcs / class / corlib / System.Collections.Concurrent.Partitioners / EnumerablePartitioner.cs
1 // 
2 // EnumerablePartitioner.cs
3 //  
4 // Author:
5 //       Jérémie "Garuma" Laval <jeremie.laval@gmail.com>
6 // 
7 // Copyright (c) 2009 Jérémie "Garuma" Laval
8 // 
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 // 
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
18 // 
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 // THE SOFTWARE.
26
27 #if NET_4_0
28
29 using System;
30 using System.Threading;
31 using System.Threading.Tasks;
32 using System.Collections.Generic;
33
34 namespace System.Collections.Concurrent.Partitioners
35 {
36         // Represent a chunk partitioner
37         internal class EnumerablePartitioner<T> : OrderablePartitioner<T>
38         {
39                 IEnumerable<T> source;
40                 
41                 const int InitialPartitionSize = 1;
42                 const int PartitionMultiplier = 2;
43                 
44                 int initialPartitionSize;
45                 int partitionMultiplier;
46
47                 public EnumerablePartitioner (IEnumerable<T> source)
48                         : this (source, InitialPartitionSize, PartitionMultiplier)
49                 {
50
51                 }
52                 
53                 // This is used to get striped partitionning (for Take and Skip for instance
54                 public EnumerablePartitioner (IEnumerable<T> source, int initialPartitionSize, int partitionMultiplier)
55                          : base (true, false, true)
56                 {
57                         this.source = source;
58                         this.initialPartitionSize = initialPartitionSize;
59                         this.partitionMultiplier = partitionMultiplier;
60                 }
61                 
62                 public override IList<IEnumerator<KeyValuePair<long, T>>> GetOrderablePartitions (int partitionCount)
63                 {
64                         if (partitionCount <= 0)
65                                 throw new ArgumentOutOfRangeException ("partitionCount");
66                         
67                         IEnumerator<KeyValuePair<long, T>>[] enumerators
68                                 = new IEnumerator<KeyValuePair<long, T>>[partitionCount];
69
70                         PartitionerState state = new PartitionerState ();
71                         IEnumerator<T> src = source.GetEnumerator ();
72                         bool isSimple = initialPartitionSize == 1 && partitionMultiplier == 1;
73
74                         for (int i = 0; i < enumerators.Length; i++) {
75                                 enumerators[i] = isSimple ? GetPartitionEnumeratorSimple (src, state, i == enumerators.Length - 1) : GetPartitionEnumerator (src, state);
76                         }
77                         
78                         return enumerators;
79                 }
80
81                 // This partitioner that is simpler than the general case (don't use a list) is called in the case
82                 // of initialPartitionSize == partitionMultiplier == 1
83                 IEnumerator<KeyValuePair<long, T>> GetPartitionEnumeratorSimple (IEnumerator<T> src,
84                                                                                  PartitionerState state,
85                                                                                  bool last)
86                 {
87                         long index = -1;
88                         var value = default (T);
89
90                         try {
91                                 do {
92                                         lock (state.SyncLock) {
93                                                 if (!src.MoveNext ())
94                                                         break;
95
96                                                 index = state.Index++;
97                                                 value = src.Current;
98                                         }
99
100                                         yield return new KeyValuePair<long, T> (index, value);
101                                 } while (true);
102                         } finally {
103                                 if (last)
104                                         src.Dispose ();
105                         }
106                 }
107                 
108                 IEnumerator<KeyValuePair<long, T>> GetPartitionEnumerator (IEnumerator<T> src, PartitionerState state)
109                 {
110                         int count = initialPartitionSize;
111                         List<T> list = new List<T> ();
112                         
113                         while (true) {
114                                 list.Clear ();
115                                 long ind = -1;
116                                 
117                                 lock (state.SyncLock) {
118                                         ind = state.Index;
119                                         
120                                         for (int i = 0; i < count; i++) {
121                                                 if (!src.MoveNext ()) {
122                                                         if (list.Count == 0)
123                                                                 yield break;
124                                                         else
125                                                                 break;
126                                                 }
127                                                 
128                                                 list.Add (src.Current);
129                                                 state.Index++;
130                                         }                                       
131                                 }
132                                 
133                                 for (int i = 0; i < list.Count; i++)
134                                         yield return new KeyValuePair<long, T> (ind + i, list[i]);
135                                 
136                                 count *= partitionMultiplier;
137                         }
138                 }
139
140                 class PartitionerState
141                 {
142                         public long Index = 0;
143                         public readonly object SyncLock = new object ();
144                 }
145         }
146 }
147 #endif