comparison src/org/tmatesoft/hg/internal/RevlogStream.java @ 398:c76c57f6b961

Merge fixed for issue 24 and issue 26 from smartgit3 branch
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 23 Feb 2012 21:53:21 +0100
parents 0ae53c32ecef 5e95b0da26f2
children 6c22bdc0bdfd
comparison
equal deleted inserted replaced
396:0ae53c32ecef 398:c76c57f6b961
486 streamOffset = (int) offset; 486 streamOffset = (int) offset;
487 streamDataAccess = daData; 487 streamDataAccess = daData;
488 daData.seek(streamOffset); 488 daData.seek(streamOffset);
489 } 489 }
490 final boolean patchToPrevious = baseRevision != i; // the only way I found to tell if it's a patch 490 final boolean patchToPrevious = baseRevision != i; // the only way I found to tell if it's a patch
491 if (streamDataAccess.isEmpty()) { 491 if (streamDataAccess.isEmpty() || compressedLen == 0) {
492 userDataAccess = new DataAccess(); // empty 492 userDataAccess = new DataAccess(); // empty
493 } else { 493 } else {
494 final byte firstByte = streamDataAccess.readByte(); 494 final byte firstByte = streamDataAccess.readByte();
495 if (firstByte == 0x78 /* 'x' */) { 495 if (firstByte == 0x78 /* 'x' */) {
496 inflater.reset(); 496 inflater.reset();
497 userDataAccess = new InflaterDataAccess(streamDataAccess, streamOffset, compressedLen, patchToPrevious ? -1 : actualLen, inflater, inflaterBuffer); 497 userDataAccess = new InflaterDataAccess(streamDataAccess, streamOffset, compressedLen, patchToPrevious ? -1 : actualLen, inflater, inflaterBuffer);
498 } else if (firstByte == 0x75 /* 'u' */) { 498 } else if (firstByte == 0x75 /* 'u' */) {
499 userDataAccess = new FilterDataAccess(streamDataAccess, streamOffset+1, compressedLen-1); 499 userDataAccess = new FilterDataAccess(streamDataAccess, streamOffset+1, compressedLen-1);
500 } else { 500 } else {
501 // XXX Python impl in fact throws exception when there's not 'x', 'u' or '0' 501 // XXX Python impl in fact throws exception when there's not 'x', 'u' or '0' but I don't see reason not to return data as is
502 // but I don't see reason not to return data as is 502 //
503 // although firstByte is already read from the streamDataAccess, FilterDataAccess#readByte would seek to
504 // initial offset before first attempt to read a byte
503 userDataAccess = new FilterDataAccess(streamDataAccess, streamOffset, compressedLen); 505 userDataAccess = new FilterDataAccess(streamDataAccess, streamOffset, compressedLen);
504 } 506 }
505 } 507 }
506 // XXX 508 // userDataAccess is revision content, either complete revision, patch of a previous content, or an empty patch
507 if (patchToPrevious && !userDataAccess.isEmpty() /* Issue 22, empty patch to an empty base revision*/) { 509 if (patchToPrevious) {
508 // this is a patch 510 // this is a patch
509 patch.read(userDataAccess); 511 if (userDataAccess.isEmpty()) {
510 userDataAccess.done(); 512 // Issue 22, empty patch to an empty base revision
511 // 513 // Issue 24, empty patch to non-empty base revision
512 // it shall be reset at the end of prev iteration, when it got assigned from userDataAccess 514 // empty patch modifies nothing, use content of a previous revision (shall present - it's a patch here)
513 // however, actual userDataAccess and lastUserData may share Inflater object, which needs to be reset 515 //
514 // Alternatively, userDataAccess.done() above may be responsible to reset Inflater (if it's InflaterDataAccess) 516 assert lastUserData.length() == actualLen; // with no patch, data size shall be the same
515 lastUserData.reset(); 517 userDataAccess = lastUserData;
516 // final long startMeasuring = System.currentTimeMillis(); // TIMING 518 } else {
517 byte[] userData = patch.apply(lastUserData, actualLen); 519 patch.read(userDataAccess);
518 // applyTime += (System.currentTimeMillis() - startMeasuring); // TIMING 520 userDataAccess.done();
519 patch.clear(); // do not keep any reference, allow byte[] data to be gc'd 521 //
520 userDataAccess = new ByteArrayDataAccess(userData); 522 // it shall be reset at the end of prev iteration, when it got assigned from userDataAccess
523 // however, actual userDataAccess and lastUserData may share Inflater object, which needs to be reset
524 // Alternatively, userDataAccess.done() above may be responsible to reset Inflater (if it's InflaterDataAccess)
525 lastUserData.reset();
526 // final long startMeasuring = System.currentTimeMillis(); // TIMING
527 byte[] userData = patch.apply(lastUserData, actualLen);
528 // applyTime += (System.currentTimeMillis() - startMeasuring); // TIMING
529 patch.clear(); // do not keep any reference, allow byte[] data to be gc'd
530 userDataAccess = new ByteArrayDataAccess(userData);
531 }
521 } 532 }
522 } else { 533 } else {
523 if (inline) { 534 if (inline) {
524 daIndex.skip(compressedLen); 535 daIndex.skip(compressedLen);
525 } 536 }
535 } 546 }
536 } 547 }
537 if (userDataAccess != null) { 548 if (userDataAccess != null) {
538 userDataAccess.reset(); // not sure this is necessary here, as lastUserData would get reset anyway before next use. 549 userDataAccess.reset(); // not sure this is necessary here, as lastUserData would get reset anyway before next use.
539 } 550 }
540 if (lastUserData != null) { 551 if (lastUserData != null && lastUserData != userDataAccess /* empty patch case, reuse of recent data in actual revision */) {
552 // release lastUserData only if we didn't reuse it in actual revision due to empty patch:
553 // empty patch means we have previous revision and didn't alter it with a patch, hence use lastUserData for userDataAccess above
541 lastUserData.done(); 554 lastUserData.done();
542 } 555 }
543 lastUserData = userDataAccess; 556 lastUserData = userDataAccess;
544 } 557 }
545 lastRevisionRead = end; 558 lastRevisionRead = end;