Example #1
0
        public static TextDiff Consume_Diff(DataStream <char> Old, DataStream <char> New)
        {
            /* Scan both streams until we either find a spot ahead in one that matches the current spot in the other OR we hit the end of the stream */
            var      CancelToken = new CancellationTokenSource();
            var      uOld        = Find_Next_Unique(Old, Old.Position);
            var      uNew        = Find_Next_Unique(New, New.Position);
            TextDiff addDiff     = null;
            TextDiff rmvDiff     = null;

            Parallel.Invoke(new ParallelOptions()
            {
                CancellationToken = CancelToken.Token
            },
                            () => /* Scan for addition */
            {                     /* Find the next unique in stream B that matches A's current */
                if (uOld == null)
                {
                    addDiff = new TextDiff(Old.Length, New.Length, EDiffType.Insertion);    /* Insertion at end */
                    return;
                }
                if (uNew == null)
                {
                    return;
                }
                var index = New.Position + (uNew.Length - 1);
                while (index < New.Length)
                {
                    var unique = Find_Next_Unique(New, index);
                    if (unique == null)
                    {
                        break;
                    }
                    else if (uOld.AsMemory().Equals(unique.AsMemory()))
                    {
                        addDiff = new TextDiff(New.Position, index, EDiffType.Insertion);
                        CancelToken.Cancel();
                    }
                    else
                    {
                        index += unique.Length - 1;
                    }
                }
            },
                            () => /* Scan for removal */
            {                     /* Find next unique in stream A matching B's current */
                if (uNew == null)
                {
                    rmvDiff = new TextDiff(New.Length, Old.Length, EDiffType.Removal);    /* Removal at end */
                    return;
                }
                if (uOld == null)
                {
                    return;
                }
                var index = Old.Position + (uOld.Length - 1);

                while (index < Old.Length)
                {
                    var unique = Find_Next_Unique(Old, index);
                    if (unique == null)
                    {
                        break;
                    }
                    else if (uNew.AsMemory().Equals(unique.AsMemory()))
                    {
                        rmvDiff = new TextDiff(Old.Position, index, EDiffType.Insertion);
                        CancelToken.Cancel();
                    }
                    else
                    {
                        index += unique.Length - 1;
                    }
                }
            }
                            );

            /* If nothing was found then the change must be a pure modification so we lockstep ahead in both streams to find the next spot that they both match */
            if (addDiff == null && rmvDiff == null)
            {
                int pos = New.Position;
                while ((pos + 1) < Old.Length && (pos + 1) < New.Length && Old.Peek(pos) != New.Peek(pos))
                {
                    pos++;
                }

                return(new TextDiff(New.Position, pos, EDiffType.Mutation));
            }
            else if (addDiff != null && rmvDiff != null)/* Else find the shorter of the two diffs and return that one */
            {
                if (addDiff.End < rmvDiff.End)
                {
                    return(addDiff);
                }
                else
                {
                    return(rmvDiff);
                }
            }
            else if (addDiff != null)
            {
                return(addDiff);
            }
            else if (rmvDiff != null)
            {
                return(rmvDiff);
            }

            return(null);
        }
Example #2
0
        /// <summary>
        /// Compiles a list of differences between the current text and some given text
        /// </summary>
        /// <param name="NewText"></param>
        /// <returns>List of start/end ranges</returns>
        public static LinkedList <TextDiff> Difference(StringPtr OldText, StringPtr NewText)
        {
            if ((OldText == null || OldText.Length <= 0) && (NewText == null || NewText.Length <= 0))
            {
                return(new LinkedList <TextDiff>());
            }
            else if ((OldText == null || OldText.Length <= 0) && NewText.Length > 0)
            {
                return(new LinkedList <TextDiff>(new TextDiff[] { new TextDiff(0, NewText.Length, EDiffType.Insertion) }));
            }
            else if ((NewText == null || NewText.Length <= 0) && OldText.Length > 0)
            {
                return(new LinkedList <TextDiff>(new TextDiff[] { new TextDiff(0, OldText.Length, EDiffType.Insertion) }));
            }


            var A = new DataStream <char>(OldText.AsMemory(), '\0');
            var B = new DataStream <char>(NewText.AsMemory(), '\0');

            var Chunks = new LinkedList <TextDiff>();

            while (true)
            {
                if (A.Next != B.Next)/* Just entered a spot where the texts stopped matching */
                {
                    var diff = TextDiff.Consume_Diff(A, B);
                    if (diff == null || diff.Length <= 0)
                    {/* No more diffs available */
                        return(Chunks);
                    }
                    /* Progress the spplicable stream by the difference ammount */
                    switch (diff.Type)
                    {
                    case EDiffType.Insertion:
                    {
                        B.Consume(diff.Length);
                    }
                    break;

                    case EDiffType.Removal:
                    {
                        A.Consume(diff.Length);
                    }
                    break;

                    case EDiffType.Mutation:
                    {
                        A.Consume(diff.Length);
                        B.Consume(diff.Length);
                    }
                    break;

                    default:
                        throw new NotImplementedException($"Handling for {nameof(EDiffType)} \"{diff.Type}\" has not been implemented!");
                    }

                    Chunks.AddLast(diff);
                }

                if (A.atEnd && B.atEnd)
                {
                    break;
                }
            }

            /* Check for text diff past the end of stream */

            return(Chunks);
        }