Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

208 lines
6.9 KiB
C#

//
// QueryConcatNode.cs
//
// Author:
// Jérémie "Garuma" Laval <jeremie.laval@gmail.com>
//
// 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;
namespace System.Linq.Parallel.QueryNodes
{
internal class QuerySelectManyNode<TSource, TCollection, TResult> : QueryStreamNode<TResult, TSource>
{
Func<TSource, IEnumerable<TCollection>> collectionSelector;
Func<TSource, int, IEnumerable<TCollection>> collectionSelectorIndexed;
Func<TSource, TCollection, TResult> resultSelector;
internal QuerySelectManyNode (QueryBaseNode<TSource> parent,
Func<TSource, int, IEnumerable<TCollection>> collectionSelectorIndexed,
Func<TSource, TCollection, TResult> resultSelector)
: base (parent, true)
{
this.collectionSelectorIndexed = collectionSelectorIndexed;
this.resultSelector = resultSelector;
}
internal QuerySelectManyNode (QueryBaseNode<TSource> parent,
Func<TSource, IEnumerable<TCollection>> collectionSelector,
Func<TSource, TCollection, TResult> resultSelector)
: base (parent, false)
{
this.collectionSelector = collectionSelector;
this.resultSelector = resultSelector;
}
internal override IEnumerable<TResult> GetSequential ()
{
IEnumerable<TSource> source = Parent.GetSequential ();
return IsIndexed ?
source.SelectMany (collectionSelectorIndexed, resultSelector) :
source.SelectMany (collectionSelector, resultSelector);
}
internal override IList<IEnumerable<TResult>> GetEnumerablesIndexed (QueryOptions options)
{
return Parent.GetOrderedEnumerables (options)
.Select ((i) => GetEnumerableInternal (i,
(kv) => collectionSelectorIndexed (kv.Value, (int)kv.Key),
(e, c) => resultSelector (e.Value, c)))
.ToList ();
}
internal override IList<IEnumerable<TResult>> GetEnumerablesNonIndexed (QueryOptions options)
{
return Parent.GetEnumerables (options)
.Select ((i) => GetEnumerableInternal (i,
collectionSelector,
(e, c) => resultSelector (e, c)))
.ToList ();
}
internal override IList<IEnumerable<KeyValuePair<long, TResult>>> GetOrderedEnumerables (QueryOptions options)
{
var source = Parent.GetOrderedEnumerables (options);
var sizeRequests = new SizeRequest[source.Count];
long deviation = 0;
Barrier barrier = new Barrier (source.Count, delegate (Barrier b) {
Array.Sort (sizeRequests, KeyComparator);
for (int i = 0; i < b.ParticipantCount; ++i) {
if (sizeRequests[i].Key == -1)
continue;
sizeRequests[i].Key = deviation;
deviation += sizeRequests[i].Size;
}
});
return source
.Select ((i, ind) => GetOrderedEnumerableInternal (i, sizeRequests, ind, barrier))
.ToList ();
}
IEnumerable<TResult> GetEnumerableInternal<T> (IEnumerable<T> source,
Func<T, IEnumerable<TCollection>> collectionner,
Func<T, TCollection, TResult> packer)
{
foreach (T element in source)
foreach (TCollection item in collectionner (element))
yield return packer (element, item);
}
IEnumerable<KeyValuePair<long, TResult>> GetOrderedEnumerableInternal (IEnumerable<KeyValuePair<long, TSource>> source,
SizeRequest[] sizeRequests,
int index,
Barrier barrier)
{
IEnumerator<KeyValuePair<long, TSource>> enumerator = source.GetEnumerator ();
bool isIndexed = IsIndexed;
try {
while (true) {
KeyValuePair<long, TSource> element;
IEnumerable<TCollection> collection;
if (enumerator.MoveNext ()) {
element = enumerator.Current;
collection = isIndexed ?
collectionSelectorIndexed (element.Value, (int)element.Key) :
collectionSelector (element.Value);
var count = GetCount (ref collection);
sizeRequests[index].Update (element.Key, count, collection, element.Value);
}
barrier.SignalAndWait ();
long i = sizeRequests[index].Key;
collection = sizeRequests[index].Collection;
var elementValue = sizeRequests[index].ElementValue;
sizeRequests[index].Clear ();
if (i == -1)
break;
foreach (TCollection item in collection)
yield return new KeyValuePair<long, TResult> (i++, resultSelector (elementValue, item));
}
} finally {
barrier.RemoveParticipant ();
enumerator.Dispose ();
}
}
/* If getting Count is a O(1) operation (i.e. actual is a ICollection) then return it immediatly
* if not process the IEnumerable into a List and return the Count from that (i.e. enumerable
* processing will only happen once in case of e.g. a Linq query)
*/
static int GetCount<T> (ref IEnumerable<T> actual)
{
ICollection coll = actual as ICollection;
if (coll != null)
return coll.Count;
var foo = actual.ToList ();
actual = foo;
return foo.Count;
}
static int KeyComparator (SizeRequest e1, SizeRequest e2)
{
return e1.Key.CompareTo (e2.Key);
}
struct SizeRequest
{
public long Key;
public int Size;
public IEnumerable<TCollection> Collection;
public TSource ElementValue;
public void Update (long k, int s, IEnumerable<TCollection> c, TSource ev)
{
Key = k;
Size = s;
Collection = c;
ElementValue = ev;
}
public void Clear ()
{
Key = -1;
Size = 0;
Collection = null;
ElementValue = default (TSource);
}
}
}
}
#endif