comparison src/org/tmatesoft/hg/internal/RevlogStream.java @ 397:5e95b0da26f2 smartgit3

Issue 24: IAE, Underflow in FilterDataAccess. Issue 26:UnsupportedOperationException when patching empty base revision. Tests
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 23 Feb 2012 15:31:57 +0100
parents 86f049e6bcae
children c76c57f6b961
comparison
equal deleted inserted replaced
393:728708de3597 397:5e95b0da26f2
1 /* 1 /*
2 * Copyright (c) 2010-2011 TMate Software Ltd 2 * Copyright (c) 2010-2012 TMate Software Ltd
3 * 3 *
4 * This program is free software; you can redistribute it and/or modify 4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by 5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License. 6 * the Free Software Foundation; version 2 of the License.
7 * 7 *
462 streamOffset = (int) offset; 462 streamOffset = (int) offset;
463 streamDataAccess = daData; 463 streamDataAccess = daData;
464 daData.seek(streamOffset); 464 daData.seek(streamOffset);
465 } 465 }
466 final boolean patchToPrevious = baseRevision != i; // the only way I found to tell if it's a patch 466 final boolean patchToPrevious = baseRevision != i; // the only way I found to tell if it's a patch
467 if (streamDataAccess.isEmpty()) { 467 if (streamDataAccess.isEmpty() || compressedLen == 0) {
468 userDataAccess = new DataAccess(); // empty 468 userDataAccess = new DataAccess(); // empty
469 } else { 469 } else {
470 final byte firstByte = streamDataAccess.readByte(); 470 final byte firstByte = streamDataAccess.readByte();
471 if (firstByte == 0x78 /* 'x' */) { 471 if (firstByte == 0x78 /* 'x' */) {
472 inflater.reset(); 472 inflater.reset();
473 userDataAccess = new InflaterDataAccess(streamDataAccess, streamOffset, compressedLen, patchToPrevious ? -1 : actualLen, inflater, inflaterBuffer); 473 userDataAccess = new InflaterDataAccess(streamDataAccess, streamOffset, compressedLen, patchToPrevious ? -1 : actualLen, inflater, inflaterBuffer);
474 } else if (firstByte == 0x75 /* 'u' */) { 474 } else if (firstByte == 0x75 /* 'u' */) {
475 userDataAccess = new FilterDataAccess(streamDataAccess, streamOffset+1, compressedLen-1); 475 userDataAccess = new FilterDataAccess(streamDataAccess, streamOffset+1, compressedLen-1);
476 } else { 476 } else {
477 // XXX Python impl in fact throws exception when there's not 'x', 'u' or '0' 477 // 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
478 // but I don't see reason not to return data as is 478 //
479 // although firstByte is already read from the streamDataAccess, FilterDataAccess#readByte would seek to
480 // initial offset before first attempt to read a byte
479 userDataAccess = new FilterDataAccess(streamDataAccess, streamOffset, compressedLen); 481 userDataAccess = new FilterDataAccess(streamDataAccess, streamOffset, compressedLen);
480 } 482 }
481 } 483 }
482 // XXX 484 // userDataAccess is revision content, either complete revision, patch of a previous content, or an empty patch
483 if (patchToPrevious && !userDataAccess.isEmpty() /* Issue 22, empty patch to an empty base revision*/) { 485 if (patchToPrevious) {
484 // this is a patch 486 // this is a patch
485 patch.read(userDataAccess); 487 if (userDataAccess.isEmpty()) {
486 userDataAccess.done(); 488 // Issue 22, empty patch to an empty base revision
487 // 489 // Issue 24, empty patch to non-empty base revision
488 // it shall be reset at the end of prev iteration, when it got assigned from userDataAccess 490 // empty patch modifies nothing, use content of a previous revision (shall present - it's a patch here)
489 // however, actual userDataAccess and lastUserData may share Inflater object, which needs to be reset 491 //
490 // Alternatively, userDataAccess.done() above may be responsible to reset Inflater (if it's InflaterDataAccess) 492 assert lastUserData.length() == actualLen; // with no patch, data size shall be the same
491 lastUserData.reset(); 493 userDataAccess = lastUserData;
492 // final long startMeasuring = System.currentTimeMillis(); // TIMING 494 } else {
493 byte[] userData = patch.apply(lastUserData, actualLen); 495 patch.read(userDataAccess);
494 // applyTime += (System.currentTimeMillis() - startMeasuring); // TIMING 496 userDataAccess.done();
495 patch.clear(); // do not keep any reference, allow byte[] data to be gc'd 497 //
496 userDataAccess = new ByteArrayDataAccess(userData); 498 // it shall be reset at the end of prev iteration, when it got assigned from userDataAccess
499 // however, actual userDataAccess and lastUserData may share Inflater object, which needs to be reset
500 // Alternatively, userDataAccess.done() above may be responsible to reset Inflater (if it's InflaterDataAccess)
501 lastUserData.reset();
502 // final long startMeasuring = System.currentTimeMillis(); // TIMING
503 byte[] userData = patch.apply(lastUserData, actualLen);
504 // applyTime += (System.currentTimeMillis() - startMeasuring); // TIMING
505 patch.clear(); // do not keep any reference, allow byte[] data to be gc'd
506 userDataAccess = new ByteArrayDataAccess(userData);
507 }
497 } 508 }
498 } else { 509 } else {
499 if (inline) { 510 if (inline) {
500 daIndex.skip(compressedLen); 511 daIndex.skip(compressedLen);
501 } 512 }
511 } 522 }
512 } 523 }
513 if (userDataAccess != null) { 524 if (userDataAccess != null) {
514 userDataAccess.reset(); // not sure this is necessary here, as lastUserData would get reset anyway before next use. 525 userDataAccess.reset(); // not sure this is necessary here, as lastUserData would get reset anyway before next use.
515 } 526 }
516 if (lastUserData != null) { 527 if (lastUserData != null && lastUserData != userDataAccess /* empty patch case, reuse of recent data in actual revision */) {
528 // release lastUserData only if we didn't reuse it in actual revision due to empty patch:
529 // empty patch means we have previous revision and didn't alter it with a patch, hence use lastUserData for userDataAccess above
517 lastUserData.done(); 530 lastUserData.done();
518 } 531 }
519 lastUserData = userDataAccess; 532 lastUserData = userDataAccess;
520 } 533 }
521 lastRevisionRead = end; 534 lastRevisionRead = end;