public DiffResult CreateCustomDiffs(string oldText, string newText, bool ignoreWhiteSpace, bool ignoreCase, Func<string, string[]> chunker) { if (oldText == null) throw new ArgumentNullException("oldText"); if (newText == null) throw new ArgumentNullException("newText"); if (chunker == null) throw new ArgumentNullException("chunker"); var pieceHash = new Dictionary<string, int>(); var lineDiffs = new List<DiffBlock>(); var modOld = new ModificationData(oldText); var modNew = new ModificationData(newText); BuildPieceHashes(pieceHash, modOld, ignoreWhiteSpace, ignoreCase, chunker); BuildPieceHashes(pieceHash, modNew, ignoreWhiteSpace, ignoreCase, chunker); BuildModificationData(modOld, modNew); int piecesALength = modOld.HashedPieces.Length; int piecesBLength = modNew.HashedPieces.Length; int posA = 0; int posB = 0; do { while (posA < piecesALength && posB < piecesBLength && !modOld.Modifications[posA] && !modNew.Modifications[posB]) { posA++; posB++; } int beginA = posA; int beginB = posB; for (; posA < piecesALength && modOld.Modifications[posA]; posA++) ; for (; posB < piecesBLength && modNew.Modifications[posB]; posB++) ; int deleteCount = posA - beginA; int insertCount = posB - beginB; if (deleteCount > 0 || insertCount > 0) { lineDiffs.Add(new DiffBlock(beginA, deleteCount, beginB, insertCount)); } } while (posA < piecesALength && posB < piecesBLength); return new DiffResult(modOld.Pieces, modNew.Pieces, lineDiffs); }
private static void BuildModificationData(ModificationData A, int startA, int endA, ModificationData B, int startB, int endB, int[] forwardDiagonal, int[] reverseDiagonal) { while (startA < endA && startB < endB && A.HashedPieces[startA].Equals(B.HashedPieces[startB])) { startA++; startB++; } while (startA < endA && startB < endB && A.HashedPieces[endA - 1].Equals(B.HashedPieces[endB - 1])) { endA--; endB--; } int aLength = endA - startA; int bLength = endB - startB; if (aLength > 0 && bLength > 0) { EditLengthResult res = CalculateEditLength(A.HashedPieces, startA, endA, B.HashedPieces, startB, endB, forwardDiagonal, reverseDiagonal); if (res.EditLength <= 0) return; if (res.LastEdit == Edit.DeleteRight && res.StartX - 1 > startA) A.Modifications[--res.StartX] = true; else if (res.LastEdit == Edit.InsertDown && res.StartY - 1 > startB) B.Modifications[--res.StartY] = true; else if (res.LastEdit == Edit.DeleteLeft && res.EndX < endA) A.Modifications[res.EndX++] = true; else if (res.LastEdit == Edit.InsertUp && res.EndY < endB) B.Modifications[res.EndY++] = true; BuildModificationData(A, startA, res.StartX, B, startB, res.StartY, forwardDiagonal, reverseDiagonal); BuildModificationData(A, res.EndX, endA, B, res.EndY, endB, forwardDiagonal, reverseDiagonal); } else if (aLength > 0) { for (int i = startA; i < endA; i++) A.Modifications[i] = true; } else if (bLength > 0) { for (int i = startB; i < endB; i++) B.Modifications[i] = true; } }
private static void BuildPieceHashes(IDictionary<string, int> pieceHash, ModificationData data, bool ignoreWhitespace, bool ignoreCase, Func<string, string[]> chunker) { string[] pieces; if (string.IsNullOrEmpty(data.RawData)) pieces = new string[0]; else pieces = chunker(data.RawData); data.Pieces = pieces; data.HashedPieces = new int[pieces.Length]; data.Modifications = new bool[pieces.Length]; for (int i = 0; i < pieces.Length; i++) { string piece = pieces[i]; if (ignoreWhitespace) piece = piece.Trim(); if (ignoreCase) piece = piece.ToUpperInvariant(); if (pieceHash.ContainsKey(piece)) { data.HashedPieces[i] = pieceHash[piece]; } else { data.HashedPieces[i] = pieceHash.Count; pieceHash[piece] = pieceHash.Count; } } }
protected static void BuildModificationData(ModificationData A, ModificationData B) { int N = A.HashedPieces.Length; int M = B.HashedPieces.Length; int MAX = M + N + 1; var forwardDiagonal = new int[MAX + 1]; var reverseDiagonal = new int[MAX + 1]; BuildModificationData(A, 0, N, B, 0, M, forwardDiagonal, reverseDiagonal); }