1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-07 19:18:19 +02:00
Beef/IDEHelper/BfDiff.cpp
2019-08-23 11:56:54 -07:00

1166 lines
No EOL
33 KiB
C++

/*#include <stdio.h>
#include <vector>
#include <map>
#include <stdio.h>
#include <iostream>
#include <assert.h>
#include <algorithm>
#include <fstream>
#include <consoleapi.h>*/
#include "Beef/BfCommon.h"
#include "BeefySysLib/util/Dictionary.h"
#include "BeefySysLib/util/Array.h"
#include "BeefySysLib/util/AllocDebug.h"
USING_NS_BF;
static FILE* gLogFP;
/*static void Log(const char* fmt ...)
{
if (gLogFP == NULL)
gLogFP = fopen("diff_dbg.txt", "wb");
va_list argList;
va_start(argList, fmt);
String aResult = vformat(fmt, argList);
va_end(argList);
fwrite(aResult.c_str(), 1, aResult.length(), gLogFP);
fflush(gLogFP);
}
static void LogClose()
{
if (gLogFP != NULL)
{
fclose(gLogFP);
gLogFP = NULL;
}
}*/
struct DuplicateLineData
{
Array<int> mRefs[2];
Array<int> mDeleteEditCommandIndices;
};
struct LineData
{
public:
//String mString;
int mBufStart;
int mBufLength;
DuplicateLineData* mDuplicateLineData;
};
bool operator!=(const LineData &a, const LineData& b)
{
return a.mDuplicateLineData != b.mDuplicateLineData;
}
typedef Dictionary<int64, DuplicateLineData*> DuplicateLineDataMap;
static DuplicateLineDataMap gDuplicateLineDataMap;
struct LineMapping
{
int mAIdx;
int mBIdx;
LineMapping* mPrevStackTop;
};
enum EditCommandKind
{
EditCommandKind_None,
EditCommandKind_InsertLine,
EditCommandKind_DeleteLine,
EditCommandKind_InsertChar,
EditCommandKind_DeleteChar,
// Forward moves:
EditCommandKind_MoveLineFrom, // Moves line down
EditCommandKind_MoveLineTo, // Moves line up into us
// Reverse moves:
EditCommandKind_MoveLine, // For reverse moves
};
typedef unsigned char uint8;
struct EditCommand
{
public:
int mAIdx;
int mBIdx;
short mCount;
uint8 mCmd;
public:
EditCommand()
{
mAIdx = -1;
mBIdx = -1;
mCount = 1;
mCmd = 0;
}
};
class BfDiff
{
public:
Array<EditCommand> mEditCommands;
const char* mAText;
const char* mBText;
Array<LineData> mALineDatas;
Array<LineData> mBLineDatas;
public:
int GetInsertPosition(int lineNum);
void ProcessText(const char* bufferData, Array<LineData>& lines, int fileNum);
int AddEditCommand(int cmdInsertPos, const EditCommand& editCommand);
public:
void Compare(const char* text1, const char* text2);
void GenerateMoveCommands();
};
int BfDiff::AddEditCommand(int cmdInsertPos, const EditCommand& editCommand)
{
//Log("AddEditCommand Pos:%d Cmd:%d A:%d B:%d Count:%d\n", cmdInsertPos, editCommand.mCmd, editCommand.mAIdx, editCommand.mBIdx, editCommand.mCount);
if (cmdInsertPos == -1)
cmdInsertPos = (int)mEditCommands.size();
if ((cmdInsertPos > 0) && (cmdInsertPos - 1 < mEditCommands.size()) && (editCommand.mCount == 1))
{
EditCommand& prevEditCommand = mEditCommands[cmdInsertPos - 1];
if ((editCommand.mCmd == EditCommandKind_InsertLine) || (editCommand.mCmd == EditCommandKind_InsertChar))
{
if ((prevEditCommand.mCmd == editCommand.mCmd) &&
(prevEditCommand.mCount < 0x7FFF) &&
(editCommand.mAIdx == prevEditCommand.mAIdx) &&
(editCommand.mBIdx == prevEditCommand.mBIdx + prevEditCommand.mCount))
{
prevEditCommand.mCount += editCommand.mCount;
return cmdInsertPos;
}
}
else if ((editCommand.mCmd == EditCommandKind_DeleteLine) || (editCommand.mCmd == EditCommandKind_DeleteChar))
{
if ((prevEditCommand.mCmd == editCommand.mCmd) &&
(prevEditCommand.mCount < 0x7FFF) &&
(editCommand.mAIdx == prevEditCommand.mAIdx + prevEditCommand.mCount))
{
prevEditCommand.mCount += editCommand.mCount;
return cmdInsertPos;
}
}
}
mEditCommands.Insert(cmdInsertPos, editCommand);
return cmdInsertPos + 1;
}
class DiffEngineBase
{
public:
virtual void EmitEntryEqual(int aIdx, int bIdx) {}
virtual void EmitEntryDelete(int aIdx) {}
virtual void EmitEntryInsert(int aIdx, int bIdx) {}
};
template <typename TElement, typename TContainer>
class DiffEngine : public DiffEngineBase
{
public:
TContainer mA;
TContainer mB;
public:
DiffEngine(TContainer a, TContainer b) :
mA(a), mB(b)
{
}
void CalcMiddleSnake(int aOfs, int aLen, int bOfs, int bLen, int& xStart, int& yStart, int& xEnd, int& yEnd, int& totalDiff)
{
int maxD = (bLen + aLen + 1) / 2;
int delta = aLen - bLen;
int vOfs = maxD + abs(delta);
// v stores x values for k lines
Array<int> v;
v.Resize(vOfs * 2 + 1);
Array<int> rv;
rv.Resize(vOfs * 2 + 1);
rv[vOfs + delta] = aLen;
for (int d = 0; d <= maxD; d++)
{
// Forward
for (int k = -d; k <= d; k += 2)
{
int newX;
if ((k == -d) || ((k != d) && (v[vOfs + k + 1] > v[vOfs + k - 1])))
{
// 'k' right of us moves down
xStart = v[vOfs + k + 1];
yStart = xStart - (k + 1);
newX = xStart;
}
else
{
// 'k' below of us moves right
xStart = v[vOfs + k - 1];
yStart = xStart - (k - 1);
newX = xStart + 1;
}
int beforeDiagX = newX;
int newY = newX - k;
while (newX < aLen)
{
if (newY >= bLen)
break;
if (mA[aOfs + newX] != mB[bOfs + newY])
break;
newX++;
newY++;
}
// This is a complicated text, but see Myers paper-
// But basically this is checking for overlap, where the forward reaches the reverse
if ((delta % 2 != 0) && (k >= delta - (d - 1)) && (k <= delta + (d - 1)) && (newX >= rv[vOfs + k]))
{
xEnd = newX;
yEnd = newY;
totalDiff = 2 * d - 1;
return;
}
v[vOfs + k] = newX;
}
// Reverse
for (int k = -d + delta; k <= d + delta; k += 2)
{
int newX;
if (d == 0)
{
newX = aLen;
xEnd = aLen;
yEnd = xEnd - k;
}
else if ((k == -d + delta) || ((k != d + delta) && (rv[vOfs + k + 1] < rv[vOfs + k - 1])))
{
// 'k' right of us moves left
xEnd = rv[vOfs + k + 1];
yEnd = xEnd - (k + 1);
newX = xEnd - 1;
}
else
{
// 'k' below of us moves up
xEnd = rv[vOfs + k - 1];
yEnd = xEnd - (k - 1);
newX = xEnd;
}
int beforeDiagX = newX;
int newY = newX - k;
while (newX > 0)
{
if (newY <= 0)
break;
if (mA[aOfs + newX - 1] != mB[bOfs + newY - 1])
break;
newX--;
newY--;
}
// This is a complicated text, but see Myers paper-
// But basically this is checking for overlap, where the forward reaches the reverse
if ((delta % 2 == 0) && (k >= -d) && (k <= d) && ((newX <= v[vOfs + k])))
{
xStart = newX;
yStart = newY;
totalDiff = 2 * d;
return;
}
rv[vOfs + k] = newX;
}
}
assert(0 == "Middle snake not found");
}
void AddDiffSpan(int aOfs, int bOfs, int xStart, int yStart, int xEnd, int yEnd, bool isReverse = false)
{
if (isReverse)
{
// Do diagonal first
while ((xStart < xEnd) && (yStart < yEnd))
{
//std::cout << aOfs + xStart << " = " << bOfs + yStart << std::endl;
int aIdx = aOfs + xStart;
int bIdx = bOfs + yStart;
EmitEntryEqual(aIdx, bIdx);
xStart++;
yStart++;
}
}
int xDelta = xEnd - xStart;
int yDelta = yEnd - yStart;
if ((xDelta == 0) && (yDelta == 0))
return;
int deltaDiff = xDelta - yDelta;
if (deltaDiff > 0)
{
for (int i = 0; i < deltaDiff; i++)
{
int aIdx = aOfs + xStart + i;
EmitEntryDelete(aIdx);
}
}
else if (deltaDiff < 0)
{
int aIdx = aOfs + xStart;
for (int i = 0; i < -deltaDiff; i++)
{
int bIdx = bOfs + yStart + i;
EmitEntryInsert(aIdx, bIdx);
}
}
if (!isReverse)
{
// Do diagonal last
int diagLen = (xEnd - xStart) - BF_MAX(0, deltaDiff);
for (int sub = diagLen; sub > 0; sub--)
{
//std::cout << aOfs + xEnd - sub << " = " << bOfs + yEnd - sub << std::endl;
int aIdx = aOfs + xEnd - sub;
int bIdx = bOfs + yEnd - sub;
EmitEntryEqual(aIdx, bIdx);
//std::cout << aIdx << "=" << bIdx << "\t" << mB[bIdx].mString.c_str();
}
}
}
void MyersDiff(int aOfs, int aLen, int bOfs, int bLen)
{
//Log("MyersDiff AOfs:%d ALen:%d BOfs:%d BLen:%d\n", aOfs, aLen, bOfs, bLen);
if ((aLen == 0) || (bLen == 0))
{
AddDiffSpan(aOfs, bOfs, 0, 0, aLen, bLen);
return;
}
int xStart = 0;
int yStart = 0;
int xEnd = 0;
int yEnd = 0;
int totalDiff = 0;
CalcMiddleSnake(aOfs, aLen, bOfs, bLen, xStart, yStart, xEnd, yEnd, totalDiff);
if (totalDiff > 1)
{
MyersDiff(aOfs, xStart, bOfs, yStart); // Top left
AddDiffSpan(aOfs, bOfs, xStart, yStart, xEnd, yEnd, (totalDiff % 2) == 0);
MyersDiff(aOfs + xEnd, aLen - xEnd, bOfs + yEnd, bLen - yEnd); // Bottom right
}
else
{
if (totalDiff == 1) // Forward snake
AddDiffSpan(aOfs, bOfs, 0, 0, xStart, yStart);
AddDiffSpan(aOfs, bOfs, xStart, yStart, xEnd, yEnd, (totalDiff % 2) == 0);
}
}
};
class CharDiffEngine : public DiffEngine<char, const char*>
{
public:
BfDiff* mBfDiff;
int mCmdInsertPos;
int mAOffset;
CharDiffEngine(BfDiff* bfDiff, const char* a, const char* b) : DiffEngine(a, b)
{
mBfDiff = bfDiff;
mCmdInsertPos = (int)mBfDiff->mEditCommands.size();
mAOffset = 0;
}
virtual void EmitEntryDelete(int aIdx) override;
virtual void EmitEntryInsert(int aIdx, int bIdx) override;
};
///
void CharDiffEngine::EmitEntryDelete(int aIdx)
{
EditCommand editCommand;
editCommand.mCmd = EditCommandKind_DeleteChar;
editCommand.mAIdx = aIdx + mAOffset;
mCmdInsertPos = mBfDiff->AddEditCommand(mCmdInsertPos, editCommand);
}
void CharDiffEngine::EmitEntryInsert(int aIdx, int bIdx)
{
EditCommand editCommand;
editCommand.mCmd = EditCommandKind_InsertChar;
editCommand.mAIdx = aIdx + mAOffset;
editCommand.mBIdx = bIdx;
mCmdInsertPos = mBfDiff->AddEditCommand(mCmdInsertPos, editCommand);
}
//
class PatienceDiffEngine : public DiffEngine<LineData, Array<LineData> >
{
public:
BfDiff* mBfDiff;
public:
PatienceDiffEngine(BfDiff* bfDiff, Array<LineData>& a, Array<LineData>& b) : DiffEngine(a, b)
{
mBfDiff = bfDiff;
}
virtual void EmitEntryEqual(int aIdx, int bIdx);
virtual void EmitEntryDelete(int aIdx);
virtual void EmitEntryInsert(int aIdx, int bIdx);
void PatienceDiff(int aOfs, int aLen, int bOfs, int bLen)
{
//Log("Patience AOfs:%d ALen:%d BOfs:%d BLen:%d\n", aOfs, aLen, bOfs, bLen);
assert(aLen >= 0);
assert(bLen >= 0);
// Reduce head
while ((aLen > 0) && (bLen > 0))
{
/*if (aOfs == 1559)
{
Log("-Head AOfs:%d BOfs:%d APtr:%p BPtr:%p\n", aOfs, bOfs, mA[aOfs].mDuplicateLineData, mB[aOfs].mDuplicateLineData);
}*/
if (mA[aOfs] != mB[bOfs])
{
break;
}
//Log("1> ");
EmitEntryEqual(aOfs, bOfs);
aOfs++;
aLen--;
bOfs++;
bLen--;
}
if ((aLen == 0) || (bLen == 0))
{
AddDiffSpan(aOfs, bOfs, 0, 0, aLen, bLen);
return;
}
int matchTailLength = 0;
// Reduce tail
while ((aLen > 0) && (bLen > 0))
{
if (mA[aOfs + aLen - 1] != mB[bOfs + bLen - 1])
break;
matchTailLength++;
aLen--;
bLen--;
}
int rangeOfs[2] = {aOfs, bOfs};
int rangeLens[2] = {aLen, bLen};
Array<LineMapping> sharedUniqueMapping;
for (int aIdx = aOfs; aIdx < aOfs + aLen; aIdx++)
{
LineData* lineDataA = &mA[aIdx];
auto duplicateLineData = lineDataA->mDuplicateLineData;
int lineInstanceCounts[2] = {0, 0};
int matchIndices[2] = {-1, -1};
for (int bufIdx = 0; bufIdx < 2; bufIdx++)
{
for (int i = 0; i < (int)duplicateLineData->mRefs[bufIdx].size(); i++)
{
int lineNum = duplicateLineData->mRefs[bufIdx][i];
if ((lineNum >= rangeOfs[bufIdx]) && (lineNum < rangeOfs[bufIdx] + rangeLens[bufIdx]))
{
//Log("lineInstanceCounts match %d %d\n", bufIdx, lineNum);
lineInstanceCounts[bufIdx]++;
if (lineInstanceCounts[bufIdx] > 1)
break;
matchIndices[bufIdx] = lineNum;
}
}
}
if ((lineInstanceCounts[0] == 1) && (lineInstanceCounts[1] == 1))
{
// There's a single instance of this line
LineMapping lineMapping;
lineMapping.mAIdx = matchIndices[0];
lineMapping.mBIdx = matchIndices[1];
lineMapping.mPrevStackTop = NULL;
sharedUniqueMapping.push_back(lineMapping);
}
}
if (sharedUniqueMapping.size() > 0)
{
if ((sharedUniqueMapping.size() == 1) && (aLen == 1) && (bLen == 1))
{
//Log("2> ");
EmitEntryEqual(aOfs, bOfs);
return;
}
// Perform "Patience sort"
// This generates the longest span possible where the line numbers are ordered within both
// the 'A' buffer and the 'B' buffer
Array<LineMapping*> sortStacks;
for (int mappingIdx = 0; mappingIdx < (int)sharedUniqueMapping.size(); mappingIdx++)
{
LineMapping* lineMapping = &sharedUniqueMapping[mappingIdx];
int newBIdx = lineMapping->mBIdx;
bool found = false;
for (int stackIdx = 0; stackIdx < (int)sortStacks.size(); stackIdx++)
{
if (newBIdx < sortStacks[stackIdx]->mBIdx)
{
found = true;
sortStacks[stackIdx] = lineMapping;
if (stackIdx > 0)
lineMapping->mPrevStackTop = sortStacks[stackIdx - 1];
break;
}
}
if (!found)
{
if (sortStacks.size() != 0)
lineMapping->mPrevStackTop = sortStacks.back();
sortStacks.push_back(lineMapping);
}
}
Array<LineMapping*> sortedLineMapping;
sortedLineMapping.Resize(sortStacks.size());
int curSortedIdx = (int)sortedLineMapping.size() - 1;
LineMapping* checkLineMapping = sortStacks.back();
while (checkLineMapping != NULL)
{
sortedLineMapping[curSortedIdx] = checkLineMapping;
checkLineMapping = checkLineMapping->mPrevStackTop;
curSortedIdx--;
}
assert(curSortedIdx == -1);
int curAIdx = aOfs;
int curBIdx = bOfs;
for (int sortedIdx = 0; sortedIdx < (int)sortedLineMapping.size(); sortedIdx++)
{
LineMapping* lineMapping = sortedLineMapping[sortedIdx];
//PatienceDiff(curAIdx, lineMapping->mAIdx - curAIdx + 1, curBIdx, lineMapping->mBIdx - curBIdx + 1);
PatienceDiff(curAIdx, lineMapping->mAIdx - curAIdx, curBIdx, lineMapping->mBIdx - curBIdx);
//Log("3> ");
EmitEntryEqual(lineMapping->mAIdx, lineMapping->mBIdx);
curAIdx = lineMapping->mAIdx + 1;
curBIdx = lineMapping->mBIdx + 1;
}
PatienceDiff(curAIdx, aOfs + aLen - curAIdx, curBIdx, bOfs + bLen - curBIdx);
}
else
{
MyersDiff(aOfs, aLen, bOfs, bLen);
}
//Log("Patience EqSpan AOfs:%d BOfs:%d %d\n", aOfs + aLen, bOfs + bLen, matchTailLength);
for (int tailIdx = 0; tailIdx < matchTailLength; tailIdx++)
{
//Log("4> ");
EmitEntryEqual(aOfs + aLen + tailIdx, bOfs + bLen + tailIdx);
}
}
};
void PatienceDiffEngine::EmitEntryEqual(int aIdx, int bIdx)
{
LineData* aLineData = &mA[aIdx];
LineData* bLineData = &mB[bIdx];
bool matches = aLineData->mBufLength == bLineData->mBufLength;
if (matches)
{
for (int i = 0; i < aLineData->mBufLength; i++)
{
if (mBfDiff->mAText[aLineData->mBufStart + i] != mBfDiff->mBText[bLineData->mBufStart + i])
{
matches = false;
break;
}
}
}
//Log("Patience EmitEntryEqual A:%d B:%d Matches:%d\n", aIdx, bIdx, matches);
/*if (!matches)
{
Log(" ALen:%d BLen: %d\n", aLineData->mBufLength, bLineData->mBufLength);
}*/
if (!matches)
{
CharDiffEngine strDiffEngine(mBfDiff, mBfDiff->mAText, mBfDiff->mBText);
strDiffEngine.MyersDiff(aLineData->mBufStart, aLineData->mBufLength, bLineData->mBufStart, bLineData->mBufLength);
//SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
}
/*std::cout << aIdx << "=" << bIdx << "\t" << mB[bIdx].mString.c_str();
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE);*/
}
void PatienceDiffEngine::EmitEntryDelete(int aIdx)
{
/*SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED);
std::cout << "x" << aIdx << " \t" << mA[aIdx].mString.c_str();
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE);*/
EditCommand editCommand;
editCommand.mCmd = EditCommandKind_DeleteLine;
editCommand.mAIdx = aIdx;
mBfDiff->AddEditCommand(-1, editCommand);
//mA[aIdx].mDuplicateLineData->mDeleteEditCommandIndices.push_back((int)mBfDiff->mEditCommands.size() - 1);
}
void PatienceDiffEngine::EmitEntryInsert(int aIdx, int bIdx)
{
/*SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN);
std::cout << aIdx << "i" << bIdx << "\t" << mB[bIdx].mString.c_str();
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE);*/
EditCommand editCommand;
editCommand.mCmd = EditCommandKind_InsertLine;
editCommand.mAIdx = aIdx;
editCommand.mBIdx = bIdx;
mBfDiff->AddEditCommand(-1, editCommand);
}
// The line hash is used to determine whether a line appears only once in a given file.
// A hash collision has a potential to generate a slightly less ideal diff, but still
// a valid diff. Since we hash out the whitespace, we already handle the case where
// that have whitespace difference need to be modified to be exact matches, that code
// will also transform a compltely wrong hash collision string into a correct string
// as well
#define MIXIN_CHAR_HASH(c) hash = ((hash ^ c) << 5) - hash
int64 GetLineHash(const char* str, int strStart, int strLen)
{
//String outStr;
bool hadWS = false;
bool hadNonWS = false;
int64 hash = 0;
for (int i = strStart; i <= strStart + strLen; i++)
{
bool remove = false;
char c = str[i];
bool isWS = iswspace((unsigned char)c) != 0;
if (!isWS)
{
if ((hadWS) && (hadNonWS))
{
//MIXIN_CHAR_HASH(' ');
//outStr += ' ';
}
MIXIN_CHAR_HASH(c);
//outStr += c;
hadNonWS = true;
hadWS = false;
}
else
{
hadWS = true;
}
}
return hash;
}
void BfDiff::ProcessText(const char* bufferData, Array<LineData>& lines, int fileNum)
{
//Log("BfDiff::ProcessText %d\n", fileNum);
int textHash = 0;
int lineNum = 0;
int lineStart = 0;
for (int i = 0; true; i++)
{
char c = bufferData[i];
textHash = ((textHash ^ c) << 5) - textHash;
if (c == 0)
break;
int lineEnd = -1;
if (c == '\r')
continue;
if (c == '\n')
lineEnd = i;
else if (bufferData[i + 1] == '\0')
lineEnd = i;
if (lineEnd != -1)
{
int lineEnd = i;
//if ((lineEnd > 0) && (bufferData[lineEnd - 1] == '\r'))
//lineEnd--;
//String strippedStr = StripWhitespace(line);
int64 lineHash = GetLineHash(bufferData, lineStart, lineEnd - lineStart);
//Log(" Line:%d Start: %d Len: %d Hash: %d\n", lineNum, lineStart, lineEnd - lineStart, lineHash);
/*auto insertPair = gDuplicateLineDataMap.insert(DuplicateLineDataMap::value_type(lineHash, DuplicateLineData()));
DuplicateLineData* duplicateLineData = &insertPair.first->second;*/
DuplicateLineData* duplicateLineData = NULL;
DuplicateLineData** duplicateLineDataPtr = NULL;
if (gDuplicateLineDataMap.TryAdd(lineHash, NULL, &duplicateLineDataPtr))
{
duplicateLineData = new DuplicateLineData();
*duplicateLineDataPtr = duplicateLineData;
}
else
duplicateLineData = *duplicateLineDataPtr;
duplicateLineData->mRefs[fileNum].push_back(lineNum);
lineNum++;
LineData lineData;
//lineData.mString = String(bufferData + lineStart, bufferData + lineEnd + 1);
lineData.mBufStart = lineStart;
lineData.mBufLength = lineEnd - lineStart + 1;
lineData.mDuplicateLineData = duplicateLineData;
lines.push_back(lineData);
lineStart = i + 1;
}
}
//Log("Text Hash: %d\n", textHash);
}
void BfDiff::GenerateMoveCommands()
{
#if 0
for (int editCmdIdx = 0; editCmdIdx < (int)mEditCommands.size(); editCmdIdx++)
{
EditCommand* editCommand = &mEditCommands[editCmdIdx];
if (editCommand->mCmd == EditCommandKind_InsertLine)
{
LineData* bLineData = &mBLineDatas[editCommand->mBIdx];
if (bLineData->mDuplicateLineData->mDeleteEditCommandIndices.size() > 0)
{
int deleteCmdIdx = bLineData->mDuplicateLineData->mDeleteEditCommandIndices[0];
bLineData->mDuplicateLineData->mDeleteEditCommandIndices.erase(bLineData->mDuplicateLineData->mDeleteEditCommandIndices.begin());
// Delete followed by insert
if (deleteCmdIdx < editCmdIdx)
{
EditCommand* deleteCommand = &mEditCommands[deleteCmdIdx];
// With 'MoveLine', mAIdx is the src within A, and mBIdx is the dest within A
deleteCommand->mCmd = EditCommandKind_MoveLineFrom;
editCommand->mCmd = EditCommandKind_MoveLineTo;
editCommand->mAltParam = deleteCmdIdx;
}
else // Insert followed by a delete
{
EditCommand* deleteCommand = &mEditCommands[deleteCmdIdx];
LineData* aLineData = &mALineDatas[deleteCommand->mAIdx];
LineData* aDestLineData = &mALineDatas[editCommand->mAIdx];
LineData* bLineData = &mBLineDatas[editCommand->mBIdx];
editCommand->mCmd = EditCommandKind_MoveLine;
editCommand->mAltParam = deleteCommand->mAIdx;
deleteCommand->mCmd = EditCommandKind_None;
//TODO: FIgure out how to insert this. These edits cause our deleteEditCommandIndices to get borked
/*CharDiffEngine strDiffEngine(mAText, mBText);
strDiffEngine.mCmdInsertPos = editCmdIdx + 1;
strDiffEngine.mAOffset = aDestLineData->mBufStart - aLineData->mBufStart;
strDiffEngine.MyersDiff(aLineData->mBufStart, aLineData->mBufLength, bLineData->mBufStart, bLineData->mBufLength);*/
}
}
}
}
#endif
}
int BfDiff::GetInsertPosition(int lineNum)
{
if (lineNum < (int)mALineDatas.size())
return mALineDatas[lineNum].mBufStart;
if (lineNum == 0)
return 0;
return mALineDatas.back().mBufStart + mALineDatas.back().mBufLength;
}
#if 0
void ApplyEditCommands()
{
outA = mAText;
int aOffset = 0;
Array<int> deferredFromSources;
for (int i = 0; i < (int)outA.size(); i++)
{
outA[i] = toupper(outA[i]);
}
for (int editCmdIdx = 0; editCmdIdx < (int)editCommands.size(); editCmdIdx++)
{
EditCommand* editCommand = &editCommands[editCmdIdx];
if (editCommand->mAIdx == -1)
continue;
if (editCommand->mCmd == EditCommandKind_DeleteLine)
{
// Delete line
//outA.erase(outA.begin() + (editCommand->mAIdx + aOffset));
LineData* lineData = &mALineDatas[editCommand->mAIdx];
outA.erase(outA.begin() + (lineData->mBufStart + aOffset), outA.begin() + (lineData->mBufStart + lineData->mBufLength + aOffset));
aOffset -= lineData->mBufLength;
}
else if (editCommand->mCmd == EditCommandKind_DeleteChar)
{
outA.erase(
outA.begin() + (editCommand->mAIdx + aOffset),
outA.begin() + (editCommand->mAIdx + 1 + aOffset));
aOffset--;
}
else if (editCommand->mCmd == EditCommandKind_InsertChar)
{
outA.insert(outA.begin() + (editCommand->mAIdx + aOffset),
mBText.begin() + editCommand->mBIdx,
mBText.begin() + editCommand->mBIdx + 1);
aOffset++;
}
else if (editCommand->mCmd == EditCommandKind_InsertLine)
{
// Insert line
int insertPosition = GetInsertPosition(editCommand->mAIdx);
LineData* bLineData = &mBLineDatas[editCommand->mBIdx];
outA.insert(outA.begin() + (insertPosition + aOffset),
mBText.begin() + bLineData->mBufStart, mBText.begin() + bLineData->mBufStart + bLineData->mBufLength);
aOffset += bLineData->mBufLength;
}
else if (editCommand->mCmd == EditCommandKind_MoveLineFrom)
{
LineData* aLineDataSrc = &mALineDatas[editCommand->mAIdx];
editCommand->mAltParam = aLineDataSrc->mBufStart + aOffset;
deferredFromSources.push_back(editCmdIdx);
//editC
// Move line
/*LineData* aLineDataSrc = &mALineDatas[editCommand->mAIdx];
//LineData* aLineDataDest = &mALineDatas[editCommand->mBIdx];
int aOrigDestBufStart = GetInsertPosition(editCommand->mBIdx);
int aDestBufStart = RemapForwardIndex(aOrigDestBufStart);
outA.insert(outA.begin() + (aDestBufStart + aOffset),
outA.begin() + (aLineDataSrc->mBufStart + aOffset),
outA.begin() + (aLineDataSrc->mBufStart + aOffset) + aLineDataSrc->mBufLength);
outA.erase(
outA.begin() + (aLineDataSrc->mBufStart + aOffset),
outA.begin() + (aLineDataSrc->mBufStart + aLineDataSrc->mBufLength + aOffset));
deferredOffsets.insert(IntIntMultimap::value_type(aOrigDestBufStart, aLineDataSrc->mBufLength));
aOffset -= aLineDataSrc->mBufLength; */
}
else if (editCommand->mCmd == EditCommandKind_MoveLineTo)
{
EditCommand* moveLineFromCmd = &editCommands[editCommand->mAltParam];
int insertPosition = GetInsertPosition(editCommand->mAIdx);
LineData* aLineDataSrc = &mALineDatas[moveLineFromCmd->mAIdx];
int aSrcAddr = moveLineFromCmd->mAltParam;
outA.insert(outA.begin() + (insertPosition + aOffset),
outA.begin() + aSrcAddr, outA.begin() + aSrcAddr + aLineDataSrc->mBufLength);
outA.erase(
outA.begin() + (aSrcAddr),
outA.begin() + (aSrcAddr + aLineDataSrc->mBufLength));
for (int deferredIdx = 0; deferredIdx < (int)deferredFromSources.size(); deferredIdx++)
{
int deferredCmdIdx = deferredFromSources[deferredIdx];
if (deferredCmdIdx == editCommand->mAltParam)
{
deferredFromSources.erase(deferredFromSources.begin() + deferredIdx);
deferredIdx--;
}
else
{
EditCommand* deferredCmd = &editCommands[deferredCmdIdx];
// Did we erase a block that comes before another deferred move block? If so, modify addr
if (aSrcAddr < deferredCmd->mAltParam)
deferredCmd->mAltParam -= aLineDataSrc->mBufLength;
}
}
//editCommand->mAltParam = aLineDataSrc->mBufStart + aOffset;
// Move line
/*LineData* aLineDataDest = &mALineDatas[editCommand->mAIdx];
LineData* aLineDataSrc = &mALineDatas[editCommand->mBIdx];
outA.erase(
outA.begin() + (aLineDataSrc->mBufStart + aOffset),
outA.begin() + (aLineDataSrc->mBufStart + aLineDataSrc->mBufLength + aOffset));
outA.insert(outA.begin() + (aLineDataDest->mBufStart + aOffset),
mAText.begin() + aLineDataSrc->mBufStart, mAText.begin() + aLineDataSrc->mBufStart + aLineDataSrc->mBufLength);*/
//aOffset += bLineData->mBufLength;
}
else if (editCommand->mCmd == EditCommandKind_MoveLine)
{
int insertPosition = GetInsertPosition(editCommand->mAIdx);
LineData* aLineDataSrc = &mALineDatas[editCommand->mAltParam];
int aSrcAddr = aLineDataSrc->mBufStart + aOffset;
outA.insert(outA.begin() + (insertPosition + aOffset),
outA.begin() + aSrcAddr,
outA.begin() + aSrcAddr + aLineDataSrc->mBufLength);
outA.erase(
outA.begin() + (aSrcAddr + aLineDataSrc->mBufLength),
outA.begin() + (aSrcAddr + aLineDataSrc->mBufLength * 2));
}
}
std::cout << std::endl;
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
/*for (auto& lineData : outA)
{
std::cout << lineData.mString.c_str() << std::endl;
}*/
std::cout << outA.c_str() << std::endl;
//
}
#endif
void BfDiff::Compare(const char* text1, const char* text2)
{
mAText = text1;
mBText = text2;
ProcessText(mAText, mALineDatas, 0);
ProcessText(mBText, mBLineDatas, 1);
PatienceDiffEngine diffEngine(this, mALineDatas, mBLineDatas);
diffEngine.PatienceDiff(0, (int)mALineDatas.size(), 0, (int)mBLineDatas.size());
//GenerateMoveCommands();
}
/////////
BF_EXPORT const char* BF_CALLTYPE BfDiff_DiffText(const char* text1, const char* text2)
{
gDuplicateLineDataMap.Clear();
BfDiff bfDiff;
bfDiff.Compare(text1, text2);
int aOffset = 0;
Array<int> deferredFromSources;
String& outString = *gTLStrReturn.Get();
outString.clear();
for (int editCmdIdx = 0; editCmdIdx < (int)bfDiff.mEditCommands.size(); editCmdIdx++)
{
EditCommand* editCommand = &bfDiff.mEditCommands[editCmdIdx];
if (editCommand->mAIdx == -1)
continue;
if (editCommand->mCmd == EditCommandKind_DeleteLine)
{
// Delete line
//outA.erase(outA.begin() + (editCommand->mAIdx + aOffset));
LineData* lineDataStart = &bfDiff.mALineDatas[editCommand->mAIdx];
LineData* lineDataEnd = &bfDiff.mALineDatas[editCommand->mAIdx + editCommand->mCount - 1];
//outA.erase(outA.begin() + (lineData->mBufStart + aOffset), outA.begin() + (lineData->mBufStart + lineData->mBufLength + aOffset));
int len = lineDataEnd->mBufStart + lineDataEnd->mBufLength - lineDataStart->mBufStart;
outString += StrFormat("- %d %d %d\n", lineDataStart->mBufStart + aOffset, len, editCommand->mAIdx);
aOffset -= len;
}
else if (editCommand->mCmd == EditCommandKind_DeleteChar)
{
/*outA.erase(
outA.begin() + (editCommand->mAIdx + aOffset),
outA.begin() + (editCommand->mAIdx + 1 + aOffset));*/
outString += StrFormat("- %d %d\n", editCommand->mAIdx + aOffset, editCommand->mCount);
aOffset -= editCommand->mCount;
}
else if (editCommand->mCmd == EditCommandKind_InsertChar)
{
/*outA.insert(outA.begin() + (editCommand->mAIdx + aOffset),
mBText.begin() + editCommand->mBIdx,
mBText.begin() + editCommand->mBIdx + 1);*/
outString += StrFormat("+ %d %d %d\n", editCommand->mAIdx + aOffset, editCommand->mBIdx, editCommand->mCount);
aOffset += editCommand->mCount;
}
else if (editCommand->mCmd == EditCommandKind_InsertLine)
{
// Insert line
int insertPosition = bfDiff.GetInsertPosition(editCommand->mAIdx);
LineData* bLineDataStart = &bfDiff.mBLineDatas[editCommand->mBIdx];
LineData* bLineDataEnd = &bfDiff.mBLineDatas[editCommand->mBIdx + editCommand->mCount - 1];
int len = bLineDataEnd->mBufStart + bLineDataEnd->mBufLength - bLineDataStart->mBufStart;
/*outA.insert(outA.begin() + (insertPosition + aOffset),
mBText.begin() + bLineData->mBufStart, mBText.begin() + bLineData->mBufStart + bLineData->mBufLength);*/
outString += StrFormat("+ %d %d %d %d\n", insertPosition + aOffset, bLineDataStart->mBufStart, len, editCommand->mAIdx);
aOffset += len;
}
else if (editCommand->mCmd == EditCommandKind_MoveLineFrom)
{
BF_FATAL("Not implemented");
/*LineData* aLineDataSrc = &mALineDatas[editCommand->mAIdx];
editCommand->mAltParam = aLineDataSrc->mBufStart + aOffset;
deferredFromSources.push_back(editCmdIdx);*/
//editC
// Move line
/*LineData* aLineDataSrc = &mALineDatas[editCommand->mAIdx];
//LineData* aLineDataDest = &mALineDatas[editCommand->mBIdx];
int aOrigDestBufStart = GetInsertPosition(editCommand->mBIdx);
int aDestBufStart = RemapForwardIndex(aOrigDestBufStart);
outA.insert(outA.begin() + (aDestBufStart + aOffset),
outA.begin() + (aLineDataSrc->mBufStart + aOffset),
outA.begin() + (aLineDataSrc->mBufStart + aOffset) + aLineDataSrc->mBufLength);
outA.erase(
outA.begin() + (aLineDataSrc->mBufStart + aOffset),
outA.begin() + (aLineDataSrc->mBufStart + aLineDataSrc->mBufLength + aOffset));
deferredOffsets.insert(IntIntMultimap::value_type(aOrigDestBufStart, aLineDataSrc->mBufLength));
aOffset -= aLineDataSrc->mBufLength; */
}
else if (editCommand->mCmd == EditCommandKind_MoveLineTo)
{
BF_FATAL("Not implemented");
/*EditCommand* moveLineFromCmd = &editCommands[editCommand->mAltParam];
int insertPosition = bfDiff.GetInsertPosition(editCommand->mAIdx);
LineData* aLineDataSrc = &bfDiff.mALineDatas[moveLineFromCmd->mAIdx];
int aSrcAddr = moveLineFromCmd->mAltParam;
outA.insert(outA.begin() + (insertPosition + aOffset),
outA.begin() + aSrcAddr, outA.begin() + aSrcAddr + aLineDataSrc->mBufLength);
outA.erase(
outA.begin() + (aSrcAddr),
outA.begin() + (aSrcAddr + aLineDataSrc->mBufLength));
for (int deferredIdx = 0; deferredIdx < (int)deferredFromSources.size(); deferredIdx++)
{
int deferredCmdIdx = deferredFromSources[deferredIdx];
if (deferredCmdIdx == editCommand->mAltParam)
{
deferredFromSources.erase(deferredFromSources.begin() + deferredIdx);
deferredIdx--;
}
else
{
EditCommand* deferredCmd = &editCommands[deferredCmdIdx];
// Did we erase a block that comes before another deferred move block? If so, modify addr
if (aSrcAddr < deferredCmd->mAltParam)
deferredCmd->mAltParam -= aLineDataSrc->mBufLength;
}
} */
//editCommand->mAltParam = aLineDataSrc->mBufStart + aOffset;
// Move line
/*LineData* aLineDataDest = &mALineDatas[editCommand->mAIdx];
LineData* aLineDataSrc = &mALineDatas[editCommand->mBIdx];
outA.erase(
outA.begin() + (aLineDataSrc->mBufStart + aOffset),
outA.begin() + (aLineDataSrc->mBufStart + aLineDataSrc->mBufLength + aOffset));
outA.insert(outA.begin() + (aLineDataDest->mBufStart + aOffset),
mAText.begin() + aLineDataSrc->mBufStart, mAText.begin() + aLineDataSrc->mBufStart + aLineDataSrc->mBufLength);*/
//aOffset += bLineData->mBufLength;
}
else if (editCommand->mCmd == EditCommandKind_MoveLine)
{
BF_FATAL("Not implemented");
/*int insertPosition = GetInsertPosition(editCommand->mAIdx);
LineData* aLineDataSrc = &mALineDatas[editCommand->mAltParam];
int aSrcAddr = aLineDataSrc->mBufStart + aOffset;
outA.insert(outA.begin() + (insertPosition + aOffset),
outA.begin() + aSrcAddr,
outA.begin() + aSrcAddr + aLineDataSrc->mBufLength);
outA.erase(
outA.begin() + (aSrcAddr + aLineDataSrc->mBufLength),
outA.begin() + (aSrcAddr + aLineDataSrc->mBufLength * 2));*/
}
}
for (auto& kv : gDuplicateLineDataMap)
delete kv.mValue;
//LogClose();
gDuplicateLineDataMap.Clear();
return outString.c_str();
}