PageRenderTime 40ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 1ms

/DiffMatchPatch.m

https://github.com/jessegrosjean/bdocuments
Objective C | 1686 lines | 1514 code | 154 blank | 18 comment | 467 complexity | f87b2a50bd37d60c4677e2036f7cc543 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. //
  2. // DiffMatchPatch.m
  3. // BDocuments
  4. //
  5. // Created by Jesse Grosjean on 10/4/08.
  6. // Copyright 2008 Hog Bay Software. All rights reserved.
  7. //
  8. #import "DiffMatchPatch.h"
  9. @interface NSString (DiffMatchPatchPrivate)
  10. - (NSInteger)JindexOf:(NSString *)aString;
  11. - (NSInteger)JindexOf:(NSString *)aString :(NSUInteger)fromIndex;
  12. - (NSInteger)JlastIndexOf:(NSString *)aString;
  13. - (NSInteger)JlastIndexOf:(NSString *)aString :(NSUInteger)fromIndex;
  14. - (NSString *)Jsubstring:(NSInteger)beginIndex;
  15. - (NSString *)Jsubstring:(NSInteger)beginIndex :(NSInteger)endIndex;
  16. - (NSString *)URLEncode;
  17. - (NSString *)URLDecode;
  18. @end
  19. @interface NSMutableString (DiffMatchPatchPrivate)
  20. - (void)unescapeForEncodeUriCompatability;
  21. @end
  22. @interface NSMutableArray (DiffMatchPatchPrivate)
  23. - (NSMutableArray *)JsubList:(NSInteger)fromIndex :(NSInteger)toIndex;
  24. @end
  25. @interface BArrayIterator : NSObject {
  26. NSMutableArray *array;
  27. NSInteger cursor;
  28. NSInteger lastRet;
  29. }
  30. - (id)initWithArray:(NSMutableArray *)anArray;
  31. - (BOOL)hasNext;
  32. - (BOOL)hasPrevious;
  33. - (id)next;
  34. - (id)previous;
  35. - (void)set:(id)object;
  36. - (void)add:(id)object;
  37. - (void)remove;
  38. @end
  39. @implementation DiffMatchPatch
  40. - (id)init {
  41. self = [super init];
  42. Diff_Timeout = 1.0f;
  43. Diff_EditCost = 4;
  44. Diff_DualThreshold = 32;
  45. Match_Balance = 0.5f;
  46. Match_Threshold = 0.5f;
  47. Match_MinLength = 100;
  48. Match_MaxLength = 1000;
  49. Patch_Margin = 4;
  50. Match_MaxBits = 32;
  51. return self;
  52. }
  53. @synthesize Diff_Timeout;
  54. @synthesize Diff_EditCost;
  55. @synthesize Diff_DualThreshold;
  56. @synthesize Match_Balance;
  57. @synthesize Match_Threshold;
  58. @synthesize Match_MinLength;
  59. @synthesize Match_MaxLength;
  60. @synthesize Patch_Margin;
  61. @synthesize Match_MaxBits;
  62. - (NSMutableArray *)diffMainText1:(NSString *)text1 text2:(NSString *)text2 {
  63. if ([text1 isEqualToString:text2]) {
  64. return [NSArray arrayWithObject:[BDiff equal:text1]];
  65. }
  66. NSInteger commonLength = [self diffCommonPrefixText1:text1 text2:text2];
  67. NSString *commonPrefix = [text1 Jsubstring:0 :commonLength];
  68. text1 = [text1 Jsubstring:commonLength];
  69. text2 = [text2 Jsubstring:commonLength];
  70. commonLength = [self diffCommonSuffixText1:text1 text2:text2];
  71. NSString *commonSuffix = [text1 Jsubstring:[text1 length] - commonLength];
  72. text1 = [text1 Jsubstring:0 :[text1 length] - commonLength];
  73. text2 = [text2 Jsubstring:0 :[text2 length] - commonLength];
  74. NSMutableArray *diffs = [self diffComputeText1:text1 text2:text2];
  75. if ([commonPrefix length] > 0) [diffs insertObject:[BDiff equal:commonPrefix] atIndex:0];
  76. if ([commonSuffix length] > 0) [diffs addObject:[BDiff equal:commonSuffix]];
  77. [self diffCleanupMerge:diffs];
  78. return diffs;
  79. }
  80. - (NSMutableArray *)diffComputeText1:(NSString *)text1 text2:(NSString *)text2 {
  81. if ([text1 length] == 0) return [NSMutableArray arrayWithObject:[BDiff insert:text2]];
  82. if ([text2 length] == 0) return [NSMutableArray arrayWithObject:[BDiff delete:text1]];
  83. NSMutableArray *diffs = [NSMutableArray array];
  84. NSString *longText = [text1 length] > [text2 length] ? text1 : text2;
  85. NSString *shortText = [text1 length] > [text2 length] ? text2 : text1;
  86. NSUInteger i = [longText rangeOfString:shortText].location;
  87. if (i != NSNotFound) {
  88. BDiffOperation op = [text1 length] > [text2 length] ? BDiffDelete : BDiffInsert;
  89. [diffs addObject:[BDiff diffWithOperationType:op text:[longText Jsubstring:0 :i]]];
  90. [diffs addObject:[BDiff equal:shortText]];
  91. [diffs addObject:[BDiff diffWithOperationType:op text:[longText Jsubstring:i + [shortText length]]]];
  92. return diffs;
  93. }
  94. longText = shortText = nil;
  95. NSArray *hm = [self diffHalfMatchText1:text1 text2:text2];
  96. if (hm) {
  97. NSString *text1A = [hm objectAtIndex:0];
  98. NSString *text1B = [hm objectAtIndex:1];
  99. NSString *text2A = [hm objectAtIndex:2];
  100. NSString *text2B = [hm objectAtIndex:3];
  101. NSString *midCommon = [hm objectAtIndex:4];
  102. NSMutableArray *diffsA = [self diffMainText1:text1A text2:text2A];
  103. NSMutableArray *diffsB = [self diffMainText1:text1B text2:text2B];
  104. diffs = diffsA;
  105. [diffs addObject:[BDiff equal:midCommon]];
  106. [diffs addObjectsFromArray:diffsB];
  107. return diffs;
  108. }
  109. diffs = [self diffMapText1:text1 text2:text2];
  110. if (!diffs) {
  111. diffs = [NSMutableArray arrayWithObjects:[BDiff delete:text1], [BDiff insert:text2], nil];
  112. }
  113. return diffs;
  114. }
  115. - (NSMutableArray *)diffMapText1:(NSString *)text1 text2:(NSString *)text2 {
  116. NSTimeInterval timeEnd = [NSDate timeIntervalSinceReferenceDate] + Diff_Timeout;
  117. NSInteger maxD = [text1 length] + [text2 length] - 1;
  118. BOOL doubleEnd = Diff_DualThreshold * 2 < maxD;
  119. NSMutableArray *vMap1 = [NSMutableArray array];
  120. NSMutableArray *vMap2 = [NSMutableArray array];
  121. NSMutableDictionary *v1 = [NSMutableDictionary dictionary];
  122. NSMutableDictionary *v2 = [NSMutableDictionary dictionary];
  123. [v1 setObject:[NSNumber numberWithInteger:1] forKey:[NSNumber numberWithInteger:0]];
  124. [v2 setObject:[NSNumber numberWithInteger:1] forKey:[NSNumber numberWithInteger:0]];
  125. NSInteger x, y;
  126. NSNumber *footstep = nil;
  127. NSMutableDictionary *footsteps = [NSMutableDictionary dictionary];
  128. BOOL done = NO;
  129. BOOL front = (([text1 length] + [text2 length]) % 2 == 1);
  130. for (NSInteger d = 0; d < maxD; d++) {
  131. if (Diff_Timeout > 0 && [NSDate timeIntervalSinceReferenceDate] > timeEnd) {
  132. return nil;
  133. }
  134. [vMap1 addObject:[NSMutableSet set]];
  135. for (NSInteger k = -d; k <= d; k += 2) {
  136. if (k == -d || k != d && [[v1 objectForKey:[NSNumber numberWithInteger:k - 1]] integerValue] < [[v1 objectForKey:[NSNumber numberWithInteger:k + 1]] integerValue]) {
  137. x = [[v1 objectForKey:[NSNumber numberWithInteger:k + 1]] integerValue];
  138. } else {
  139. x = [[v1 objectForKey:[NSNumber numberWithInteger:k - 1]] integerValue] + 1;
  140. }
  141. y = x - k;
  142. if (doubleEnd) {
  143. footstep = [self diffFootprintX:x y:y];
  144. if (front && ([footsteps objectForKey:footstep] != NULL)) {
  145. done = YES;
  146. }
  147. if (!front) {
  148. [footsteps setObject:[NSNumber numberWithInteger:d] forKey:footstep];
  149. }
  150. }
  151. while (!done && x < [text1 length] && y < [text2 length] && [text1 characterAtIndex:x] == [text2 characterAtIndex:y]) {
  152. x++;
  153. y++;
  154. if (doubleEnd) {
  155. footstep = [self diffFootprintX:x y:y];
  156. if (front && ([footsteps objectForKey:footstep] != NULL)) {
  157. done = YES;
  158. }
  159. if (!front) {
  160. [footsteps setObject:[NSNumber numberWithInteger:d] forKey:footstep];
  161. }
  162. }
  163. }
  164. [v1 setObject:[NSNumber numberWithInteger:x] forKey:[NSNumber numberWithInteger:k]];
  165. [[vMap1 objectAtIndex:d] addObject:[self diffFootprintX:x y:y]];
  166. if (x == [text1 length] && y == [text2 length]) {
  167. return [self diffPath1:vMap1 text1:text1 text2:text2];
  168. } else if (done) {
  169. vMap2 = [vMap2 JsubList:0 :[[footsteps objectForKey:footstep] integerValue] + 1];
  170. NSMutableArray *a = [self diffPath1:vMap1 text1:[text1 Jsubstring:0 :x] text2:[text2 Jsubstring:0 :y]];
  171. [a addObjectsFromArray:[self diffPath2:vMap2 text1:[text1 Jsubstring:x] text2:[text2 Jsubstring:y]]];
  172. return a;
  173. }
  174. }
  175. if (doubleEnd) {
  176. [vMap2 addObject:[NSMutableSet set]];
  177. for (NSInteger k = -d; k <= d; k += 2) {
  178. if (k == -d || k != d && [[v2 objectForKey:[NSNumber numberWithInteger:k - 1]] integerValue] < [[v2 objectForKey:[NSNumber numberWithInteger:k + 1]] integerValue]) {
  179. x = [[v2 objectForKey:[NSNumber numberWithInteger:k + 1]] integerValue];
  180. } else {
  181. x = [[v2 objectForKey:[NSNumber numberWithInteger:k - 1]] integerValue] + 1;
  182. }
  183. y = x - k;
  184. footstep = [self diffFootprintX:[text1 length] - x y:[text2 length] - y];
  185. if (!front && ([footsteps objectForKey:footstep] != NULL)) {
  186. done = YES;
  187. }
  188. if (front) {
  189. [footsteps setObject:[NSNumber numberWithInteger:d] forKey:footstep];
  190. }
  191. while (!done && x < [text1 length] && y < [text2 length] && [text1 characterAtIndex:[text1 length] - x - 1] == [text2 characterAtIndex:[text2 length] - y - 1]) {
  192. x++;
  193. y++;
  194. footstep = [self diffFootprintX:[text1 length] - x y:[text2 length] - y];
  195. if (!front && ([footsteps objectForKey:footstep] != NULL)) {
  196. done = YES;
  197. }
  198. if (front) {
  199. [footsteps setObject:[NSNumber numberWithInteger:d] forKey:footstep];
  200. }
  201. }
  202. [v2 setObject:[NSNumber numberWithInteger:x] forKey:[NSNumber numberWithInteger:k]];
  203. [[vMap2 objectAtIndex:d] addObject:[self diffFootprintX:x y:y]];
  204. if (done) {
  205. vMap1 = [vMap1 JsubList:0 :[[footsteps objectForKey:footstep] integerValue] + 1];
  206. NSMutableArray *a = [self diffPath1:vMap1 text1:[text1 Jsubstring:0 :[text1 length] - x] text2:[text2 Jsubstring:0 :[text2 length] - y]];
  207. [a addObjectsFromArray:[self diffPath2:vMap2 text1:[text1 Jsubstring:[text1 length] - x] text2:[text2 Jsubstring:[text2 length] - y]]];
  208. return a;
  209. }
  210. }
  211. }
  212. }
  213. return nil;
  214. }
  215. - (NSMutableArray *)diffPath1:(NSMutableArray *)vMap text1:(NSString *)text1 text2:(NSString *)text2 {
  216. NSMutableArray *path = [NSMutableArray array];
  217. NSInteger x = [text1 length];
  218. NSInteger y = [text2 length];
  219. BDiffOperation lastOp = 0;
  220. for (NSInteger d = [vMap count] - 2; d >= 0; d--) {
  221. while (YES) {
  222. if ([[vMap objectAtIndex:d] containsObject:[self diffFootprintX:x - 1 y:y]]) {
  223. x--;
  224. if (lastOp == BDiffDelete) {
  225. [(id)[[path objectAtIndex:0] text] insertString:[text1 substringWithRange:NSMakeRange(x, 1)] atIndex:0];
  226. } else {
  227. [path insertObject:[BDiff delete:[text1 Jsubstring:x :x + 1]] atIndex:0];
  228. }
  229. lastOp = BDiffDelete;
  230. break;
  231. } else if ([[vMap objectAtIndex:d] containsObject:[self diffFootprintX:x y:y - 1]]) {
  232. y--;
  233. if (lastOp == BDiffInsert) {
  234. [(id)[[path objectAtIndex:0] text] insertString:[text2 substringWithRange:NSMakeRange(y, 1)] atIndex:0];
  235. } else {
  236. [path insertObject:[BDiff insert:[text2 Jsubstring:y :y + 1]] atIndex:0];
  237. }
  238. lastOp = BDiffInsert;
  239. break;
  240. } else {
  241. x--;
  242. y--;
  243. if (lastOp == BDiffEqual) {
  244. [(id)[[path objectAtIndex:0] text] insertString:[text1 substringWithRange:NSMakeRange(x, 1)] atIndex:0];
  245. } else {
  246. [path insertObject:[BDiff equal:[text1 Jsubstring:x :x + 1]] atIndex:0];
  247. }
  248. lastOp = BDiffEqual;
  249. }
  250. }
  251. }
  252. return path;
  253. }
  254. - (NSMutableArray *)diffPath2:(NSMutableArray *)vMap text1:(NSString *)text1 text2:(NSString *)text2 {
  255. NSMutableArray *path = [NSMutableArray array];
  256. NSInteger x = [text1 length];
  257. NSInteger y = [text2 length];
  258. BDiffOperation lastOp = 0;
  259. for (NSInteger d = [vMap count] - 2; d >= 0; d--) {
  260. while (YES) {
  261. if ([[vMap objectAtIndex:d] containsObject:[self diffFootprintX:x - 1 y:y]]) {
  262. x--;
  263. if (lastOp == BDiffDelete) {
  264. [(id)[[path lastObject] text] appendString:[text1 substringWithRange:NSMakeRange([text1 length] - x - 1, 1)]];
  265. } else {
  266. [path addObject:[BDiff delete:[text1 Jsubstring:[text1 length] - x - 1 :[text1 length] - x]]];
  267. }
  268. lastOp = BDiffDelete;
  269. break;
  270. } else if ([[vMap objectAtIndex:d] containsObject:[self diffFootprintX:x y:y - 1]]) {
  271. y--;
  272. if (lastOp == BDiffInsert) {
  273. [(id)[[path lastObject] text] appendString:[text2 substringWithRange:NSMakeRange([text2 length] - y - 1, 1)]];
  274. } else {
  275. [path addObject:[BDiff insert:[text2 Jsubstring:[text2 length] - y - 1 :[text2 length] - y]]];
  276. }
  277. lastOp = BDiffInsert;
  278. break;
  279. } else {
  280. x--;
  281. y--;
  282. if (lastOp == BDiffEqual) {
  283. [(id)[[path lastObject] text] appendString:[text1 substringWithRange:NSMakeRange([text1 length] - x - 1, 1)]];
  284. } else {
  285. [path addObject:[BDiff equal:[text1 Jsubstring:[text1 length] - x - 1 :[text1 length] - x]]];
  286. }
  287. lastOp = BDiffEqual;
  288. }
  289. }
  290. }
  291. return path;
  292. }
  293. - (NSNumber *)diffFootprintX:(NSInteger)x y:(NSInteger)y {
  294. long long result = x;
  295. result = (result << 32);
  296. result += y;
  297. return [NSNumber numberWithLongLong:result];
  298. }
  299. - (NSInteger)diffCommonPrefixText1:(NSString *)text1 text2:(NSString *)text2 {
  300. NSInteger temp1 = [text1 length];
  301. NSInteger temp2 = [text2 length];
  302. NSInteger n = MIN(temp1, temp2);
  303. for (NSInteger i = 0; i < n; i++) {
  304. if ([text1 characterAtIndex:i] != [text2 characterAtIndex:i]) {
  305. return i;
  306. }
  307. }
  308. return n;
  309. }
  310. - (NSInteger)diffCommonSuffixText1:(NSString *)text1 text2:(NSString *)text2 {
  311. NSInteger l1 = [text1 length];
  312. NSInteger l2 = [text2 length];
  313. NSInteger n = MIN(l1, l2);
  314. for (NSInteger i = 0; i < n; i++) {
  315. if ([text1 characterAtIndex:l1 - i - 1] != [text2 characterAtIndex:l2 - i - 1]) {
  316. return i;
  317. }
  318. }
  319. return n;
  320. }
  321. - (NSArray *)diffHalfMatchText1:(NSString *)text1 text2:(NSString *)text2 {
  322. NSInteger l1 = [text1 length];
  323. NSInteger l2 = [text2 length];
  324. NSString *longText = l1 > l2 ? text1 : text2;
  325. NSString *shortText = l1 > l2 ? text2 : text1;
  326. if ([longText length] < 10 || [shortText length] < 1) {
  327. return nil;
  328. }
  329. NSArray *hm1 = [self diffHalfMatch1LongText:longText shortText:shortText i:([longText length] + 3) / 4];
  330. NSArray *hm2 = [self diffHalfMatch1LongText:longText shortText:shortText i:([longText length] + 1) / 2];
  331. NSArray *hm = nil;
  332. if (hm1 == nil && hm2 == nil) {
  333. return nil;
  334. } else if (hm2 == nil) {
  335. hm = hm1;
  336. } else if (hm1 == nil) {
  337. hm = hm2;
  338. } else {
  339. hm = [[hm1 objectAtIndex:4] length] > [[hm2 objectAtIndex:4] length] ? hm1 : hm2;
  340. }
  341. if (l1 > l2) {
  342. return hm;
  343. } else {
  344. return [NSArray arrayWithObjects:[hm objectAtIndex:2], [hm objectAtIndex:3], [hm objectAtIndex:0], [hm objectAtIndex:1], [hm objectAtIndex:4], nil];
  345. }
  346. return nil;
  347. }
  348. - (NSArray *)diffHalfMatch1LongText:(NSString *)longText shortText:(NSString *)shortText i:(NSInteger)i {
  349. NSString *seed = [longText Jsubstring:i :i + (NSInteger) floor([longText length] / 4)];
  350. NSInteger j = -1;
  351. NSString *bestCommon = @"";
  352. NSString *bestLongTextA = @"";
  353. NSString *bestLongTextB = @"";
  354. NSString *bestShortTextA = @"";
  355. NSString *bestShortTextB = @"";
  356. while ((j = [shortText JindexOf:seed :j + 1]) != -1) {
  357. NSInteger prefixLength = [self diffCommonPrefixText1:[longText Jsubstring:i] text2:[shortText Jsubstring:j]];
  358. NSInteger suffixLength = [self diffCommonSuffixText1:[longText Jsubstring:0 :i] text2:[shortText Jsubstring:0 :j]];
  359. if ([bestCommon length] < suffixLength + prefixLength) {
  360. bestCommon = [[shortText Jsubstring:j - suffixLength :j] stringByAppendingString:[shortText Jsubstring:j : j + prefixLength]];
  361. bestLongTextA = [longText Jsubstring:0 :i - suffixLength];
  362. bestLongTextB = [longText Jsubstring:i + prefixLength];
  363. bestShortTextA = [shortText Jsubstring:0 :j - suffixLength];
  364. bestShortTextB = [shortText Jsubstring:j + prefixLength];
  365. }
  366. }
  367. if ([bestCommon length] >= [longText length] / 2) {
  368. return [NSArray arrayWithObjects:bestLongTextA, bestLongTextB, bestShortTextA, bestShortTextB, bestCommon, nil];
  369. } else {
  370. return nil;
  371. }
  372. }
  373. - (void)diffCleanupSemantic:(NSMutableArray *)diffs {
  374. if ([diffs count] == 0) return;
  375. BOOL changes = NO;
  376. NSMutableArray *equalities = [NSMutableArray array];
  377. NSString *lastEquality = nil;
  378. BArrayIterator *pointer = [[[BArrayIterator alloc] initWithArray:diffs] autorelease];
  379. NSInteger lengthChanges1 = 0;
  380. NSInteger lengthChanges2 = 0;
  381. BDiff *thisDiff = [pointer next];
  382. while (thisDiff) {
  383. if (thisDiff.operation == BDiffEqual) {
  384. [equalities addObject:thisDiff];
  385. lengthChanges1 = lengthChanges2;
  386. lengthChanges2 = 0;
  387. lastEquality = [[thisDiff.text copy] autorelease];
  388. } else {
  389. lengthChanges2 += [thisDiff.text length];
  390. if (lastEquality != nil && ([lastEquality length] <= lengthChanges1) && ([lastEquality length] <= lengthChanges2)) {
  391. while (thisDiff != [equalities lastObject]) {
  392. thisDiff = [pointer previous];
  393. }
  394. [pointer next];
  395. [pointer set:[BDiff delete:lastEquality]];
  396. [pointer add:[BDiff insert:lastEquality]];
  397. [equalities removeLastObject];
  398. if ([equalities count] != 0) {
  399. [equalities removeLastObject];
  400. }
  401. if ([equalities count] == 0) {
  402. while ([pointer hasPrevious]) {
  403. [pointer previous];
  404. }
  405. } else {
  406. thisDiff = [equalities lastObject];
  407. while (thisDiff != [pointer previous]) {
  408. // empty
  409. }
  410. }
  411. lengthChanges1 = 0;
  412. lengthChanges2 = 0;
  413. lastEquality = nil;
  414. changes = YES;
  415. }
  416. }
  417. thisDiff = [pointer hasNext] ? [pointer next] : nil;
  418. }
  419. if (changes) {
  420. [self diffCleanupMerge:diffs];
  421. }
  422. [self diffCleanupSemanticLossless:diffs];
  423. }
  424. - (void)diffCleanupSemanticLossless:(NSMutableArray *)diffs {
  425. NSString *equality1 = nil;
  426. NSString *edit = nil;
  427. NSString *equality2 = nil;
  428. NSString *commonString = nil;
  429. NSInteger commonOffset = 0;
  430. NSInteger score = 0;
  431. NSInteger bestScore = 0;
  432. NSString *bestEquality1 = nil;
  433. NSString *bestEdit = nil;
  434. NSString *bestEquality2 = nil;
  435. BArrayIterator *pointer = [[[BArrayIterator alloc] initWithArray:diffs] autorelease];
  436. BDiff *prevDiff = [pointer hasNext] ? [pointer next] : nil;
  437. BDiff *thisDiff = [pointer hasNext] ? [pointer next] : nil;
  438. BDiff *nextDiff = [pointer hasNext] ? [pointer next] : nil;
  439. while (nextDiff) {
  440. if (prevDiff.operation == BDiffEqual && nextDiff.operation == BDiffEqual) {
  441. equality1 = [[prevDiff.text copy] autorelease];
  442. edit = [[thisDiff.text copy] autorelease];
  443. equality2 = [[nextDiff.text copy] autorelease];
  444. commonOffset = [self diffCommonSuffixText1:equality1 text2:edit];
  445. if (commonOffset != 0) {
  446. commonString = [edit Jsubstring:[edit length] - commonOffset];
  447. equality1 = [equality1 Jsubstring:0 :[equality1 length] - commonOffset];
  448. edit = [commonString stringByAppendingString:[edit Jsubstring:0 :[edit length] - commonOffset]];
  449. equality2 = [commonString stringByAppendingString:equality2];
  450. }
  451. bestEquality1 = equality1;
  452. bestEdit = edit;
  453. bestEquality2 = equality2;
  454. bestScore = [self diffCleanupSemanticScoreText1:equality1 text2:edit] + [self diffCleanupSemanticScoreText1:edit text2:equality2];
  455. while ([edit length] != 0 && [equality2 length] != 0 && [edit characterAtIndex:0] == [equality2 characterAtIndex:0]) {
  456. equality1 = [equality1 stringByAppendingFormat:@"%C", [edit characterAtIndex:0]];
  457. edit = [[edit Jsubstring:1] stringByAppendingFormat:@"%C", [equality2 characterAtIndex:0]];
  458. equality2 = [equality2 Jsubstring:1];
  459. score = [self diffCleanupSemanticScoreText1:equality1 text2:edit] + [self diffCleanupSemanticScoreText1:edit text2:equality2];
  460. if (score >= bestScore) {
  461. bestScore = score;
  462. bestEquality1 = equality1;
  463. bestEdit = edit;
  464. bestEquality2 = equality2;
  465. }
  466. }
  467. if (![prevDiff.text isEqualToString:bestEquality1]) {
  468. if ([bestEquality1 length] != 0) {
  469. prevDiff.text = bestEquality1;
  470. } else {
  471. [pointer previous];
  472. [pointer previous];
  473. [pointer previous];
  474. [pointer remove];
  475. [pointer next];
  476. [pointer next];
  477. }
  478. thisDiff.text = bestEdit;
  479. if ([bestEquality2 length] != 0) {
  480. nextDiff.text = bestEquality2;
  481. } else {
  482. [pointer remove];
  483. nextDiff = thisDiff;
  484. thisDiff = prevDiff;
  485. }
  486. }
  487. }
  488. prevDiff = thisDiff;
  489. thisDiff = nextDiff;
  490. nextDiff = [pointer hasNext] ? [pointer next] : nil;
  491. }
  492. }
  493. - (NSInteger)diffCleanupSemanticScoreText1:(NSString *)one text2:(NSString *)two {
  494. if ([one length] == 0 || [two length] == 0) return 5;
  495. NSCharacterSet *letterOrDigit = [NSCharacterSet alphanumericCharacterSet];
  496. NSCharacterSet *whitespace = [NSCharacterSet whitespaceAndNewlineCharacterSet];
  497. NSCharacterSet *control = [NSCharacterSet controlCharacterSet];
  498. NSInteger score = 0;
  499. if (![letterOrDigit characterIsMember:[one characterAtIndex:[one length] - 1]] ||
  500. ![letterOrDigit characterIsMember:[two characterAtIndex:0]]) {
  501. score++;
  502. if ([whitespace characterIsMember:[one characterAtIndex:[one length] - 1]] ||
  503. [whitespace characterIsMember:[two characterAtIndex:0]]) {
  504. score++;
  505. if ([control characterIsMember:[one characterAtIndex:[one length] - 1]] ||
  506. [control characterIsMember:[two characterAtIndex:0]]) {
  507. score++;
  508. if ([one rangeOfRegex:@"\\n\\r?\\n$"].location != NSNotFound || [two rangeOfRegex:@"^\\r?\\n\\r?\\n"].location != NSNotFound) {
  509. score++;
  510. }
  511. }
  512. }
  513. }
  514. return score;
  515. }
  516. - (void)diffCleanupEfficiency:(NSMutableArray *)diffs {
  517. if ([diffs count] == 0) return;
  518. BOOL changes = NO;
  519. NSMutableArray *equalities = [NSMutableArray array];
  520. NSString *lastEquality = nil;
  521. BArrayIterator *pointer = [[[BArrayIterator alloc] initWithArray:diffs] autorelease];
  522. BOOL preIns = NO;
  523. BOOL preDel = NO;
  524. BOOL postIns = NO;
  525. BOOL postDel = NO;
  526. BDiff *thisDiff = [pointer next];
  527. BDiff *safeDiff = thisDiff;
  528. while (thisDiff) {
  529. if (thisDiff.operation == BDiffEqual) {
  530. if ([thisDiff.text length] < Diff_EditCost && (postIns || postDel)) {
  531. [equalities addObject:thisDiff];
  532. preIns = postIns;
  533. preDel = postDel;
  534. lastEquality = [[thisDiff.text copy] autorelease];
  535. } else {
  536. [equalities removeAllObjects];
  537. lastEquality = nil;
  538. safeDiff = thisDiff;
  539. }
  540. postIns = postDel = NO;
  541. } else {
  542. if (thisDiff.operation == BDiffDelete) {
  543. postDel = YES;
  544. } else {
  545. postIns = YES;
  546. }
  547. if (lastEquality != nil
  548. && ((preIns && preDel & postIns && postDel)
  549. || (([lastEquality length] < Diff_EditCost / 2)
  550. && ((preIns ? 1 : 0) + (preDel ? 1 : 0)
  551. + (postIns ? 1 : 0) + (postDel ? 1 : 0)) == 3))) {
  552. while (thisDiff != [equalities lastObject]) {
  553. thisDiff = [pointer previous];
  554. }
  555. [pointer next];
  556. [pointer set:[BDiff delete:lastEquality]];
  557. thisDiff = [BDiff insert:lastEquality];
  558. [pointer add:thisDiff];
  559. [equalities removeLastObject];
  560. lastEquality = nil;
  561. if (preIns && preDel) {
  562. postIns = postDel = YES;
  563. [equalities removeAllObjects];
  564. safeDiff = thisDiff;
  565. } else {
  566. if ([equalities count] != 0) {
  567. [equalities removeLastObject];
  568. }
  569. if ([equalities count] == 0) {
  570. thisDiff = safeDiff;
  571. } else {
  572. thisDiff = [equalities lastObject];
  573. }
  574. while (thisDiff != [pointer previous]) {
  575. // empty
  576. }
  577. postIns = postDel = NO;
  578. }
  579. changes = YES;
  580. }
  581. }
  582. thisDiff = [pointer hasNext] ? [pointer next] : nil;
  583. }
  584. if (changes) {
  585. [self diffCleanupMerge:diffs];
  586. }
  587. }
  588. - (void)diffCleanupMerge:(NSMutableArray *)diffs {
  589. [diffs addObject:[BDiff equal:@""]];
  590. BArrayIterator *pointer = [[[BArrayIterator alloc] initWithArray:diffs] autorelease];
  591. NSInteger countDelete = 0;
  592. NSInteger countInsert = 0;
  593. NSMutableString *textDelete = [NSMutableString string];
  594. NSMutableString *textInsert = [NSMutableString string];
  595. BDiff *thisDiff = [pointer next];
  596. BDiff *prevEqual = nil;
  597. NSInteger commonLength;
  598. while (thisDiff) {
  599. switch (thisDiff.operation) {
  600. case BDiffInsert: {
  601. countInsert++;
  602. [textInsert appendString:thisDiff.text];
  603. prevEqual = nil;
  604. break;
  605. }
  606. case BDiffDelete: {
  607. countDelete++;
  608. [textDelete appendString:thisDiff.text];
  609. prevEqual = nil;
  610. break;
  611. }
  612. case BDiffEqual: {
  613. if (countDelete != 0 || countInsert != 0) {
  614. [pointer previous];
  615. while (countDelete-- > 0) {
  616. [pointer previous];
  617. [pointer remove];
  618. }
  619. while (countInsert-- > 0) {
  620. [pointer previous];
  621. [pointer remove];
  622. }
  623. if (countDelete != 0 && countInsert != 0) {
  624. commonLength = [self diffCommonPrefixText1:textInsert text2:textDelete];
  625. if (commonLength != 0) {
  626. if ([pointer hasPrevious]) {
  627. thisDiff = [pointer previous];
  628. [thisDiff.text appendString:[textInsert Jsubstring:0 :commonLength]];
  629. [pointer next];
  630. } else {
  631. [pointer add:[BDiff equal:[textInsert Jsubstring:0 :commonLength]]];
  632. }
  633. textInsert = [[[textInsert Jsubstring:commonLength] mutableCopy] autorelease];
  634. textDelete = [[[textDelete Jsubstring:commonLength] mutableCopy] autorelease];
  635. }
  636. commonLength = [self diffCommonSuffixText1:textInsert text2:textDelete];
  637. if (commonLength != 0) {
  638. thisDiff = [pointer next];
  639. [thisDiff.text insertString:[textInsert Jsubstring:[textInsert length] - commonLength] atIndex:0];
  640. textInsert = [[[textInsert Jsubstring:0 :[textInsert length] - commonLength] mutableCopy] autorelease];
  641. textDelete = [[[textDelete Jsubstring:0 :[textDelete length] - commonLength] mutableCopy] autorelease];
  642. [pointer previous];
  643. }
  644. }
  645. if ([textDelete length] != 0) {
  646. [pointer add:[BDiff delete:textDelete]];
  647. }
  648. if ([textInsert length] != 0) {
  649. [pointer add:[BDiff insert:textInsert]];
  650. }
  651. thisDiff = [pointer hasNext] ? [pointer next] : nil;
  652. } else if (prevEqual != nil) {
  653. [prevEqual.text appendString:thisDiff.text];
  654. [pointer remove];
  655. thisDiff = [pointer previous];
  656. [pointer next];
  657. }
  658. countInsert = 0;
  659. countDelete = 0;
  660. textDelete = [NSMutableString string];
  661. textInsert = [NSMutableString string];
  662. prevEqual = thisDiff;
  663. break;
  664. }
  665. }
  666. thisDiff = [pointer hasNext] ? [pointer next] : nil;
  667. }
  668. if ([[[diffs lastObject] text] length] == 0) {
  669. [diffs removeLastObject];
  670. }
  671. BOOL changes = NO;
  672. pointer = [[[BArrayIterator alloc] initWithArray:diffs] autorelease];
  673. BDiff *prevDiff = [pointer hasNext] ? [pointer next] : nil;
  674. thisDiff = [pointer hasNext] ? [pointer next] : nil;
  675. BDiff *nextDiff = [pointer hasNext] ? [pointer next] : nil;
  676. while (nextDiff != nil) {
  677. if (prevDiff.operation == BDiffEqual && nextDiff.operation == BDiffEqual) {
  678. if ([thisDiff.text hasSuffix:prevDiff.text]) {
  679. thisDiff.text = [NSString stringWithFormat:@"%@%@", prevDiff.text, [thisDiff.text Jsubstring:0 :[thisDiff.text length] - [prevDiff.text length]]];
  680. [nextDiff.text insertString:prevDiff.text atIndex:0];
  681. [pointer previous];
  682. [pointer previous];
  683. [pointer previous];
  684. [pointer remove];
  685. [pointer next];
  686. thisDiff = [pointer next];
  687. nextDiff = [pointer hasNext] ? [pointer next] : nil;
  688. changes = YES;
  689. } else if ([thisDiff.text hasPrefix:nextDiff.text]) {
  690. [prevDiff.text appendString:nextDiff.text];
  691. thisDiff.text = [NSString stringWithFormat:@"%@%@", [thisDiff.text Jsubstring:[nextDiff.text length]], nextDiff.text];
  692. [pointer remove];
  693. nextDiff = [pointer hasNext] ? [pointer next] : nil;
  694. changes = YES;
  695. }
  696. }
  697. prevDiff = thisDiff;
  698. thisDiff = nextDiff;
  699. nextDiff = [pointer hasNext] ? [pointer next] : nil;
  700. }
  701. if (changes) {
  702. [self diffCleanupMerge:diffs];
  703. }
  704. }
  705. - (NSInteger)diffXIndex:(NSArray *)diffs location:(NSInteger)location {
  706. NSInteger chars1 = 0;
  707. NSInteger chars2 = 0;
  708. NSInteger lastChars1 = 0;
  709. NSInteger lastChars2 = 0;
  710. BDiff *lastDiff = nil;
  711. for (BDiff *aDiff in diffs) {
  712. if (aDiff.operation != BDiffInsert) {
  713. chars1 += [aDiff.text length];
  714. }
  715. if (aDiff.operation != BDiffDelete) {
  716. chars2 += [aDiff.text length];
  717. }
  718. if (chars1 > location) {
  719. lastDiff = aDiff;
  720. break;
  721. }
  722. lastChars1 = chars1;
  723. lastChars2 = chars2;
  724. }
  725. if (lastDiff != nil && lastDiff.operation == BDiffDelete) {
  726. return lastChars2;
  727. }
  728. return lastChars2 + (location - lastChars1);
  729. }
  730. - (NSString *)diffPrettyHTML:(NSArray *)diffs {
  731. NSMutableString *html = [NSMutableString string];
  732. NSInteger i = 0;
  733. for (BDiff *aDiff in diffs) {
  734. NSMutableString *text = [[aDiff.text mutableCopy] autorelease];
  735. [text replaceOccurrencesOfString:@"&" withString:@"&amp;" options:0 range:NSMakeRange(0, [text length])];
  736. [text replaceOccurrencesOfString:@"<" withString:@"&lt;" options:0 range:NSMakeRange(0, [text length])];
  737. [text replaceOccurrencesOfString:@">" withString:@"&gt;" options:0 range:NSMakeRange(0, [text length])];
  738. [text replaceOccurrencesOfString:@"\n" withString:@"&para;<BR>" options:0 range:NSMakeRange(0, [text length])];
  739. switch (aDiff.operation) {
  740. case BDiffInsert:
  741. [html appendFormat:@"<ins STYLE=\"background:#E6FFE6;\" TITLE=\"i=%i\">%@</ins>", i, text];
  742. break;
  743. case BDiffDelete:
  744. [html appendFormat:@"<del STYLE=\"background:#FFE6E6;\" TITLE=\"i=%i\">%@</del>", i, text];
  745. break;
  746. case BDiffEqual:
  747. [html appendFormat:@"<span TITLE=\"i=%i\">%@</span>", i, text];
  748. break;
  749. }
  750. if (aDiff.operation != BDiffDelete) {
  751. i += [aDiff.text length];
  752. }
  753. }
  754. return html;
  755. }
  756. - (NSString *)diffText1:(NSArray *)diffs {
  757. NSMutableString *text = [NSMutableString string];
  758. for (BDiff *aDiff in diffs) {
  759. if (aDiff.operation != BDiffInsert) {
  760. [text appendString:aDiff.text];
  761. }
  762. }
  763. return text;
  764. }
  765. - (NSString *)diffText2:(NSArray *)diffs {
  766. NSMutableString *text = [NSMutableString string];
  767. for (BDiff *aDiff in diffs) {
  768. if (aDiff.operation != BDiffDelete) {
  769. [text appendString:aDiff.text];
  770. }
  771. }
  772. return text;
  773. }
  774. - (NSString *)diffToDelta:(NSArray *)diffs {
  775. NSMutableString *text = [NSMutableString string];
  776. for (BDiff *aDiff in diffs) {
  777. switch (aDiff.operation) {
  778. case BDiffInsert:
  779. [text appendString:@"+"];
  780. [text appendString:[[aDiff.text URLEncode] stringByReplacingOccurrencesOfString:@"+" withString:@" "]];
  781. [text appendString:@"\t"];
  782. break;
  783. case BDiffDelete:
  784. [text appendFormat:@"-%i\t", [aDiff.text length]];
  785. break;
  786. case BDiffEqual:
  787. [text appendFormat:@"=%i\t", [aDiff.text length]];
  788. break;
  789. }
  790. }
  791. NSMutableString *delta = text;
  792. if ([delta length] != 0) {
  793. [delta replaceCharactersInRange:NSMakeRange([delta length] - 1, 1) withString:@""];
  794. [delta unescapeForEncodeUriCompatability];
  795. }
  796. return delta;
  797. }
  798. - (NSMutableArray *)diffFromDeltaText1:(NSString *)text1 delta:(NSString *)delta {
  799. NSNumberFormatter *numberFormatter = [[[NSNumberFormatter alloc] init] autorelease];
  800. NSMutableArray *diffs = [NSMutableArray array];
  801. int pointer = 0;
  802. for (NSString *token in [delta componentsSeparatedByString:@"\t"]) {
  803. if ([token length] == 0) {
  804. continue;
  805. }
  806. NSString *param = [token Jsubstring:1];
  807. switch ([token characterAtIndex:0]) {
  808. case '+':
  809. param = [param stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"];
  810. param = [param URLDecode];
  811. [diffs addObject:[BDiff insert:param]];
  812. break;
  813. case '-':
  814. // Fall through
  815. case '=': {
  816. NSInteger n = [[numberFormatter numberFromString:param] integerValue];
  817. NSString *text = [text1 Jsubstring:pointer :pointer + n];
  818. pointer += n;
  819. if ([token characterAtIndex:0] == '=') {
  820. [diffs addObject:[BDiff equal:text]];
  821. } else {
  822. [diffs addObject:[BDiff delete:text]];
  823. }
  824. break;
  825. }
  826. default:
  827. [NSException raise:@"BAD!" format:@""];
  828. }
  829. }
  830. return diffs;
  831. }
  832. - (NSInteger)matchMain:(NSString *)text pattern:(NSString *)pattern loc:(NSInteger)loc {
  833. NSInteger temp = [text length] - [pattern length];
  834. loc = MAX(0, MIN(loc, temp));
  835. if ([text isEqualToString:pattern]) {
  836. return 0;
  837. } else if ([text length] == 0) {
  838. return -1;
  839. } else if ([[text Jsubstring:loc :loc + [pattern length]] isEqualToString:pattern]) {
  840. return loc;
  841. } else {
  842. return [self matchBitap:text pattern:pattern loc:loc];
  843. }
  844. }
  845. - (NSInteger)matchBitap:(NSString *)text pattern:(NSString *)pattern loc:(NSInteger)loc {
  846. NSDictionary *s = [self matchAlphabet:pattern];
  847. NSInteger scoreTextLength = [text length];
  848. scoreTextLength = MAX(scoreTextLength, Match_MinLength);
  849. scoreTextLength = MIN(scoreTextLength, Match_MaxLength);
  850. double scoreThreshold = Match_Threshold;
  851. NSInteger bestLoc = [text JindexOf:pattern :loc];
  852. if (bestLoc != -1) {
  853. double score = [self matchBitapScore:0 x:bestLoc loc:loc scoreTextLength:scoreTextLength pattern:pattern];
  854. scoreThreshold = MIN(score, scoreThreshold);
  855. }
  856. bestLoc = [text JlastIndexOf:pattern :loc + [pattern length]];
  857. if (bestLoc != -1) {
  858. double score = [self matchBitapScore:0 x:bestLoc loc:loc scoreTextLength:scoreTextLength pattern:pattern];
  859. scoreThreshold = MIN(score, scoreThreshold);
  860. }
  861. NSInteger matchmask = 1 << ([pattern length] - 1);
  862. bestLoc = -1;
  863. NSInteger binMin, binMid;
  864. NSInteger temp = [text length];
  865. NSInteger binMax = MAX(loc + loc, temp);
  866. NSInteger *lastRd = NULL;
  867. for (NSInteger d = 0; d < [pattern length]; d++) {
  868. NSInteger *rd = malloc([text length] * sizeof(NSInteger));
  869. binMin = loc;
  870. binMid = binMax;
  871. while (binMin < binMid) {
  872. if ([self matchBitapScore:d x:binMid loc:loc scoreTextLength:scoreTextLength pattern:pattern] < scoreThreshold) {
  873. binMin = binMid;
  874. } else {
  875. binMax = binMid;
  876. }
  877. binMid = (binMax - binMin) / 2 + binMin;
  878. }
  879. binMax = binMid;
  880. NSInteger start = MAX(0, loc - (binMid - loc) - 1);
  881. NSInteger temp1 = [text length];
  882. NSInteger temp2 = [pattern length];
  883. NSInteger finish = MIN(temp1 - 1, temp2 + binMid);
  884. if ([text characterAtIndex:finish] == [pattern characterAtIndex:[pattern length] - 1]) {
  885. rd[finish] = (1 << (d + 1)) - 1;
  886. } else {
  887. rd[finish] = (1 << d) - 1;
  888. }
  889. for (NSInteger j = finish - 1; j >= start; j--) {
  890. NSNumber *value = [s objectForKey:[NSNumber numberWithInteger:[text characterAtIndex:j]]];
  891. if (d == 0) {
  892. rd[j] = ((rd[j + 1] << 1) | 1) & (value != nil ? [value integerValue] : 0);
  893. } else {
  894. rd[j] = ((rd[j + 1] << 1) | 1) & (value != nil ? [value integerValue] : 0) | ((lastRd[j + 1] << 1) | 1) | ((lastRd[j] << 1) | 1) | lastRd[j + 1];
  895. }
  896. if ((rd[j] & matchmask) != 0) {
  897. double score = [self matchBitapScore:d x:j loc:loc scoreTextLength:scoreTextLength pattern:pattern];
  898. if (score <= scoreThreshold) {
  899. scoreThreshold = score;
  900. bestLoc = j;
  901. if (j > loc) {
  902. start = MAX(0, loc - (j - loc));
  903. } else {
  904. break;
  905. }
  906. }
  907. }
  908. }
  909. if ([self matchBitapScore:d + 1 x:loc loc:loc scoreTextLength:scoreTextLength pattern:pattern] > scoreThreshold) {
  910. break;
  911. }
  912. if (lastRd) {
  913. free(lastRd);
  914. }
  915. lastRd = rd;
  916. }
  917. if (lastRd) {
  918. free(lastRd);
  919. }
  920. return bestLoc;
  921. }
  922. - (double)matchBitapScore:(NSInteger)e x:(NSInteger)x loc:(NSInteger)loc scoreTextLength:(NSInteger)scoreTextLength pattern:(NSString *)pattern {
  923. return (e / (float) [pattern length] / Match_Balance) + (ABS(loc - x) / (float) scoreTextLength / (1.0 - Match_Balance));
  924. }
  925. - (NSDictionary *)matchAlphabet:(NSString *)pattern {
  926. NSMutableDictionary *s = [NSMutableDictionary dictionary];
  927. NSUInteger length = [pattern length];
  928. for (NSUInteger i = 0; i < length; i++) {
  929. [s setObject:[NSNumber numberWithInteger:0] forKey:[NSNumber numberWithInteger:[pattern characterAtIndex:i]]];
  930. }
  931. for (NSUInteger i = 0; i < length; i++) {
  932. unichar c = [pattern characterAtIndex:i];
  933. NSNumber *key = [NSNumber numberWithInteger:c];
  934. [s setObject:[NSNumber numberWithInteger:[[s objectForKey:key] integerValue] | (1 << ([pattern length] - i - 1))] forKey:key];
  935. }
  936. return s;
  937. }
  938. - (void)patchAddContext:(BPatch *)patch text:(NSString *)text {
  939. NSString *pattern = [text Jsubstring:patch.start2 :patch.start2 + patch.length1];
  940. NSInteger padding = 0;
  941. while ([text JindexOf:pattern] != [text JlastIndexOf:pattern] && [pattern length] < Match_MaxBits - Patch_Margin - Patch_Margin) {
  942. padding += Patch_Margin;
  943. NSInteger temp = [text length];
  944. pattern = [text Jsubstring:MAX(0, patch.start2 - padding) :MIN(temp, patch.start2 + patch.length1 + padding)];
  945. }
  946. padding += Patch_Margin;
  947. NSString *prefix = [text Jsubstring:MAX(0, patch.start2 - padding) :patch.start2];
  948. if ([prefix length] != 0) {
  949. [patch.diffs insertObject:[BDiff equal:prefix] atIndex:0];
  950. }
  951. NSInteger temp = [text length];
  952. NSString *suffix = [text Jsubstring:patch.start2 + patch.length1 :MIN(temp, patch.start2 + patch.length1 + padding)];
  953. if ([suffix length] != 0) {
  954. [patch.diffs addObject:[BDiff equal:suffix]];
  955. }
  956. patch.start1 -= [prefix length];
  957. patch.start2 -= [prefix length];
  958. patch.length1 += [prefix length] + [suffix length];
  959. patch.length2 += [prefix length] + [suffix length];
  960. }
  961. - (NSMutableArray *)patchMakeText1:(NSString *)text1 text2:(NSString *)text2 {
  962. NSMutableArray *diffs = [self diffMainText1:text1 text2:text2];
  963. if ([diffs count] > 2) {
  964. [self diffCleanupSemantic:diffs];
  965. [self diffCleanupEfficiency:diffs];
  966. }
  967. return [self patchMakeText1:text1 diffs:diffs];
  968. }
  969. - (NSMutableArray *)patchMakeDiffs:(NSArray *)diffs {
  970. NSString *text1 = [self diffText1:diffs];
  971. return [self patchMakeText1:text1 diffs:diffs];
  972. }
  973. - (NSMutableArray *)patchMakeText1:(NSString *)text1 diffs:(NSArray *)diffs {
  974. NSMutableArray *patches = [NSMutableArray array];
  975. if ([diffs count] == 0) {
  976. return patches;
  977. }
  978. BPatch *patch = [[[BPatch alloc] init] autorelease];
  979. NSInteger charCount1 = 0;
  980. NSInteger charCount2 = 0;
  981. NSString *prepatchText = text1;
  982. NSString *postPatchText = text1;
  983. for (BDiff *aDiff in diffs) {
  984. if ([patch.diffs count] == 0 && aDiff.operation != BDiffEqual) {
  985. patch.start1 = charCount1;
  986. patch.start2 = charCount2;
  987. }
  988. switch (aDiff.operation) {
  989. case BDiffInsert:
  990. [patch.diffs addObject:aDiff];
  991. patch.length2 += [aDiff.text length];
  992. postPatchText = [NSString stringWithFormat:@"%@%@%@", [postPatchText Jsubstring:0 :charCount2], aDiff.text, [postPatchText Jsubstring:charCount2]];
  993. break;
  994. case BDiffDelete:
  995. patch.length1 += [aDiff.text length];
  996. [patch.diffs addObject:aDiff];
  997. postPatchText = [NSString stringWithFormat:@"%@%@", [postPatchText Jsubstring:0 :charCount2], [postPatchText Jsubstring:charCount2 + [aDiff.text length]]];
  998. break;
  999. case BDiffEqual:
  1000. if ([aDiff.text length] <= 2 * Patch_Margin && [patch.diffs count] != 0 && aDiff != [diffs lastObject]) {
  1001. [patch.diffs addObject:aDiff];
  1002. patch.length1 += [aDiff.text length];
  1003. patch.length2 += [aDiff.text length];
  1004. }
  1005. if ([aDiff.text length] >= 2 * Patch_Margin) {
  1006. if ([patch.diffs count] != 0) {
  1007. [self patchAddContext:patch text:prepatchText];
  1008. [patches addObject:patch];
  1009. patch = [[[BPatch alloc] init] autorelease];
  1010. prepatchText = postPatchText;
  1011. }
  1012. }
  1013. break;
  1014. }
  1015. if (aDiff.operation != BDiffInsert) {
  1016. charCount1 += [aDiff.text length];
  1017. }
  1018. if (aDiff.operation != BDiffDelete) {
  1019. charCount2 += [aDiff.text length];
  1020. }
  1021. }
  1022. if ([patch.diffs count] != 0) {
  1023. [self patchAddContext:patch text:prepatchText];
  1024. [patches addObject:patch];
  1025. }
  1026. return patches;
  1027. }
  1028. - (NSMutableArray *)patchDeepCopy:(NSMutableArray *)patches {
  1029. NSMutableArray *patchesCopy = [NSMutableArray arrayWithCapacity:[patches count]];
  1030. for (BPatch *aPatch in patches) {
  1031. BPatch *patchCopy = [[[BPatch alloc] init] autorelease];
  1032. for (BDiff *aDiff in aPatch.diffs) {
  1033. BDiff *diffCopy = [BDiff diffWithOperationType:aDiff.operation text:aDiff.text];
  1034. [patchCopy.diffs addObject:diffCopy];
  1035. }
  1036. patchCopy.start1 = aPatch.start1;
  1037. patchCopy.start2 = aPatch.start2;
  1038. patchCopy.length1 = aPatch.length1;
  1039. patchCopy.length2 = aPatch.length2;
  1040. [patchesCopy addObject:patchCopy];
  1041. }
  1042. return patchesCopy;
  1043. }
  1044. - (NSArray *)patchApply:(NSMutableArray *)patches text:(NSString *)text {
  1045. if ([patches count] == 0) {
  1046. return [NSArray arrayWithObjects:text, [NSNumber numberWithBool:NO], nil];
  1047. }
  1048. patches = [self patchDeepCopy:patches];
  1049. NSMutableString *textResult = [[text mutableCopy] autorelease];
  1050. NSString *nullPadding = [self patchAddPadding:patches];
  1051. [textResult insertString:nullPadding atIndex:0];
  1052. [textResult appendString:nullPadding];
  1053. [self patchSplitMax:patches];
  1054. NSInteger x = 0;
  1055. NSInteger delta = 0;
  1056. NSMutableArray *results = [NSMutableArray arrayWithCapacity:[patches count]];
  1057. NSInteger expectedLoc, startLoc;
  1058. NSString *text1;
  1059. NSString *text2;
  1060. NSInteger index1, index2;
  1061. for (BPatch *aPatch in patches) {
  1062. expectedLoc = aPatch.start2 + delta;
  1063. text1 = [self diffText1:aPatch.diffs];
  1064. startLoc = [self matchMain:textResult pattern:text1 loc:expectedLoc];
  1065. if (startLoc == -1) {
  1066. [results addObject:[NSNumber numberWithBool:NO]];
  1067. } else {
  1068. [results addObject:[NSNumber numberWithBool:YES]];
  1069. delta = startLoc - expectedLoc;
  1070. text2 = [textResult Jsubstring:startLoc : MIN(startLoc + [text1 length], [textResult length])];
  1071. if ([text1 isEqualToString:text2]) {
  1072. [textResult replaceCharactersInRange:NSMakeRange(startLoc, [text1 length]) withString:[self diffText2:aPatch.diffs]];
  1073. } else {
  1074. NSMutableArray *diffs = [self diffMapText1:text1 text2:text2];
  1075. [self diffCleanupSemanticLossless:diffs];
  1076. index1 = 0;
  1077. for (BDiff *aDiff in aPatch.diffs) {
  1078. if (aDiff.operation != BDiffEqual) {
  1079. index2 = [self diffXIndex:diffs location:index1];
  1080. if (aDiff.operation == BDiffInsert) {
  1081. [textResult insertString:aDiff.text atIndex:startLoc + index2];
  1082. } else if (aDiff.operation == BDiffDelete) {
  1083. NSInteger start = startLoc + index2;
  1084. NSInteger diff = [self diffXIndex:diffs location:index1 + [aDiff.text length]];
  1085. NSRange range = NSMakeRange(start, (startLoc + diff) - start);
  1086. [textResult replaceCharactersInRange:range withString:@""];
  1087. }
  1088. }
  1089. if (aDiff.operation != BDiffDelete) {
  1090. index1 += [aDiff.text length];
  1091. }
  1092. }
  1093. }
  1094. }
  1095. x++;
  1096. }
  1097. [textResult replaceCharactersInRange:NSMakeRange(0, [nullPadding length]) withString:@""];
  1098. [textResult replaceCharactersInRange:NSMakeRange([textResult length] - [nullPadding length], [nullPadding length]) withString:@""];
  1099. return [NSArray arrayWithObjects:textResult, results, nil];
  1100. }
  1101. - (NSString *)patchAddPadding:(NSArray *)patches {
  1102. NSMutableArray *diffs;
  1103. NSMutableString *nullPadding = [NSMutableString string];
  1104. for (NSInteger x = 0; x < Patch_Margin; x++) {
  1105. [nullPadding appendFormat:@"%C", (char) x];
  1106. }
  1107. for (BPatch *aPatch in patches) {
  1108. aPatch.start1 += [nullPadding length];
  1109. aPatch.start2 += [nullPadding length];
  1110. }
  1111. BPatch *patch = [patches objectAtIndex:0];
  1112. diffs = patch.diffs;
  1113. if ([diffs count] == 0 || [[diffs objectAtIndex:0] operation] != BDiffEqual) {
  1114. [diffs insertObject:[BDiff equal:nullPadding] atIndex:0];
  1115. patch.start1 -= [nullPadding length];
  1116. patch.start2 -= [nullPadding length];
  1117. patch.length1 += [nullPadding length];
  1118. patch.length2 += [nullPadding length];
  1119. } else if ([nullPadding length] > [[[diffs objectAtIndex:0] text] length]) {
  1120. BDiff *firstDiff = [diffs objectAtIndex:0];
  1121. NSInteger extraLength = [nullPadding length] - [firstDiff.text length];
  1122. [firstDiff.text insertString:[nullPadding Jsubstring:[firstDiff.text length]] atIndex:0];
  1123. patch.start1 -= extraLength;
  1124. patch.start2 -= extraLength;
  1125. patch.length1 += extraLength;
  1126. patch.length2 += extraLength;
  1127. }
  1128. patch = [patches lastObject];
  1129. diffs = patch.diffs;
  1130. if ([diffs count] == 0 || [[diffs lastObject] operation] != BDiffEqual) {
  1131. [diffs addObject:[BDiff equal:nullPadding]];
  1132. patch.length1 += [nullPadding length];
  1133. patch.length2 += [nullPadding length];
  1134. } else if ([nullPadding length] > [[[diffs lastObject] text] length]) {
  1135. BDiff *lastDiff = [diffs lastObject];
  1136. NSInteger extraLength = [nullPadding length] - [[[diffs lastObject] text] length];
  1137. [lastDiff.text appendString:[nullPadding Jsubstring:0 :extraLength]];
  1138. patch.length1 += extraLength;
  1139. patch.length2 += extraLength;
  1140. }
  1141. return nullPadding;
  1142. }
  1143. - (void)patchSplitMax:(NSMutableArray *)patches {
  1144. NSInteger patchSize;
  1145. NSString *precontext;
  1146. NSString *postcontext;
  1147. BPatch *patch;
  1148. NSInteger start1, start2;
  1149. BOOL empty;
  1150. BDiffOperation diffType;
  1151. NSString *diffText;
  1152. BArrayIterator *pointer = [[[BArrayIterator alloc] initWithArray:patches] autorelease];
  1153. BPatch *bigpatch = [pointer hasNext] ? [pointer next] : nil;
  1154. while (bigpatch != nil) {
  1155. if (bigpatch.length1 <= Match_MaxBits) {
  1156. bigpatch = [pointer hasNext] ? [pointer next] : nil;
  1157. continue;
  1158. }
  1159. [pointer remove];
  1160. patchSize = Match_MaxBits;
  1161. start1 = bigpatch.start1;
  1162. start2 = bigpatch.start2;
  1163. precontext = @"";
  1164. while ([bigpatch.diffs count] != 0) {
  1165. patch = [[[BPatch alloc] init] autorelease];
  1166. empty = YES;
  1167. patch.start1 = start1 - [precontext length];
  1168. patch.start2 = start2 - [precontext length];
  1169. if ([precontext length] != 0) {
  1170. patch.length1 = [precontext length];
  1171. patch.length2 = [precontext length];
  1172. [patch.diffs addObject:[BDiff equal:precontext]];
  1173. }
  1174. while ([bigpatch.diffs count] != 0 && patch.length1 < patchSize - Patch_Margin) {
  1175. diffType = [[bigpatch.diffs objectAtIndex:0] operation];
  1176. diffText = [[bigpatch.diffs objectAtIndex:0] text];
  1177. if (diffType == BDiffInsert) {
  1178. patch.length2 += [diffText length];
  1179. start2 += [diffText length];
  1180. [patch.diffs addObject:[bigpatch.diffs objectAtIndex:0]];
  1181. [bigpatch.diffs removeObjectAtIndex:0];
  1182. empty = NO;
  1183. } else {
  1184. NSInteger temp = [diffText length];
  1185. diffText = [diffText Jsubstring:0 :MIN(temp, patchSize - patch.length1 - Patch_Margin)];
  1186. patch.length1 += [diffText length];
  1187. start1 += [diffText length];
  1188. if (diffType == BDiffEqual) {
  1189. patch.length2 += [diffText length];
  1190. start2 += [diffText length];
  1191. } else {
  1192. empty = NO;
  1193. }
  1194. [patch.diffs addObject:[BDiff diffWithOperationType:diffType text:diffText]];
  1195. if ([diffText isEqualToString:[[bigpatch.diffs objectAtIndex:0] text]]) {
  1196. [bigpatch.diffs removeObjectAtIndex:0];
  1197. } else {
  1198. [(id)[[bigpatch.diffs objectAtIndex:0] text] replaceCharactersInRange:NSMakeRange(0, [diffText length]) withString:@""];
  1199. }
  1200. }
  1201. }
  1202. precontext = [self diffText2:patch.diffs];
  1203. NSInteger temp = [precontext length];
  1204. precontext = [precontext Jsubstring:MAX(0, temp - Patch_Margin)];
  1205. NSString *diffText1 = [self diffText1:bigpatch.diffs];
  1206. if ([diffText1 length] > Patch_Margin) {
  1207. postcontext = [diffText1 Jsubstring:0 :Patch_Margin];
  1208. } else {
  1209. postcontext = diffText1;
  1210. }
  1211. if ([postcontext length] != 0) {
  1212. patch.length1 += [postcontext length];
  1213. patch.length2 += [postcontext length];
  1214. if ([patch.diffs count] != 0 && [[patch.diffs lastObject] operation] == BDiffEqual) {
  1215. [(id)[[patch.diffs lastObject] text] appendString:postcontext];
  1216. } else {
  1217. [patch.diffs addObject:[BDiff equal:postcontext]];
  1218. }
  1219. }
  1220. if (!empty) {
  1221. [pointer add:patch];
  1222. }
  1223. }
  1224. bigpatch = [pointer hasNext] ? [pointer next] : nil;
  1225. }
  1226. }
  1227. - (NSString *)patchToText:(NSArray *)patches {
  1228. NSMutableString *text = [NSMutableString string];
  1229. for (BPatch *aPatch in patches) {
  1230. [text appendString:[aPatch description]];
  1231. }
  1232. return text;
  1233. }
  1234. - (NSMutableArray *)patchFromText:(NSString *)textline {
  1235. NSNumberFormatter *numberFormatter = [[[NSNumberFormatter alloc] init] autorelease];
  1236. NSMutableArray *patches = [NSMutableArray array];
  1237. if ([textline length] == 0) {
  1238. return patches;
  1239. }
  1240. NSMutableArray *text = [[[textline componentsSeparatedByString:@"\n"] mutableCopy] autorelease];
  1241. BPatch *patch;
  1242. NSString *patchHeader = @"^@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@$";
  1243. unichar sign;
  1244. NSString *line;
  1245. while ([text count] != 0) {
  1246. NSString *firstText = [text objectAtIndex:0];
  1247. NSRange capture1Range = [firstText rangeOfRegex:patchHeader capture:1];
  1248. NSRange capture2Range = [firstText rangeOfRegex:patchHeader capture:2];
  1249. NSRange capture3Range = [firstText rangeOfRegex:patchHeader capture:3];
  1250. NSRange capture4Range = [firstText rangeOfRegex:patchHeader capture:4];
  1251. NSString *capture1String = [firstText substringWithRange:capture1Range];
  1252. NSString *capture2String = [firstText substringWithRange:capture2Range];
  1253. NSString *capture3String = [firstText substringWithRange:capture3Range];
  1254. NSString *capture4String = [firstText substringWithRange:capture4Range];
  1255. patch = [[[BPatch alloc] init] autorelease];
  1256. [patches addObject:patch];
  1257. patch.start1 = [[numberFormatter numberFromString:capture1String] integerValue];
  1258. if ([capture2String length] == 0) {
  1259. patch.start1--;
  1260. patch.length1 = 1;
  1261. } else if ([capture2String isEqualToString:@"0"]) {
  1262. patch.length1 = 0;
  1263. } else {
  1264. patch.start1--;
  1265. patch.length1 = [[numberFormatter numberFromString:capture2String] integerValue];
  1266. }
  1267. patch.start2 = [[numberFormatter numberFromString:capture3String] integerValue];
  1268. if ([capture4String length] == 0) {
  1269. patch.start2--;
  1270. patch.length2 = 1;
  1271. } else if ([capture4String isEqualToString:@"0"]) {
  1272. patch.length2 = 0;
  1273. } else {
  1274. patch.start2--;
  1275. patch.length2 = [[numberFormatter numberFromString:capture4String] integerValue];
  1276. }
  1277. [text removeObjectAtIndex:0];
  1278. while ([text count] != 0) {
  1279. firstText = [text objectAtIndex:0];
  1280. if ([firstText length] > 0) { // not blank line
  1281. sign = [firstText characterAtIndex:0];
  1282. line = [[text objectAtIndex:0] Jsubstring:1];
  1283. line = [line stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"];
  1284. line = [line URLDecode];
  1285. if (sign == '-') {
  1286. [patch.diffs addObject:[BDiff delete:line]];
  1287. } else if (sign == '+') {
  1288. [patch.diffs addObject:[BDiff insert:line]];
  1289. } else if (sign == ' ') {
  1290. [patch.diffs addObject:[BDiff equal:line]];
  1291. } else if (sign == '@') {
  1292. break;
  1293. } else {
  1294. [NSException raise:@"Invalid patch mode" format:nil];
  1295. }
  1296. }
  1297. [text removeObjectAtIndex:0];
  1298. }
  1299. }
  1300. return patches;
  1301. }
  1302. @end
  1303. @implementation BDiff
  1304. + (id)equal:(NSString *)text {
  1305. return [self diffWithOperationType:BDiffEqual text:text];
  1306. }
  1307. + (id)insert:(NSString *)text {
  1308. return [self diffWithOperationType:BDiffInsert text:text];
  1309. }
  1310. + (id)delete:(NSString *)text {
  1311. return [self diffWithOperationType:BDiffDelete text:text];
  1312. }
  1313. + (id)diffWithOperationType:(BDiffOperation)operation text:(NSString *)text {
  1314. return [[[BDiff alloc] initWithText:text operation:operation] autorelease];
  1315. }
  1316. - (id)initWithText:(NSString *)aText operation:(BDiffOperation)anOperation {
  1317. if (self = [super init]) {
  1318. text = [aText mutableCopy];
  1319. operation = anOperation;
  1320. }
  1321. return self;
  1322. }
  1323. - (void)dealloc {
  1324. [text release];
  1325. [super dealloc];
  1326. }
  1327. - (BOOL)isEqual:(id)anObject {
  1328. if ([anObject isKindOfClass:[self class]]) {
  1329. BDiff *other = (id) anObject;
  1330. return other.operation == self.operation && [other.text isEqual:self.text];
  1331. }
  1332. return NO;
  1333. }
  1334. - (NSUInteger)hash {
  1335. return [self.text hash] + self.operation;
  1336. }
  1337. @synthesize operation;
  1338. @synthesize text;
  1339. - (void)setText:(id)aString {
  1340. [text autorelease];
  1341. text = [aString mutableCopy];
  1342. }
  1343. - (NSString *)description {
  1344. NSString *prettyText = [self.text stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
  1345. prettyText = [prettyText stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"];
  1346. NSString

Large files files are truncated, but you can click here to view the full file