5 // Jérémie "Garuma" Laval <jeremie.laval@gmail.com>
7 // Copyright (c) 2010 Jérémie "Garuma" Laval
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:
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
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
29 using System.Threading;
30 using System.Collections;
31 using System.Collections.Generic;
32 using System.Collections.Concurrent;
34 namespace System.Linq.Parallel.QueryNodes
36 internal class QueryJoinNode<TFirst, TSecond, TKey, TResult> : QueryMuxNode<TFirst, TSecond, TResult>
40 public readonly bool HasValue;
41 public readonly T Value;
43 public VSlot (T value)
50 Func<TFirst, TKey> firstKeySelector;
51 Func<TSecond, TKey> secondKeySelector;
52 Func<TFirst, TSecond, TResult> resultSelector;
53 IEqualityComparer<TKey> comparer;
55 internal QueryJoinNode (QueryBaseNode<TFirst> first,
56 QueryBaseNode<TSecond> second,
57 Func<TFirst, TKey> firstKeySelector,
58 Func<TSecond, TKey> secondKeySelector,
59 Func<TFirst, TSecond, TResult> resultSelector,
60 IEqualityComparer<TKey> comparer) : base (first, second)
62 this.firstKeySelector = firstKeySelector;
63 this.secondKeySelector = secondKeySelector;
64 this.resultSelector = resultSelector;
65 this.comparer = comparer;
68 internal override IEnumerable<TResult> GetSequential ()
70 return Parent.GetSequential ().Join (Second.GetSequential (),
77 internal override IList<IEnumerable<TResult>> GetEnumerables (QueryOptions options)
79 var first = Parent.GetEnumerables (options);
80 var second = Second.GetEnumerables (options);
82 if (first.Count != second.Count)
83 throw new InvalidOperationException ("Internal size mismatch");
85 var store = new TemporaryArea<TKey, Tuple<VSlot<TFirst>, VSlot<TSecond>>> (comparer);
88 .Select ((f, i) => GetEnumerable (f, second[i], store, firstKeySelector, secondKeySelector, resultSelector))
93 internal override IList<IEnumerable<KeyValuePair<long, TResult>>> GetOrderedEnumerables (QueryOptions options)
95 var first = Parent.GetOrderedEnumerables (options);
96 var second = Second.GetOrderedEnumerables (options);
98 if (first.Count != second.Count)
99 throw new InvalidOperationException ("Internal size mismatch");
101 var store = new TemporaryArea<TKey, Tuple<VSlot<KeyValuePair<long, TFirst>>, VSlot<KeyValuePair<long, TSecond>>>> (comparer);
104 .Select ((f, i) => GetEnumerable<KeyValuePair<long, TFirst>, KeyValuePair<long, TSecond>, KeyValuePair<long, TResult>> (f,
107 (e) => firstKeySelector (e.Value),
108 (e) => secondKeySelector (e.Value),
109 (e1, e2) => new KeyValuePair<long, TResult> (e1.Key, resultSelector (e1.Value, e2.Value))))
113 IEnumerable<T> GetEnumerable<U, V, T> (IEnumerable<U> first,
114 IEnumerable<V> second,
115 TemporaryArea<TKey, Tuple<VSlot<U>, VSlot<V>>> store,
116 Func<U, TKey> fKeySelect,
117 Func<V, TKey> sKeySelect,
118 Func<U, V, T> resultor)
120 IEnumerator<U> eFirst = first.GetEnumerator ();
121 IEnumerator<V> eSecond = second.GetEnumerator ();
124 while (eFirst.MoveNext ()) {
125 if (!eSecond.MoveNext ())
128 U e1 = eFirst.Current;
129 V e2 = eSecond.Current;
131 TKey key1 = fKeySelect (e1);
132 TKey key2 = sKeySelect (e2);
134 if (comparer.Equals (key1, key2)) {
135 yield return resultor (e1, e2);
139 Tuple<VSlot<U>, VSlot<V>> kvp;
142 if (store.TryRemove (key1, out kvp) && kvp.Item2.HasValue) {
143 yield return resultor (e1, kvp.Item2.Value);
146 } while (!store.TryAdd (key1, Tuple.Create (new VSlot<U> (e1), new VSlot<V> ())));
149 if (store.TryRemove (key2, out kvp) && kvp.Item1.HasValue) {
150 yield return resultor (kvp.Item1.Value, e2);
153 } while (!store.TryAdd (key2, Tuple.Create (new VSlot<U> (), new VSlot<V> (e2))));