Skip to content

Commit

Permalink
added xml doc comments for all public types/methods (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
spkl authored Jan 31, 2021
1 parent f53a50f commit 440697a
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 8 deletions.
75 changes: 75 additions & 0 deletions src/ArrayView{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,71 +3,145 @@

namespace spkl.Diffs
{
/// <summary>
/// A (worse-performing) .NET standard 2.0 / .NET Full Framework alternative to the Span&lt;T&gt;/Memory&lt;T&gt; type.
/// This struct gives you the ability to encapsulate a segment of an array into an object that gives array-like read and write access to the original array.
/// For example: new ArrayView&lt;int&gt;(new int[]{0, 1, 2, 3}, 1, 2) gives you access to indexes 1 and 2 from the original array,
/// where ArrayView index 0 corresponds to original array index 1.
/// </summary>
/// <typeparam name="T">The array element type.</typeparam>
public readonly struct ArrayView<T> : IReadOnlyList<T>
{
private readonly T[] array;

/// <summary>
/// The start offset/index of this <see cref="ArrayView{T}"/> in relation to the original array.
/// Index 0 of the <see cref="ArrayView{T}"/> corresponds to index <see cref="StartOffset"/> of the original array.
/// </summary>
public int StartOffset { get; }

/// <summary>
/// Gets the number of elements of the <see cref="ArrayView{T}"/>.
/// </summary>
public int Length { get; }

/// <summary>
/// Creates a new instance granting access to all of the array.
/// </summary>
/// <param name="array">The array.</param>
public ArrayView(T[] array)
: this(array, 0, array.Length)
{
}

/// <summary>
/// Creates a new instance granting access to the array elements from <paramref name="startIndex"/> to the end of the array.
/// </summary>
/// <param name="array">The array.</param>
/// <param name="startIndex">The start index.</param>
public ArrayView(T[] array, int startIndex)
: this(array, startIndex, array.Length - startIndex)
{
}

/// <summary>
/// Creates a new instance granting access to <paramref name="length"/> array elements, beginning from <paramref name="startIndex"/>.
/// </summary>
/// <param name="array">The array.</param>
/// <param name="startIndex">The start index.</param>
/// <param name="length">The length of the <see cref="ArrayView{T}"/>.</param>
public ArrayView(T[] array, int startIndex, int length)
{
this.array = array;
this.StartOffset = startIndex;
this.Length = length;
}

/// <summary>
/// Creates a new instance granting access to the <see cref="ArrayView{T}"/> elements from <paramref name="startIndex"/> to the end of the <see cref="ArrayView{T}"/>.
/// </summary>
/// <param name="source">The source <see cref="ArrayView{T}"/>.</param>
/// <param name="startIndex">The start index.</param>
public ArrayView(ArrayView<T> source, int startIndex)
: this(source, startIndex, source.Length - startIndex)
{
}

/// <summary>
/// Creates a new instance granting access to <paramref name="length"/> array elements,
/// beginning from <paramref name="startIndex"/> (in relation to the <paramref name="source"/> indexes).
/// </summary>
/// <param name="source">The source <see cref="ArrayView{T}"/>.</param>
/// <param name="startIndex">The start index.</param>
/// <param name="length">The length of the <see cref="ArrayView{T}"/>.</param>
public ArrayView(ArrayView<T> source, int startIndex, int length)
{
this.array = source.array;
this.StartOffset = source.StartOffset + startIndex;
this.Length = length;
}

/// <summary>
/// Creates a new <see cref="ArrayView{T}"/> from this instance, skipping the first <paramref name="count"/> elements.
/// </summary>
/// <param name="count">The number of elements to skip from the start of this <see cref="ArrayView{T}"/>.</param>
/// <returns>A new <see cref="ArrayView{T}"/> with a smaller number of elements.</returns>
public ArrayView<T> TrimStart(int count)
{
return new ArrayView<T>(this, count);
}

/// <summary>
/// Creates a new <see cref="ArrayView{T}"/> from this instance, skipping the last <paramref name="count"/> elements.
/// </summary>
/// <param name="count">The number of elements to skip from the end of this <see cref="ArrayView{T}"/>.</param>
/// <returns>A new <see cref="ArrayView{T}"/> with a smaller number of elements.</returns>
public ArrayView<T> TrimEnd(int count)
{
return new ArrayView<T>(this, 0, this.Length - count);
}

/// <summary>
/// Creates a new <see cref="ArrayView{T}"/> from this instance,
/// granting access to element indexes [<paramref name="startIndex"/>..<paramref name="endIndexExclusive"/>] (in relation to this instances indexes).
/// </summary>
/// <param name="startIndex">The start index.</param>
/// <param name="endIndexExclusive">The index of the first element not included in the new <see cref="ArrayView{T}"/>.</param>
/// <returns>A new <see cref="ArrayView{T}"/> with a smaller number of elements.</returns>
public ArrayView<T> Range(int startIndex, int endIndexExclusive)
{
return new ArrayView<T>(this, startIndex, endIndexExclusive - startIndex);
}

/// <summary>
/// Creates a new <see cref="ArrayView{T}"/> from this instance,
/// granting access to the <see cref="ArrayView{T}"/> elements from <paramref name="startIndex"/> to the end of the <see cref="ArrayView{T}"/>.
/// </summary>
/// <remarks>
/// This is essentially the same as calling <see cref="TrimStart(int)"/>.
/// </remarks>
/// <param name="startIndex">The start index.</param>
/// <returns>A new <see cref="ArrayView{T}"/> with a smaller number of elements.</returns>
public ArrayView<T> Range(int startIndex)
{
return this.TrimStart(startIndex);
}

/// <summary>
/// Gets or sets the element at the specified index.
/// The operation is performed on the original array; indexes are translated accordingly.
/// </summary>
/// <param name="index">The zero-based index of the element to get or set.</param>
public T this[int index]
{
get => this.array[index + this.StartOffset];
set => this.array[index + this.StartOffset] = value;
}

/// <inheritdoc />
public int Count => this.Length;

/// <inheritdoc />
public IEnumerator<T> GetEnumerator()
{
for (int i = 0; i < this.Length; i++)
Expand All @@ -76,6 +150,7 @@ public IEnumerator<T> GetEnumerator()
}
}

/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
Expand Down
1 change: 1 addition & 0 deletions src/Diffs.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<TargetFrameworks>netstandard2.0;netcoreapp2.1;netcoreapp3.1;net5.0</TargetFrameworks>
<RootNamespace>spkl.Diffs</RootNamespace>
<AssemblyName>spkl.Diffs</AssemblyName>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Authors>Sebastian Fischer</Authors>
<Company>Sebastian Fischer</Company>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
Expand Down
40 changes: 37 additions & 3 deletions src/MyersDiff.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@

namespace spkl.Diffs
{
/// <summary>
/// Provides the diff result or shortest edit script for two sequences A and B using Eugene Myers diff algorithm.
/// </summary>
/// <typeparam name="T">The sequence item type.</typeparam>
public class MyersDiff<T>
{
private T[] aValues, bValues;
Expand All @@ -13,6 +17,13 @@ public class MyersDiff<T>

private VArray<int> Vf, Vr;

/// <summary>
/// Creates a new instance of the <see cref="MyersDiff{T}"/> class
/// and calculates the diff result of sequences A and B
/// using the <see cref="object.Equals(object)"/> method to determine item equality.
/// </summary>
/// <param name="aValues">Item sequence A.</param>
/// <param name="bValues">Item sequence B.</param>
public MyersDiff(T[] aValues, T[] bValues)
{
this.aValues = aValues;
Expand Down Expand Up @@ -44,23 +55,38 @@ public MyersDiff(T[] aValues, T[] bValues)
#endif
}

/// <summary>
/// Creates a new instance of the <see cref="MyersDiff{T}"/> class
/// and calculates the diff result of sequences A and B
/// using the provided <see cref="IEqualityComparer{T}"/> to determine item equality.
/// </summary>
/// <param name="aValues">Item sequence A.</param>
/// <param name="bValues">Item sequence B.</param>
/// <param name="comparer">The implementation to determine item equality.</param>
public MyersDiff(T[] aValues, T[] bValues, IEqualityComparer<T> comparer)
: this(aValues, bValues)
{
this.comparer = comparer;
}

public bool AreEqual(int aIndex, int bIndex)
private bool AreEqual(int aIndex, int bIndex)
{
if (this.comparer != null)
{
return this.comparer.Equals(this.aValues[aIndex], this.bValues[bIndex]);
}

return object.Equals(this.aValues[aIndex], this.bValues[bIndex]);
}

public IEnumerable<(ResultType, T, T)> GetResult()
/// <summary>
/// Gets the calculated diff result in the form of matched items/lines:
/// ResultType: Specifies whether the line includes only an item from A, from B or from both sequences.
/// AItem: The item from sequence A; this is the default value/null if resultType is <see cref="ResultType.B"/>.
/// BItem: The item from sequence B, this is the default value/null if resultType is <see cref="ResultType.A"/>.
/// </summary>
/// <returns>An enumerable of diff lines containing one unmatched or two matched items.</returns>
public IEnumerable<(ResultType ResultType, T AItem, T BItem)> GetResult()
{
int currentA = 0, currentB = 0;
while (currentA < this.aRemoved.Length || currentB < this.bAdded.Length)
Expand Down Expand Up @@ -97,6 +123,14 @@ public bool AreEqual(int aIndex, int bIndex)
}
}

/// <summary>
/// Gets the edit script that results from the comparison of the two sequences.
/// Every item of the enumerable equals one edit instruction. Read this as follows:
/// LineA, CountA: Starting at index LineA in sequence A, remove CountA items.
/// LineB, CountB: Starting at index LineB in sequence B, insert CountB items.
/// Line numbers start with 0 and correspond to the sequences that were put into the constructor.
/// </summary>
/// <returns>An enumerable of edit instructions.</returns>
public IEnumerable<(int LineA, int LineB, int CountA, int CountB)> GetEditScript()
{
int currentA = 0, currentB = 0;
Expand Down
12 changes: 12 additions & 0 deletions src/ResultType.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
namespace spkl.Diffs
{
/// <summary>
/// Describes the contents of a result item.
/// </summary>
public enum ResultType : byte
{
/// <summary>
/// The result only contains an item from sequence A.
/// </summary>
A,
/// <summary>
/// The result only contains an item from sequence B.
/// </summary>
B,
/// <summary>
/// The result contains an item from both sequence A and B.
/// </summary>
Both
}
}
36 changes: 31 additions & 5 deletions src/VArray{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

namespace spkl.Diffs
{
/// <summary>
/// An array wrapper enabling arbitrary (including negative) element indexes.
/// </summary>
/// <typeparam name="T">The array element type.</typeparam>
public class VArray<T> : IEnumerable<T>
{
private readonly T[] array;
Expand All @@ -16,6 +20,9 @@ private VArray(int firstIndex, int length)
this.offset = firstIndex;
}

/// <summary>
/// Creates a new <see cref="VArray{T}"/> with indexes ranging from <paramref name="firstIndex"/> to <paramref name="lastIndex"/>.
/// </summary>
public static VArray<T> CreateFromTo(int firstIndex, int lastIndex)
{
if (lastIndex < firstIndex)
Expand All @@ -26,45 +33,64 @@ public static VArray<T> CreateFromTo(int firstIndex, int lastIndex)
return new VArray<T>(firstIndex, lastIndex - firstIndex + 1);
}

/// <summary>
/// Creates a new <see cref="VArray{T}"/> with indexes starting at <paramref name="firstIndex"/> and the specified <paramref name="length"/>.
/// </summary>
public static VArray<T> CreateByLength(int firstIndex, int length)
{
return new VArray<T>(firstIndex, length);
}

/// <summary>
/// Provides a debugging view combining all indexes with their corresponding elements.
/// </summary>
public (int index, T value)[] DebugView
{
get
{
(int, T)[] result = new (int, T)[this.Count];
for (int i = 0; i < result.Length; i++)
{
result[i] = (i + offset, this.array[i]);
result[i] = (i + this.offset, this.array[i]);
}

return result;
}
}

/// <summary>
/// Gets or sets the element at the specified index.
/// </summary>
/// <param name="index">The index of the element to get or set.</param>
public T this[int index]
{
get => this.array[index - this.offset];
set => this.array[index - this.offset] = value;
}

/// <summary>
/// The smallest index of this instance.
/// </summary>
public int LowerBoundInclusive => this.offset;

public int UpperBoundExclusive => this.Count + offset;
/// <summary>
/// The first index that is not part of the instance.
/// </summary>
public int UpperBoundExclusive => this.Count + this.offset;

public int Count => ((ICollection<T>)array).Count;
/// <inheritdoc cref="ICollection{T}.Count"/>
public int Count => ((ICollection<T>)this.array).Count;

/// <inheritdoc />
public IEnumerator<T> GetEnumerator()
{
return ((IEnumerable<T>)array).GetEnumerator();
return ((IEnumerable<T>)this.array).GetEnumerator();
}

/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator()
{
return array.GetEnumerator();
return this.array.GetEnumerator();
}
}
}

0 comments on commit 440697a

Please sign in to comment.