1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2006-2009, 2011 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "SVNLineDiff.h"
22 const svn_diff_fns2_t
SVNLineDiff::SVNLineDiff_vtable
=
24 SVNLineDiff::datasources_open
,
25 SVNLineDiff::datasource_close
,
26 SVNLineDiff::next_token
,
27 SVNLineDiff::compare_token
,
28 SVNLineDiff::discard_token
,
29 SVNLineDiff::discard_all_token
32 #define SVNLINEDIFF_CHARTYPE_NONE 0
33 #define SVNLINEDIFF_CHARTYPE_ALPHANUMERIC 1
34 #define SVNLINEDIFF_CHARTYPE_SPACE 2
35 #define SVNLINEDIFF_CHARTYPE_OTHER 3
37 typedef void (*LineParser
)(LPCTSTR line
, apr_size_t lineLength
, std::vector
<std::wstring
> &tokens
);
39 void SVNLineDiff::ParseLineWords(
40 LPCTSTR line
, apr_size_t lineLength
, std::vector
<std::wstring
> &tokens
)
43 int prevCharType
= SVNLINEDIFF_CHARTYPE_NONE
;
44 tokens
.reserve(lineLength
/2);
45 for (apr_size_t i
= 0; i
< lineLength
; ++i
)
48 IsCharAlphaNumeric(line
[i
]) ? SVNLINEDIFF_CHARTYPE_ALPHANUMERIC
:
49 IsCharWhiteSpace(line
[i
]) ? SVNLINEDIFF_CHARTYPE_SPACE
:
50 SVNLINEDIFF_CHARTYPE_OTHER
;
52 // Token is a sequence of either alphanumeric or whitespace characters.
53 // Treat all other characters as a separate tokens.
54 if (charType
== prevCharType
&& charType
!= SVNLINEDIFF_CHARTYPE_OTHER
)
59 tokens
.push_back(token
);
62 prevCharType
= charType
;
65 tokens
.push_back(token
);
68 void SVNLineDiff::ParseLineChars(
69 LPCTSTR line
, apr_size_t lineLength
, std::vector
<std::wstring
> &tokens
)
72 for (apr_size_t i
= 0; i
< lineLength
; ++i
)
75 tokens
.push_back(token
);
79 svn_error_t
* SVNLineDiff::datasources_open(void *baton
, apr_off_t
*prefix_lines
, apr_off_t
* /*suffix_lines*/, const svn_diff_datasource_e
*datasources
, apr_size_t datasource_len
)
81 SVNLineDiff
* linediff
= (SVNLineDiff
*)baton
;
82 LineParser parser
= linediff
->m_bWordDiff
? ParseLineWords
: ParseLineChars
;
83 for (apr_size_t i
= 0; i
< datasource_len
; i
++)
85 switch (datasources
[i
])
87 case svn_diff_datasource_original
:
88 parser(linediff
->m_line1
, linediff
->m_line1length
, linediff
->m_line1tokens
);
90 case svn_diff_datasource_modified
:
91 parser(linediff
->m_line2
, linediff
->m_line2length
, linediff
->m_line2tokens
);
95 // Don't claim any prefix matches.
101 svn_error_t
* SVNLineDiff::datasource_close(void * /*baton*/, svn_diff_datasource_e
/*datasource*/)
106 void SVNLineDiff::NextTokenWords(
107 apr_uint32_t
* hash
, void** token
, apr_size_t
& linePos
, const std::vector
<std::wstring
>& tokens
)
109 if (linePos
< tokens
.size())
111 *token
= (void*)tokens
[linePos
].c_str();
112 *hash
= SVNLineDiff::Adler32(0, tokens
[linePos
].c_str(), tokens
[linePos
].size());
117 void SVNLineDiff::NextTokenChars(
118 apr_uint32_t
* hash
, void** token
, apr_size_t
& linePos
, LPCTSTR line
, apr_size_t lineLength
)
120 if (linePos
< lineLength
)
122 *token
= (void*)&line
[linePos
];
123 *hash
= line
[linePos
];
128 svn_error_t
* SVNLineDiff::next_token(
129 apr_uint32_t
* hash
, void ** token
, void * baton
, svn_diff_datasource_e datasource
)
131 SVNLineDiff
* linediff
= (SVNLineDiff
*)baton
;
135 case svn_diff_datasource_original
:
136 if (linediff
->m_bWordDiff
)
137 NextTokenWords(hash
, token
, linediff
->m_line1pos
, linediff
->m_line1tokens
);
139 NextTokenChars(hash
, token
, linediff
->m_line1pos
, linediff
->m_line1
, linediff
->m_line1length
);
141 case svn_diff_datasource_modified
:
142 if (linediff
->m_bWordDiff
)
143 NextTokenWords(hash
, token
, linediff
->m_line2pos
, linediff
->m_line2tokens
);
145 NextTokenChars(hash
, token
, linediff
->m_line2pos
, linediff
->m_line2
, linediff
->m_line2length
);
151 svn_error_t
* SVNLineDiff::compare_token(void * baton
, void * token1
, void * token2
, int * compare
)
153 SVNLineDiff
* linediff
= (SVNLineDiff
*)baton
;
154 if (linediff
->m_bWordDiff
)
156 LPCTSTR s1
= (LPCTSTR
)token1
;
157 LPCTSTR s2
= (LPCTSTR
)token2
;
160 *compare
= _tcscmp(s1
, s2
);
165 TCHAR
* c1
= (TCHAR
*)token1
;
166 TCHAR
* c2
= (TCHAR
*)token2
;
180 void SVNLineDiff::discard_token(void * /*baton*/, void * /*token*/)
184 void SVNLineDiff::discard_all_token(void * /*baton*/)
188 SVNLineDiff::SVNLineDiff()
199 m_pool
= svn_pool_create(NULL
);
202 SVNLineDiff::~SVNLineDiff()
204 svn_pool_destroy(m_pool
);
207 bool SVNLineDiff::Diff(svn_diff_t
**diff
, LPCTSTR line1
, apr_size_t len1
, LPCTSTR line2
, apr_size_t len2
, bool bWordDiff
)
210 svn_pool_clear(m_subpool
);
212 m_subpool
= svn_pool_create(m_pool
);
214 if (m_subpool
== NULL
)
217 m_bWordDiff
= bWordDiff
;
220 m_line1length
= len1
? len1
: _tcslen(m_line1
);
221 m_line2length
= len2
? len2
: _tcslen(m_line2
);
225 m_line1tokens
.clear();
226 m_line2tokens
.clear();
227 svn_error_t
* err
= svn_diff_diff_2(diff
, this, &SVNLineDiff_vtable
, m_subpool
);
230 svn_error_clear(err
);
231 svn_pool_clear(m_subpool
);
237 #define ADLER_MOD_BASE 65521
238 #define ADLER_MOD_BLOCK_SIZE 5552
240 apr_uint32_t
SVNLineDiff::Adler32(apr_uint32_t checksum
, const WCHAR
*data
, apr_size_t len
)
242 const unsigned char * input
= (const unsigned char *)data
;
243 apr_uint32_t s1
= checksum
& 0xFFFF;
244 apr_uint32_t s2
= checksum
>> 16;
247 apr_size_t blocks
= len
/ ADLER_MOD_BLOCK_SIZE
;
249 len
%= ADLER_MOD_BLOCK_SIZE
;
253 int count
= ADLER_MOD_BLOCK_SIZE
;
261 s1
%= ADLER_MOD_BASE
;
262 s2
%= ADLER_MOD_BASE
;
272 return ((s2
% ADLER_MOD_BASE
) << 16) | (s1
% ADLER_MOD_BASE
);
275 bool SVNLineDiff::IsCharWhiteSpace(TCHAR c
)
277 return (c
== ' ') || (c
== '\t');
280 bool SVNLineDiff::ShowInlineDiff(svn_diff_t
* diff
)
282 svn_diff_t
* tempdiff
= diff
;
283 apr_size_t diffcounts
= 0;
284 apr_off_t origsize
= 0;
285 apr_off_t diffsize
= 0;
288 if (tempdiff
->type
== svn_diff__type_common
)
290 origsize
+= tempdiff
->original_length
;
295 diffsize
+= tempdiff
->original_length
;
296 diffsize
+= tempdiff
->modified_length
;
298 tempdiff
= tempdiff
->next
;
300 return (origsize
> 0.5 * diffsize
+ 1.0 * diffcounts
); // Multiplier values may be subject to individual preferences