// // QueryJoinNode.cs // // Author: // Jérémie "Garuma" Laval // // Copyright (c) 2010 Jérémie "Garuma" Laval // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #if NET_4_0 using System; using System.Threading; using System.Collections; using System.Collections.Generic; using System.Collections.Concurrent; namespace System.Linq.Parallel.QueryNodes { internal class QueryJoinNode : QueryMuxNode { struct VSlot { public readonly bool HasValue; public readonly T Value; public VSlot (T value) { HasValue = true; Value = value; } } Func firstKeySelector; Func secondKeySelector; Func resultSelector; IEqualityComparer comparer; internal QueryJoinNode (QueryBaseNode first, QueryBaseNode second, Func firstKeySelector, Func secondKeySelector, Func resultSelector, IEqualityComparer comparer) : base (first, second) { this.firstKeySelector = firstKeySelector; this.secondKeySelector = secondKeySelector; this.resultSelector = resultSelector; this.comparer = comparer; } internal override IEnumerable GetSequential () { return Parent.GetSequential ().Join (Second.GetSequential (), firstKeySelector, secondKeySelector, resultSelector, comparer); } internal override IList> GetEnumerables (QueryOptions options) { var first = Parent.GetEnumerables (options); var second = Second.GetEnumerables (options); if (first.Count != second.Count) throw new InvalidOperationException ("Internal size mismatch"); var store = new ConcurrentDictionary, VSlot>> (comparer); return first .Select ((f, i) => GetEnumerable (f, second[i], store, firstKeySelector, secondKeySelector, resultSelector)) .ToList (); } internal override IList>> GetOrderedEnumerables (QueryOptions options) { var first = Parent.GetOrderedEnumerables (options); var second = Second.GetOrderedEnumerables (options); if (first.Count != second.Count) throw new InvalidOperationException ("Internal size mismatch"); var store = new ConcurrentDictionary>, VSlot>>> (comparer); return first .Select ((f, i) => GetEnumerable, KeyValuePair, KeyValuePair> (f, second[i], store, (e) => firstKeySelector (e.Value), (e) => secondKeySelector (e.Value), (e1, e2) => new KeyValuePair (e1.Key, resultSelector (e1.Value, e2.Value)))) .ToList (); } IEnumerable GetEnumerable (IEnumerable first, IEnumerable second, ConcurrentDictionary, VSlot>> store, Func fKeySelect, Func sKeySelect, Func resultor) { IEnumerator eFirst = first.GetEnumerator (); IEnumerator eSecond = second.GetEnumerator (); try { while (eFirst.MoveNext ()) { if (!eSecond.MoveNext ()) yield break; U e1 = eFirst.Current; V e2 = eSecond.Current; TKey key1 = fKeySelect (e1); TKey key2 = sKeySelect (e2); if (comparer.Equals (key1, key2)) { yield return resultor (e1, e2); continue; } Tuple, VSlot> kvp; do { if (store.TryRemove (key1, out kvp) && kvp.Item2.HasValue) { yield return resultor (e1, kvp.Item2.Value); break; } } while (!store.TryAdd (key1, Tuple.Create (new VSlot (e1), new VSlot ()))); do { if (store.TryRemove (key2, out kvp) && kvp.Item1.HasValue) { yield return resultor (kvp.Item1.Value, e2); break; } } while (!store.TryAdd (key2, Tuple.Create (new VSlot (), new VSlot (e2)))); } } finally { eFirst.Dispose (); eSecond.Dispose (); } } } } #endif