From 13e522ba64e2a0ee0bc417e158c43aa861fe99a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anta=CC=83o=20Almada?= Date: Sun, 10 Sep 2023 12:39:31 +0100 Subject: [PATCH 1/2] Add Where() --- LICENSE | 2 +- ...etFabric.DoublyLinkedList.Benchmark.csproj | 4 +- .../NetFabric.DoublyLinkedList.Tests.csproj | 12 +- .../DoublyLinkedList'1.ForwardEnumeration.cs | 9 +- .../DoublyLinkedList'1.ReverseEnumeration.cs | 7 +- ...blyLinkedList'1.WhereForwardEnumeration.cs | 115 ++++++++++++++++++ ...blyLinkedList'1.WhereReverseEnumeration.cs | 115 ++++++++++++++++++ .../DoublyLinkedList'1.cs | 4 +- .../NetFabric.DoublyLinkedList.csproj | 14 +-- 9 files changed, 259 insertions(+), 23 deletions(-) create mode 100644 NetFabric.DoublyLinkedList/DoublyLinkedList'1.WhereForwardEnumeration.cs create mode 100644 NetFabric.DoublyLinkedList/DoublyLinkedList'1.WhereReverseEnumeration.cs diff --git a/LICENSE b/LICENSE index 2413786..95e4ec9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2021 Antao Almada +Copyright (c) 2018-2023 Antao Almada Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/NetFabric.DoublyLinkedList.Benchmark/NetFabric.DoublyLinkedList.Benchmark.csproj b/NetFabric.DoublyLinkedList.Benchmark/NetFabric.DoublyLinkedList.Benchmark.csproj index 3b1ecf3..9c3030b 100644 --- a/NetFabric.DoublyLinkedList.Benchmark/NetFabric.DoublyLinkedList.Benchmark.csproj +++ b/NetFabric.DoublyLinkedList.Benchmark/NetFabric.DoublyLinkedList.Benchmark.csproj @@ -1,4 +1,4 @@ - + Exe @@ -7,7 +7,7 @@ - + diff --git a/NetFabric.DoublyLinkedList.Tests/NetFabric.DoublyLinkedList.Tests.csproj b/NetFabric.DoublyLinkedList.Tests/NetFabric.DoublyLinkedList.Tests.csproj index 844a2b8..b6be92d 100644 --- a/NetFabric.DoublyLinkedList.Tests/NetFabric.DoublyLinkedList.Tests.csproj +++ b/NetFabric.DoublyLinkedList.Tests/NetFabric.DoublyLinkedList.Tests.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1;netcoreapp3.1;net5.0 @@ -6,14 +6,14 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ForwardEnumeration.cs b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ForwardEnumeration.cs index a793f72..e3b9c45 100644 --- a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ForwardEnumeration.cs +++ b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ForwardEnumeration.cs @@ -18,6 +18,9 @@ public readonly struct ForwardEnumeration internal ForwardEnumeration(DoublyLinkedList list) => this.list = list; + public WhereForwardEnumeration Where(Func predicate) + => new(list, predicate); + public int Count => list.count; @@ -102,12 +105,12 @@ public bool MoveNext() return current is not null; } - public readonly void Reset() - => Throw.NotSupportedException(); + public void Reset() + => current = null; public readonly void Dispose() { } - } + } } } } diff --git a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ReverseEnumeration.cs b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ReverseEnumeration.cs index 1246e9d..83f177e 100644 --- a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ReverseEnumeration.cs +++ b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ReverseEnumeration.cs @@ -17,6 +17,9 @@ public readonly struct ReverseEnumeration internal ReverseEnumeration(DoublyLinkedList list) => this.list = list; + public WhereReverseEnumeration Where(Func predicate) + => new(list, predicate); + public int Count => list.count; @@ -101,8 +104,8 @@ public bool MoveNext() return current is not null; } - public readonly void Reset() - => Throw.NotSupportedException(); + public void Reset() + => current = null; public readonly void Dispose() { } diff --git a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.WhereForwardEnumeration.cs b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.WhereForwardEnumeration.cs new file mode 100644 index 0000000..e365eab --- /dev/null +++ b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.WhereForwardEnumeration.cs @@ -0,0 +1,115 @@ +using NetFabric.Hyperlinq; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; + +namespace NetFabric +{ + public partial class DoublyLinkedList + { + public readonly struct WhereForwardEnumeration + : IValueEnumerable + { + readonly DoublyLinkedList list; + readonly Func predicate; + + internal WhereForwardEnumeration(DoublyLinkedList list, Func predicate) + { + this.list = list; + this.predicate = predicate; + } + + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() => + new(list, predicate); + + DisposableEnumerator IValueEnumerable.GetEnumerator() => + new(list, predicate); + + IEnumerator IEnumerable.GetEnumerator() => + new DisposableEnumerator(list, predicate); + + IEnumerator IEnumerable.GetEnumerator() => + new DisposableEnumerator(list, predicate); + + public struct Enumerator + { + readonly DoublyLinkedList list; + readonly Func predicate; + readonly int version; + Node? current; + + internal Enumerator(DoublyLinkedList list, Func predicate) + { + this.list = list; + this.predicate = predicate; + version = list.version; + current = null; + } + + public readonly T Current + => current!.Value; + + public bool MoveNext() + { + if (version != list.version) + Throw.InvalidOperationException(); + do + { + current = current is null + ? list.First + : current.Next; + } + while (current is not null && !predicate(current.Value)); + return current is not null; + } + } + + public struct DisposableEnumerator + : IEnumerator + { + readonly DoublyLinkedList list; + readonly Func predicate; + readonly int version; + Node? current; + + internal DisposableEnumerator(DoublyLinkedList list, Func predicate) + { + this.list = list; + this.predicate = predicate; + version = list.version; + current = null; + } + + public readonly T Current + => current!.Value; + readonly object? IEnumerator.Current + => current!.Value; + + public bool MoveNext() + { + if (version != list.version) + Throw.InvalidOperationException(); + do + { + current = current is null + ? list.First + : current.Next; + } + while (current is not null && !predicate(current.Value)); + return current is not null; + } + + public void Reset() + => current = null; + + public readonly void Dispose() + { } + } + } + } +} diff --git a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.WhereReverseEnumeration.cs b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.WhereReverseEnumeration.cs new file mode 100644 index 0000000..55aaf15 --- /dev/null +++ b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.WhereReverseEnumeration.cs @@ -0,0 +1,115 @@ +using NetFabric.Hyperlinq; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; + +namespace NetFabric +{ + public partial class DoublyLinkedList + { + public readonly struct WhereReverseEnumeration + : IValueEnumerable + { + readonly DoublyLinkedList list; + readonly Func predicate; + + internal WhereReverseEnumeration(DoublyLinkedList list, Func predicate) + { + this.list = list; + this.predicate = predicate; + } + + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() => + new(list, predicate); + + DisposableEnumerator IValueEnumerable.GetEnumerator() => + new(list, predicate); + + IEnumerator IEnumerable.GetEnumerator() => + new DisposableEnumerator(list, predicate); + + IEnumerator IEnumerable.GetEnumerator() => + new DisposableEnumerator(list, predicate); + + public struct Enumerator + { + readonly DoublyLinkedList list; + readonly Func predicate; + readonly int version; + Node? current; + + internal Enumerator(DoublyLinkedList list, Func predicate) + { + this.list = list; + this.predicate = predicate; + version = list.version; + current = null; + } + + public readonly T Current + => current!.Value; + + public bool MoveNext() + { + if (version != list.version) + Throw.InvalidOperationException(); + do + { + current = current is null + ? list.Last + : current.Previous; + } + while (current is not null && !predicate(current.Value)); + return current is not null; + } + } + + public struct DisposableEnumerator + : IEnumerator + { + readonly DoublyLinkedList list; + readonly Func predicate; + readonly int version; + Node? current; + + internal DisposableEnumerator(DoublyLinkedList list, Func predicate) + { + this.list = list; + this.predicate = predicate; + version = list.version; + current = null; + } + + public readonly T Current + => current!.Value; + readonly object? IEnumerator.Current + => current!.Value; + + public bool MoveNext() + { + if (version != list.version) + Throw.InvalidOperationException(); + do + { + current = current is null + ? list.Last + : current.Previous; + } + while (current is not null && !predicate(current.Value)); + return current is not null; + } + + public void Reset() + => current = null; + + public readonly void Dispose() + { } + } + } + } +} diff --git a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.cs b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.cs index 8c344fe..72bf7ef 100644 --- a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.cs +++ b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.cs @@ -854,7 +854,7 @@ public void RemoveLast() tail = node.Previous; tail.Next = null; } - node.Invalidate(); + node!.Invalidate(); count--; version++; } @@ -944,7 +944,7 @@ public void ReverseInPlace() if (count < 2) return; - Node temp; + Node? temp; var current = head; while (current is not null) { diff --git a/NetFabric.DoublyLinkedList/NetFabric.DoublyLinkedList.csproj b/NetFabric.DoublyLinkedList/NetFabric.DoublyLinkedList.csproj index e07db33..5bc2fe4 100644 --- a/NetFabric.DoublyLinkedList/NetFabric.DoublyLinkedList.csproj +++ b/NetFabric.DoublyLinkedList/NetFabric.DoublyLinkedList.csproj @@ -1,13 +1,13 @@ - + - net461;netstandard2.0 + net461;netstandard2.0; net6.0 NetFabric.DoublyLinkedList NetFabric.DoublyLinkedList An alternative to System.Collection.Generics.LinkedList with reverse operation and enumeration without allocation. - 3.0.0 + 3.1.0 Antao Almada - Copyright 2018-2021 Antao Almada + Copyright 2018-2023 Antao Almada Icon.png LICENSE collections, data structures, algorithms, list @@ -29,16 +29,16 @@ - + all runtime; build; native; contentfiles; analyzers - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 66947605f79f8696ba614a0764684c9d3b863333 Mon Sep 17 00:00:00 2001 From: Antao Almada Date: Sun, 10 Sep 2023 21:30:03 +0100 Subject: [PATCH 2/2] Add Any(), First(), FirstOrDefault(), Single() and SingleOrDefault() --- .../DoublyLinkedList'1.ForwardEnumeration.cs | 112 ++++++++++++++- ...blyLinkedList'1.WhereForwardEnumeration.cs | 1 - ...blyLinkedList'1.WhereReverseEnumeration.cs | 1 - .../DoublyLinkedList'1.cs | 2 - .../DoublyLinkedList.cs | 2 - .../NetFabric.DoublyLinkedList.csproj | 21 +++ .../Resources.Designer.cs | 99 +++++++++++++ NetFabric.DoublyLinkedList/Resources.resx | 132 ++++++++++++++++++ NetFabric.DoublyLinkedList/Throw.cs | 5 + 9 files changed, 368 insertions(+), 7 deletions(-) create mode 100644 NetFabric.DoublyLinkedList/Resources.Designer.cs create mode 100644 NetFabric.DoublyLinkedList/Resources.resx diff --git a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ForwardEnumeration.cs b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ForwardEnumeration.cs index e3b9c45..c1aefd4 100644 --- a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ForwardEnumeration.cs +++ b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ForwardEnumeration.cs @@ -2,7 +2,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; @@ -21,6 +20,117 @@ internal ForwardEnumeration(DoublyLinkedList list) public WhereForwardEnumeration Where(Func predicate) => new(list, predicate); + public bool Any() + => !list.IsEmpty; + + public bool Any(Func predicate) + { + var current = list.First; + while (current is not null) + { + if (predicate(current.Value)) + return true; + current = current.Next; + } + return false; + } + + public T First() + { + if (list.IsEmpty) + Throw.InvalidOperationException(Resources.NoElements); + return list.First!.Value; + } + + public T First(Func predicate) + { + var current = list.First; + while (current is not null) + { + if (predicate(current.Value)) + return current.Value; + current = current.Next; + } + return Throw.InvalidOperationException(Resources.NoMatches); + } + + public T? FirstOrDefault() + => list.First is not null + ? list.First.Value + : default; + + public T? FirstOrDefault(Func predicate) + { + var current = list.First; + while (current is not null) + { + if (predicate(current.Value)) + return current.Value; + current = current.Next; + } + return default; + } + + public T Single() + { + if (list.IsEmpty) + Throw.InvalidOperationException(Resources.NoElements); + if (list.First != list.Last) + Throw.InvalidOperationException(Resources.MoreThanOneElement); + return list.First!.Value; + } + + public T Single(Func predicate) + { + var current = list.First; + while (current is not null) + { + if (predicate(current.Value)) + { + var value = current.Value; + + // found first, keep going until end or find second + current = current.Next; + while (current is not null) + { + if (predicate(current.Value)) + Throw.InvalidOperationException(Resources.MoreThanOneMatch); + } + + return value; + } + } + return Throw.InvalidOperationException(Resources.NoMatches); + } + + public T? SingleOrDefault() + => list.First is not null + ? Single() + : default; + + public T? SingleOrDefault(Func predicate) + { + var current = list.First; + while (current is not null) + { + if (predicate(current.Value)) + { + var value = current.Value; + + // found first, keep going until end or find second + current = current.Next; + while (current is not null) + { + if (predicate(current.Value)) + Throw.InvalidOperationException(Resources.MoreThanOneMatch); + } + + return value; + } + } + return default; + } + public int Count => list.count; diff --git a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.WhereForwardEnumeration.cs b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.WhereForwardEnumeration.cs index e365eab..946d242 100644 --- a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.WhereForwardEnumeration.cs +++ b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.WhereForwardEnumeration.cs @@ -2,7 +2,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; diff --git a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.WhereReverseEnumeration.cs b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.WhereReverseEnumeration.cs index 55aaf15..a9e7421 100644 --- a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.WhereReverseEnumeration.cs +++ b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.WhereReverseEnumeration.cs @@ -2,7 +2,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; diff --git a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.cs b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.cs index 72bf7ef..3e115be 100644 --- a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.cs +++ b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; namespace NetFabric diff --git a/NetFabric.DoublyLinkedList/DoublyLinkedList.cs b/NetFabric.DoublyLinkedList/DoublyLinkedList.cs index f32c74d..ba5395e 100644 --- a/NetFabric.DoublyLinkedList/DoublyLinkedList.cs +++ b/NetFabric.DoublyLinkedList/DoublyLinkedList.cs @@ -1,5 +1,3 @@ -using System; -using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("NetFabric.DoublyLinkedList.Tests")] diff --git a/NetFabric.DoublyLinkedList/NetFabric.DoublyLinkedList.csproj b/NetFabric.DoublyLinkedList/NetFabric.DoublyLinkedList.csproj index 5bc2fe4..04a602b 100644 --- a/NetFabric.DoublyLinkedList/NetFabric.DoublyLinkedList.csproj +++ b/NetFabric.DoublyLinkedList/NetFabric.DoublyLinkedList.csproj @@ -44,4 +44,25 @@ + + + True + True + Resources.resx + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + 1.3.0 + + + diff --git a/NetFabric.DoublyLinkedList/Resources.Designer.cs b/NetFabric.DoublyLinkedList/Resources.Designer.cs new file mode 100644 index 0000000..73a5b8f --- /dev/null +++ b/NetFabric.DoublyLinkedList/Resources.Designer.cs @@ -0,0 +1,99 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace NetFabric { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NetFabric.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Sequence contains more than one element. + /// + internal static string MoreThanOneElement { + get { + return ResourceManager.GetString("MoreThanOneElement", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sequence contains more than one matching element. + /// + internal static string MoreThanOneMatch { + get { + return ResourceManager.GetString("MoreThanOneMatch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sequence contains no elements. + /// + internal static string NoElements { + get { + return ResourceManager.GetString("NoElements", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sequence contains no matching element. + /// + internal static string NoMatches { + get { + return ResourceManager.GetString("NoMatches", resourceCulture); + } + } + } +} diff --git a/NetFabric.DoublyLinkedList/Resources.resx b/NetFabric.DoublyLinkedList/Resources.resx new file mode 100644 index 0000000..0866805 --- /dev/null +++ b/NetFabric.DoublyLinkedList/Resources.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Sequence contains more than one element + + + Sequence contains more than one matching element + + + Sequence contains no elements + + + Sequence contains no matching element + + \ No newline at end of file diff --git a/NetFabric.DoublyLinkedList/Throw.cs b/NetFabric.DoublyLinkedList/Throw.cs index 3fc18eb..5bfbf69 100644 --- a/NetFabric.DoublyLinkedList/Throw.cs +++ b/NetFabric.DoublyLinkedList/Throw.cs @@ -26,6 +26,11 @@ public static T ArgumentNullException(string paramName, string? message = def public static void InvalidOperationException(string? message = default) => throw new InvalidOperationException(message); + [DoesNotReturn] + [MethodImpl(MethodImplOptions.NoInlining)] + public static T InvalidOperationException(string? message = default) + => throw new InvalidOperationException(message); + [DoesNotReturn] [MethodImpl(MethodImplOptions.NoInlining)] public static void NotSupportedException()