Skip to content

Commit

Permalink
Merge pull request dotnet/corefx#15389 from jamesqo/change.linking
Browse files Browse the repository at this point in the history
Reduce linked node size in Enumerable.Append/Prepend/Union

Commit migrated from dotnet/corefx@d0001fe
  • Loading branch information
VSadov committed Feb 3, 2017
2 parents eb10a4a + 62cf97e commit 0958723
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 163 deletions.
1 change: 1 addition & 0 deletions src/libraries/System.Linq/src/System.Linq.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
<Compile Include="System\Linq\SequenceEqual.cs" />
<Compile Include="System\Linq\Set.cs" />
<Compile Include="System\Linq\Single.cs" />
<Compile Include="System\Linq\SingleLinkedNode.cs" />
<Compile Include="System\Linq\Skip.cs" />
<Compile Include="System\Linq\Sum.cs" />
<Compile Include="System\Linq\Take.cs" />
Expand Down
131 changes: 27 additions & 104 deletions src/libraries/System.Linq/src/System/Linq/AppendPrepend.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public static IEnumerable<TSource> Append<TSource>(this IEnumerable<TSource> sou
return appendable.Append(element);
}

return new AppendPrepend1Iterator<TSource>(source, element, true);
return new AppendPrepend1Iterator<TSource>(source, element, appending: true);
}

public static IEnumerable<TSource> Prepend<TSource>(this IEnumerable<TSource> source, TSource element)
Expand All @@ -38,7 +38,7 @@ public static IEnumerable<TSource> Prepend<TSource>(this IEnumerable<TSource> so
return appendable.Prepend(element);
}

return new AppendPrepend1Iterator<TSource>(source, element, false);
return new AppendPrepend1Iterator<TSource>(source, element, appending: false);
}

/// <summary>
Expand Down Expand Up @@ -154,23 +154,23 @@ public override AppendPrependIterator<TSource> Append(TSource item)
{
if (_appending)
{
return new AppendPrependN<TSource>(_source, null, new SingleLinkedNode<TSource>(_item, item));
return new AppendPrependN<TSource>(_source, null, new SingleLinkedNode<TSource>(_item).Add(item), prependCount: 0, appendCount: 2);
}
else
{
return new AppendPrependN<TSource>(_source, new SingleLinkedNode<TSource>(_item), new SingleLinkedNode<TSource>(item));
return new AppendPrependN<TSource>(_source, new SingleLinkedNode<TSource>(_item), new SingleLinkedNode<TSource>(item), prependCount: 1, appendCount: 1);
}
}

public override AppendPrependIterator<TSource> Prepend(TSource item)
{
if (_appending)
{
return new AppendPrependN<TSource>(_source, new SingleLinkedNode<TSource>(item), new SingleLinkedNode<TSource>(_item));
return new AppendPrependN<TSource>(_source, new SingleLinkedNode<TSource>(item), new SingleLinkedNode<TSource>(_item), prependCount: 1, appendCount: 1);
}
else
{
return new AppendPrependN<TSource>(_source, new SingleLinkedNode<TSource>(_item, item), null);
return new AppendPrependN<TSource>(_source, new SingleLinkedNode<TSource>(_item).Add(item), null, prependCount: 2, appendCount: 0);
}
}

Expand Down Expand Up @@ -256,94 +256,6 @@ public override int GetCount(bool onlyIfCheap)
}
}

/// <summary>
/// An immutable node in a singly-linked list of items.
/// </summary>
/// <typeparam name="TSource">The type of the node's item.</typeparam>
private sealed class SingleLinkedNode<TSource>
{
/// <summary>
/// Constructs a node linked to the tail node.
/// </summary>
/// <param name="first">The first item, to be placed in the tail node.</param>
/// <param name="second">The second item, to be placed in this node.</param>
public SingleLinkedNode(TSource first, TSource second)
{
Linked = new SingleLinkedNode<TSource>(first);
Item = second;
Count = 2;
}

/// <summary>
/// Constructs a tail node.
/// </summary>
/// <param name="item">The item to place in the tail node.</param>
public SingleLinkedNode(TSource item)
{
Item = item;
Count = 1;
}

/// <summary>
/// Constructs a node linked to the specified node.
/// </summary>
/// <param name="linked">The linked node.</param>
/// <param name="item">The item to place in this node.</param>
private SingleLinkedNode(SingleLinkedNode<TSource> linked, TSource item)
{
Debug.Assert(linked != null);
Linked = linked;
Item = item;
Count = linked.Count + 1;
}

/// <summary>
/// The item held by this node.
/// </summary>
public TSource Item { get; }

/// <summary>
/// The next node in the singly-linked list.
/// </summary>
public SingleLinkedNode<TSource> Linked { get; }

/// <summary>
/// The number of items stored in this and subsequent nodes.
/// </summary>
public int Count { get; }

/// <summary>
/// Creates a new node that holds the specified item and is linked to this node.
/// </summary>
/// <param name="item">The item to place in the new node.</param>
public SingleLinkedNode<TSource> Add(TSource item) => new SingleLinkedNode<TSource>(this, item);

/// <summary>
/// Gets an <see cref="IEnumerator{TSource}"/> that enumerates the items of this node's singly-linked list in reverse.
/// </summary>
public IEnumerator<TSource> GetEnumerator()
{
return ((IEnumerable<TSource>)ToArray()).GetEnumerator();
}

/// <summary>
/// Returns an <see cref="T:TSource[]"/> that contains the items of this node's singly-linked list in reverse.
/// </summary>
public TSource[] ToArray()
{
TSource[] array = new TSource[Count];
int index = Count;
for (SingleLinkedNode<TSource> node = this; node != null; node = node.Linked)
{
--index;
array[index] = node.Item;
}

Debug.Assert(index == 0);
return array;
}
}

/// <summary>
/// Represents the insertion of multiple items before or after an <see cref="IEnumerable{TSource}"/>.
/// </summary>
Expand All @@ -352,17 +264,26 @@ private class AppendPrependN<TSource> : AppendPrependIterator<TSource>
{
private readonly SingleLinkedNode<TSource> _prepended;
private readonly SingleLinkedNode<TSource> _appended;
private readonly int _prependCount;
private readonly int _appendCount;
private SingleLinkedNode<TSource> _node;

public AppendPrependN(IEnumerable<TSource> source, SingleLinkedNode<TSource> prepended, SingleLinkedNode<TSource> appended)
public AppendPrependN(IEnumerable<TSource> source, SingleLinkedNode<TSource> prepended, SingleLinkedNode<TSource> appended, int prependCount, int appendCount)
: base(source)
{
Debug.Assert(prepended != null || appended != null);
Debug.Assert(prependCount > 0 || appendCount > 0);
Debug.Assert(prependCount + appendCount >= 2);
Debug.Assert((prepended?.GetCount() ?? 0) == prependCount);
Debug.Assert((appended?.GetCount() ?? 0) == appendCount);

_prepended = prepended;
_appended = appended;
_prependCount = prependCount;
_appendCount = appendCount;
}

public override Iterator<TSource> Clone() => new AppendPrependN<TSource>(_source, _prepended, _appended);
public override Iterator<TSource> Clone() => new AppendPrependN<TSource>(_source, _prepended, _appended, _prependCount, _appendCount);

public override bool MoveNext()
{
Expand Down Expand Up @@ -394,7 +315,7 @@ public override bool MoveNext()
return false;
}

_enumerator = _appended.GetEnumerator();
_enumerator = _appended.GetEnumerator(_appendCount);
_state = 4;
goto case 4;
case 4:
Expand All @@ -407,12 +328,14 @@ public override bool MoveNext()

public override AppendPrependIterator<TSource> Append(TSource item)
{
return new AppendPrependN<TSource>(_source, _prepended, _appended != null ? _appended.Add(item) : new SingleLinkedNode<TSource>(item));
var appended = _appended != null ? _appended.Add(item) : new SingleLinkedNode<TSource>(item);
return new AppendPrependN<TSource>(_source, _prepended, appended, _prependCount, _appendCount + 1);
}

public override AppendPrependIterator<TSource> Prepend(TSource item)
{
return new AppendPrependN<TSource>(_source, _prepended != null ? _prepended.Add(item) : new SingleLinkedNode<TSource>(item), _appended);
var prepended = _prepended != null ? _prepended.Add(item) : new SingleLinkedNode<TSource>(item);
return new AppendPrependN<TSource>(_source, prepended, _appended, _prependCount + 1, _appendCount);
}

private TSource[] LazyToArray()
Expand All @@ -423,14 +346,14 @@ private TSource[] LazyToArray()

if (_prepended != null)
{
builder.Reserve(_prepended.Count);
builder.Reserve(_prependCount);
}

builder.AddRange(_source);

if (_appended != null)
{
builder.Reserve(_appended.Count);
builder.Reserve(_appendCount);
}

TSource[] array = builder.ToArray();
Expand Down Expand Up @@ -502,7 +425,7 @@ public override List<TSource> ToList()
list.AddRange(_source);
if (_appended != null)
{
IEnumerator<TSource> e = _appended.GetEnumerator();
IEnumerator<TSource> e = _appended.GetEnumerator(_appendCount);
while (e.MoveNext())
{
list.Add(e.Current);
Expand All @@ -518,10 +441,10 @@ public override int GetCount(bool onlyIfCheap)
if (listProv != null)
{
int count = listProv.GetCount(onlyIfCheap);
return count == -1 ? -1 : count + (_appended == null ? 0 : _appended.Count) + (_prepended == null ? 0 : _prepended.Count);
return count == -1 ? -1 : count + _appendCount + _prependCount;
}

return !onlyIfCheap || _source is ICollection<TSource> ? _source.Count() + (_appended == null ? 0 : _appended.Count) + (_prepended == null ? 0 : _prepended.Count) : -1;
return !onlyIfCheap || _source is ICollection<TSource> ? _source.Count() + _appendCount + _prependCount : -1;
}
}
}
Expand Down
6 changes: 1 addition & 5 deletions src/libraries/System.Linq/src/System/Linq/Distinct.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,7 @@ public override void Dispose()
private Set<TSource> FillSet()
{
Set<TSource> set = new Set<TSource>(_comparer);
foreach (TSource element in _source)
{
set.Add(element);
}

set.UnionWith(_source);
return set;
}

Expand Down
22 changes: 18 additions & 4 deletions src/libraries/System.Linq/src/System/Linq/Set.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ private void Resize()
/// Creates an array from the items in this set.
/// </summary>
/// <returns>An array of the items in this set.</returns>
internal TElement[] ToArray()
public TElement[] ToArray()
{
#if DEBUG
Debug.Assert(!_haveRemoved, "Optimised ToArray cannot be called if Remove has been called.");
Expand All @@ -175,7 +175,7 @@ internal TElement[] ToArray()
/// Creates a list from the items in this set.
/// </summary>
/// <returns>A list of the items in this set.</returns>
internal List<TElement> ToList()
public List<TElement> ToList()
{
#if DEBUG
Debug.Assert(!_haveRemoved, "Optimised ToList cannot be called if Remove has been called.");
Expand All @@ -193,14 +193,28 @@ internal List<TElement> ToList()
/// <summary>
/// The number of items in this set.
/// </summary>
internal int Count => _count;
public int Count => _count;

/// <summary>
/// Unions this set with an enumerable.
/// </summary>
/// <param name="other">The enumerable.</param>
public void UnionWith(IEnumerable<TElement> other)
{
Debug.Assert(other != null);

foreach (TElement item in other)
{
Add(item);
}
}

/// <summary>
/// Gets the hash code of the provided value with its sign bit zeroed out, so that modulo has a positive result.
/// </summary>
/// <param name="value">The value to hash.</param>
/// <returns>The lower 31 bits of the value's hash code.</returns>
internal int InternalGetHashCode(TElement value)
private int InternalGetHashCode(TElement value)
{
// Handle comparer implementations that throw when passed null
return (value == null) ? 0 : _comparer.GetHashCode(value) & 0x7FFFFFFF;
Expand Down
Loading

0 comments on commit 0958723

Please sign in to comment.