; --------------------------------------------------------------------------------------------; Copyright (c) Fantaisie Software. All rights reserved.; Dual licensed under the GPL and Fantaisie Software licenses.; See LICENSE and LICENSE-FANTAISIE in the project root for license information.; --------------------------------------------------------------------------------------------; Implementation of the Myers Diff algorithm with the linear space refinement specified in; "An O(ND) Difference Algorithm and Its Variations" -- Algorithmica 1, 2 (1986), 251-266.; See here: http://www.xmailserver.org/diff2.pdf;; A useful tutorial with code examples:; https://blog.jcoglan.com/2017/02/12/the-myers-diff-algorithm-part-1/; https://blog.jcoglan.com/2017/02/15/the-myers-diff-algorithm-part-2/; https://blog.jcoglan.com/2017/02/17/the-myers-diff-algorithm-part-3/; https://blog.jcoglan.com/2017/03/22/myers-diff-in-linear-space-theory/; https://blog.jcoglan.com/2017/04/25/myers-diff-in-linear-space-implementation/;; A very useful tutorial and graphical tool to visualize the algorithm:; http://simplygenius.net/Article/DiffTutorial1; http://simplygenius.net/Article/DiffTutorial2;; Compute checksum of a line depending on the given Flags;Procedure Diff_ComputeChecksum(*Start, Length, Flags)If Length = 0ProcedureReturn 0ElseIf Flags = 0ProcedureReturn CRC32Fingerprint(*Start, Length)Else; need to get a string to apply further options first;Protected Line$ = PeekS(*Start, Length, #PB_Ascii) ; ok for ascii and utf8, as we just need a checksum and no contentIf Flags & #DIFF_IgnoreCaseLine$ = LCase(Line$)EndIfIf Flags & #DIFF_IgnoreSpaceAllLine$ = Trim(ReplaceString(Line,ドル Chr(9), " ")) ; convert tab, remove space on sides; collapse any multiple space to single ones, so we have only significant spaces leftWhile FindString(Line,ドル " ", 1)Line$ = ReplaceString(Line,ドル " ", " ")WendElseIf Flags & #DIFF_IgnoreSpaceLeftWhile Left(Line,ドル 1) = " " Or Left(Line,ドル 1) = Chr(9) ; simple trim is not enough hereLine$ = Right(Line,ドル Len(Line$)-1)WendEndIfIf Flags & #DIFF_IgnoreSpaceRightWhile Right(Line,ドル 1) = " " Or Right(Line,ドル 1) = Chr(9)Line$ = Left(Line,ドル Len(Line$)-1)WendEndIfEndIfIf Len(Line$) = 0ProcedureReturn 0Else; No need to convert back to Ascii. Just compute the hash of the unicode lineProcedureReturn CRC32Fingerprint(@Line,ドル StringByteLength(Line$))EndIfEndIfEndProcedure; Scan a file and fill the lines array;Procedure Diff_PreProcess(*Pointer.Byte, Size, Array Lines.DiffLine(1), Flags)Protected *BufferEnd = *Pointer + SizeProtected Lines = 0Protected Space = ArraySize(Lines()) ; don't do +1 to have the extra space for the last lineProtected *LineStart = *PointerWhile *Pointer < *BufferEnd; detect next newlineIf *Pointer\b = 13*Pointer + 1If *Pointer < *BufferEnd And *Pointer\b = 10*Pointer + 1EndIfElseIf *Pointer\b = 10*Pointer + 1Else; no newline*Pointer + 1ContinueEndIf; newline foundIf Space = 0ReDim Lines(Lines * 2)Space = LinesEndIfLines(Lines)\Checksum = Diff_ComputeChecksum(*LineStart, *Pointer-*LineStart, Flags)Lines(Lines)\Start = *LineStartLines(Lines)\Length = *Pointer - *LineStartLines + 1Space - 1*LineStart = *PointerWend; add the last lineIf *Pointer > *LineStartLines(Lines)\Checksum = Diff_ComputeChecksum(*LineStart, *Pointer-*LineStart, Flags)Lines(Lines)\Start = *LineStartLines(Lines)\Length = *Pointer - *LineStartLines + 1EndIfProcedureReturn LinesEndProcedure; Add a (possible empty) edit to the edit script; Multiple edits with the same Op are combined into one;Procedure Diff_AddEdit(*Ctx.DiffContext, Op, Array Lines.DiffLine(1), StartLine, Lines)Protected LineIf Lines > 0If LastElement(*Ctx\Edits()) = 0 Or *Ctx\Edits()\Op <> Op; start a new editAddElement(*Ctx\Edits())*Ctx\Edits()\Op = Op*Ctx\Edits()\StartLine = StartLine*Ctx\Edits()\Start = Lines(StartLine)\StartEndIf; append lines to existing edit*Ctx\Edits()\Lines + LinesFor Line = StartLine To StartLine + Lines - 1*Ctx\Edits()\Length + Lines(Line)\LengthNext LineEndIfEndProcedure; Find middle snake; Returns edit distance D;Procedure Diff_FindMiddleSnake(*Ctx.DiffContext, AOffset, N, BOffset, M, *x.Integer, *y.Integer, *u.Integer, *v.Integer)Protected DELTA = N - MProtected OFFSET = (N + M) * 2Protected odd, mid, D, k, c, x, yodd = Bool(DELTA % 2 <> 0)If oddmid = (N + M) / 2 + 1Elsemid = (N + M) / 2EndIf*Ctx\FV(1+OFFSET) = 0*Ctx\RV(DELTA-1+OFFSET) = NFor D = 0 To mid; forward searchFor k = -D To D Step 2If k = -D Or (k <> D And *Ctx\FV(k-1+OFFSET) < *Ctx\FV(k+1+OFFSET))x = *Ctx\FV(k+1+OFFSET)Elsex = *Ctx\FV(k-1+OFFSET) + 1EndIfy = x - k*x\i = x*y\i = yWhile x < N And y < M And *Ctx\A(AOffset + x)\Checksum = *Ctx\B(BOffset + y)\Checksumx + 1y + 1Wend*Ctx\FV(k+OFFSET) = xIf odd And k >= (DELTA - (D - 1)) And k <= (DELTA + (D - 1))If x >= *Ctx\RV(k+OFFSET)*u\i = x*v\i = yProcedureReturn 2 * D - 1EndIfEndIfNext k; reverse searchFor k = -D To D Step 2c = k + DELTAIf k = D Or (k <> -D And *Ctx\RV(c-1+OFFSET) < *Ctx\RV(c+1+OFFSET))x = *Ctx\RV(c-1+OFFSET)Elsex = *Ctx\RV(c+1+OFFSET) - 1EndIfy = x - c*u\i = x*v\i = yWhile x > 0 And y > 0 And *Ctx\A(AOffset + x-1)\Checksum = *Ctx\B(BOffset + y-1)\Checksumx - 1y - 1Wend*Ctx\RV(c+OFFSET) = xIf odd = 0 And c >= -D And c <= DIf x <= *Ctx\FV(c+OFFSET)*x\i = x*y\i = yProcedureReturn 2 * DEndIfEndIfNext kNext DEndProcedure; Recursively compute Diff;Procedure Diff_Recursive(*Ctx.DiffContext, AOffset, N, BOffset, M)Protected D, x, y, u, vIf N > 0 And M = 0; second sequence is empty: so only deletionsDiff_AddEdit(*Ctx, #DIFF_DELETE, *Ctx\A(), AOffset, N)ElseIf N = 0 And M > 0; first sequence is empty: so only additionsDiff_AddEdit(*Ctx, #DIFF_INSERT, *Ctx\B(), BOffset, M)ElseIf N > 0 And M > 0D = Diff_FindMiddleSnake(*Ctx, AOffset, N, BOffset, M, @x, @y, @u, @v);Debug "D = " + D + " x=" + x + " y=" + y + " -> u=" + u + " v=" + vIf D = 0; both sequences are equalDiff_AddEdit(*Ctx, #DIFF_MATCH, *Ctx\A(), AOffset, N)ElseIf D = 1; exactly one edit and 0 or more matchesIf M > NIf x = u; \; \; |Diff_AddEdit(*Ctx, #DIFF_MATCH, *Ctx\A(), AOffset, N)Diff_AddEdit(*Ctx, #DIFF_INSERT, *Ctx\B(), BOffset + M-1, 1)Else; |; \; \Diff_AddEdit(*Ctx, #DIFF_INSERT, *Ctx\B(), BOffset, 1)Diff_AddEdit(*Ctx, #DIFF_MATCH, *Ctx\A(), AOffset, N)EndIfElseIf x = u; \; \; -Diff_AddEdit(*Ctx, #DIFF_MATCH, *Ctx\A(), AOffset, M)Diff_AddEdit(*Ctx, #DIFF_DELETE, *Ctx\A(), AOffset + N-1, 1)Else; -; \; \Diff_AddEdit(*Ctx, #DIFF_DELETE, *Ctx\A(), AOffset, 1)Diff_AddEdit(*Ctx, #DIFF_MATCH, *Ctx\A(), AOffset + 1, M)EndIfEndIfElse; general case: recurse around the middle snakeDiff_Recursive(*Ctx, AOffset, x, BOffset, y)Diff_AddEdit(*Ctx, #DIFF_MATCH, *Ctx\A(), AOffset + x, u-x)Diff_Recursive(*Ctx, AOffset + u, N-u, BOffset + v, M-v)EndIfEndIfEndProcedure; Main Diff function;Procedure Diff(*Ctx.DiffContext, *BufferA, SizeA, *BufferB, SizeB, Flags = 0)Protected N, M, MAX, OffsetN = Diff_PreProcess(*BufferA, SizeA, *Ctx\A(), Flags)M = Diff_PreProcess(*BufferB, SizeB, *Ctx\B(), Flags)*Ctx\LineCountA = N*Ctx\LineCountB = MDim *Ctx\FV((N + M)*4+1)Dim *Ctx\RV((N + M)*4+1)ClearList(*Ctx\Edits()); Note:; The handling of the D=1 Case in FindMiddleSnake() assumes that we always start; with an inset Or delete. So strip off common lines at the start to ensure thisOffset = 0While Offset < N And Offset < M And *Ctx\A(Offset)\Checksum = *Ctx\B(Offset)\ChecksumOffset + 1WendDiff_AddEdit(*Ctx, #DIFF_Match, *Ctx\A(), 0, Offset)Diff_Recursive(*Ctx, Offset, N-Offset, Offset, M-Offset)FreeArray(*Ctx\FV())FreeArray(*Ctx\RV())EndProcedure; Some debugging utilities;CompilerIf #PB_Compiler_DebuggerProcedure DebugEdit(Prefix,ドル List Edits.DiffEdit(), Array Lines.DiffLine(1))Protected Line, Length, Line$For Line = Edits()\StartLine To Edits()\StartLine + Edits()\Lines - 1Line$ = PeekS(Lines(Line)\Start, Lines(Line)\Length, #PB_Ascii)Line$ = RTrim(RTrim(Line,ドル Chr(10)), Chr(13)) ; trim newlineDebug Prefix$ + " " + Line$Next LineEndProcedureProcedure DebugDiff(*Ctx.DiffContext)ForEach *Ctx\Edits()Select *Ctx\Edits()\OpCase #DIFF_MatchDebugEdit(" ", *Ctx\Edits(), *Ctx\A())Case #DIFF_DeleteDebugEdit("-", *Ctx\Edits(), *Ctx\A())Case #DIFF_InsertDebugEdit("+", *Ctx\Edits(), *Ctx\B())EndSelectNext *Ctx\Edits()EndProcedureProcedure DebugFile(FileA,ドル FileB$)Protected *BufferA, SizeA, *BufferB, SizeB, Context.DiffContextIf ReadFile(0, FileA$)SizeA = Lof(0)*BufferA = AllocateMemory(SizeA)ReadData(0, *BufferA, SizeA)CloseFile(0)ElseDebug "Cannot load: " + FileA$EndEndIfIf ReadFile(0, FileB$)SizeB = Lof(0)*BufferB = AllocateMemory(SizeB)ReadData(0, *BufferB, SizeB)CloseFile(0)ElseDebug "Cannot load: " + FileB$EndEndIfDiff(@Context, *BufferA, SizeA, *BufferB, SizeB)DebugDiff(@Context)FreeMemory(*BufferA)FreeMemory(*BufferB)EndProcedure;DebugFile("C:\PureBasic\Test\diff\Preferences0081円.pb", "C:\PureBasic\Test\diff\Preferences0082円.pb")CompilerEndIf
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。