@ -63,7 +63,6 @@ import java.util.zip.Inflater;
import org.eclipse.jgit.JGitText ;
import org.eclipse.jgit.JGitText ;
import org.eclipse.jgit.errors.CorruptObjectException ;
import org.eclipse.jgit.errors.CorruptObjectException ;
import org.eclipse.jgit.errors.LargeObjectException ;
import org.eclipse.jgit.errors.MissingObjectException ;
import org.eclipse.jgit.errors.MissingObjectException ;
import org.eclipse.jgit.errors.PackInvalidException ;
import org.eclipse.jgit.errors.PackInvalidException ;
import org.eclipse.jgit.errors.PackMismatchException ;
import org.eclipse.jgit.errors.PackMismatchException ;
@ -275,11 +274,25 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
return getReverseIdx ( ) . findObject ( offset ) ;
return getReverseIdx ( ) . findObject ( offset ) ;
}
}
private final void decompress ( final long position , final WindowCursor curs ,
private final byte [ ] decompress ( final long position , final int sz ,
final byte [ ] dstbuf , final int dstoff , final int dstsz )
final WindowCursor curs ) throws IOException , DataFormatException {
throws IOException , DataFormatException {
byte [ ] dstbuf ;
if ( curs . inflate ( this , position , dstbuf , dstoff ) ! = dstsz )
try {
dstbuf = new byte [ sz ] ;
} catch ( OutOfMemoryError noMemory ) {
// The size may be larger than our heap allows, return null to
// let the caller know allocation isn't possible and it should
// use the large object streaming approach instead.
//
// For example, this can occur when sz is 640 MB, and JRE
// maximum heap size is only 256 MB. Even if the JRE has
// 200 MB free, it cannot allocate a 640 MB byte array.
return null ;
}
if ( curs . inflate ( this , position , dstbuf , 0 ) ! = sz )
throw new EOFException ( MessageFormat . format ( JGitText . get ( ) . shortCompressedStreamAt , position ) ) ;
throw new EOFException ( MessageFormat . format ( JGitText . get ( ) . shortCompressedStreamAt , position ) ) ;
return dstbuf ;
}
}
final void copyAsIs ( PackOutputStream out , LocalObjectToPack src ,
final void copyAsIs ( PackOutputStream out , LocalObjectToPack src ,
@ -608,12 +621,19 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
, getPackFile ( ) ) ) ;
, getPackFile ( ) ) ) ;
}
}
ObjectLoader load ( final WindowCursor curs , final long pos )
ObjectLoader load ( final WindowCursor curs , long pos )
throws IOException {
throws IOException {
try {
final byte [ ] ib = curs . tempId ;
final byte [ ] ib = curs . tempId ;
Delta delta = null ;
byte [ ] data = null ;
int type = Constants . OBJ_BAD ;
boolean cached = false ;
SEARCH : for ( ; ; ) {
readFully ( pos , ib , 0 , 20 , curs ) ;
readFully ( pos , ib , 0 , 20 , curs ) ;
int c = ib [ 0 ] & 0xff ;
int c = ib [ 0 ] & 0xff ;
final int type = ( c > > 4 ) & 7 ;
final int typeCod e = ( c > > 4 ) & 7 ;
long sz = c & 15 ;
long sz = c & 15 ;
int shift = 4 ;
int shift = 4 ;
int p = 1 ;
int p = 1 ;
@ -623,47 +643,116 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
shift + = 7 ;
shift + = 7 ;
}
}
try {
switch ( typeCode ) {
switch ( type ) {
case Constants . OBJ_COMMIT :
case Constants . OBJ_COMMIT :
case Constants . OBJ_TREE :
case Constants . OBJ_TREE :
case Constants . OBJ_BLOB :
case Constants . OBJ_BLOB :
case Constants . OBJ_TAG : {
case Constants . OBJ_TAG : {
if ( sz < curs . getStreamFileThreshold ( ) ) {
if ( sz < curs . getStreamFileThreshold ( ) )
byte [ ] data ;
data = decompress ( pos + p , ( int ) sz , curs ) ;
try {
data = new byte [ ( int ) sz ] ;
if ( delta ! = null ) {
} catch ( OutOfMemoryError tooBig ) {
type = typeCode ;
return largeWhole ( curs , pos , type , sz , p ) ;
break SEARCH ;
}
decompress ( pos + p , curs , data , 0 , data . length ) ;
return new ObjectLoader . SmallObject ( type , data ) ;
}
}
return largeWhole ( curs , pos , type , sz , p ) ;
if ( data ! = null )
return new ObjectLoader . SmallObject ( typeCode , data ) ;
else
return new LargePackedWholeObject ( typeCode , sz , pos , p ,
this , curs . db ) ;
}
}
case Constants . OBJ_OFS_DELTA : {
case Constants . OBJ_OFS_DELTA : {
c = ib [ p + + ] & 0xff ;
c = ib [ p + + ] & 0xff ;
long ofs = c & 127 ;
long base = c & 127 ;
while ( ( c & 128 ) ! = 0 ) {
while ( ( c & 128 ) ! = 0 ) {
ofs + = 1 ;
base + = 1 ;
c = ib [ p + + ] & 0xff ;
c = ib [ p + + ] & 0xff ;
ofs < < = 7 ;
base < < = 7 ;
ofs + = ( c & 127 ) ;
base + = ( c & 127 ) ;
}
base = pos - base ;
delta = new Delta ( delta , pos , ( int ) sz , p , base ) ;
if ( sz ! = delta . deltaSize )
break SEARCH ;
DeltaBaseCache . Entry e = DeltaBaseCache . get ( this , base ) ;
if ( e ! = null ) {
type = e . type ;
data = e . data ;
cached = true ;
break SEARCH ;
}
}
return loadDelta ( pos , p , sz , pos - ofs , curs ) ;
pos = base ;
continue SEARCH ;
}
}
case Constants . OBJ_REF_DELTA : {
case Constants . OBJ_REF_DELTA : {
readFully ( pos + p , ib , 0 , 20 , curs ) ;
readFully ( pos + p , ib , 0 , 20 , curs ) ;
long ofs = findDeltaBase ( ObjectId . fromRaw ( ib ) ) ;
long base = findDeltaBase ( ObjectId . fromRaw ( ib ) ) ;
return loadDelta ( pos , p + 20 , sz , ofs , curs ) ;
delta = new Delta ( delta , pos , ( int ) sz , p + 20 , base ) ;
if ( sz ! = delta . deltaSize )
break SEARCH ;
DeltaBaseCache . Entry e = DeltaBaseCache . get ( this , base ) ;
if ( e ! = null ) {
type = e . type ;
data = e . data ;
cached = true ;
break SEARCH ;
}
pos = base ;
continue SEARCH ;
}
}
default :
default :
throw new IOException ( MessageFormat . format (
throw new IOException ( MessageFormat . format (
JGitText . get ( ) . unknownObjectType , type ) ) ;
JGitText . get ( ) . unknownObjectType , typeCod e ) ) ;
}
}
}
// At this point there is at least one delta to apply to data.
// (Whole objects with no deltas to apply return early above.)
if ( data = = null )
return delta . large ( this , curs ) ;
do {
// Cache only the base immediately before desired object.
if ( cached )
cached = false ;
else if ( delta . next = = null )
DeltaBaseCache . store ( this , delta . basePos , data , type ) ;
pos = delta . deltaPos ;
final byte [ ] cmds = decompress ( pos + delta . hdrLen ,
delta . deltaSize , curs ) ;
if ( cmds = = null ) {
data = null ; // Discard base in case of OutOfMemoryError
return delta . large ( this , curs ) ;
}
final long sz = BinaryDelta . getResultSize ( cmds ) ;
if ( Integer . MAX_VALUE < = sz )
return delta . large ( this , curs ) ;
final byte [ ] result ;
try {
result = new byte [ ( int ) sz ] ;
} catch ( OutOfMemoryError tooBig ) {
data = null ; // Discard base in case of OutOfMemoryError
return delta . large ( this , curs ) ;
}
BinaryDelta . apply ( data , cmds , result ) ;
data = result ;
delta = delta . next ;
} while ( delta ! = null ) ;
return new ObjectLoader . SmallObject ( type , data ) ;
} catch ( DataFormatException dfe ) {
} catch ( DataFormatException dfe ) {
CorruptObjectException coe = new CorruptObjectException (
CorruptObjectException coe = new CorruptObjectException (
MessageFormat . format (
MessageFormat . format (
@ -683,61 +772,41 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
return ofs ;
return ofs ;
}
}
private ObjectLoader loadDelta ( long posSelf , int hdrLen , long sz ,
private static class Delta {
long posBase , WindowCursor curs ) throws IOException ,
/** Child that applies onto this object. */
DataFormatException {
final Delta next ;
if ( Integer . MAX_VALUE < = sz )
return largeDelta ( posSelf , hdrLen , posBase , curs ) ;
byte [ ] base ;
/** Offset of the delta object. */
int type ;
final long deltaPos ;
DeltaBaseCache . Entry e = DeltaBaseCache . get ( this , posBase ) ;
/** Size of the inflated delta stream. */
if ( e ! = null ) {
final int deltaSize ;
base = e . data ;
type = e . type ;
} else {
ObjectLoader p = load ( curs , posBase ) ;
try {
base = p . getCachedBytes ( curs . getStreamFileThreshold ( ) ) ;
} catch ( LargeObjectException tooBig ) {
return largeDelta ( posSelf , hdrLen , posBase , curs ) ;
}
type = p . getType ( ) ;
DeltaBaseCache . store ( this , posBase , base , type ) ;
}
final byte [ ] delta ;
/** Total size of the delta's pack entry header (including base). */
try {
final int hdrLen ;
delta = new byte [ ( int ) sz ] ;
} catch ( OutOfMemoryError tooBig ) {
return largeDelta ( posSelf , hdrLen , posBase , curs ) ;
}
decompress ( posSelf + hdrLen , curs , delta , 0 , delta . length ) ;
/** Offset of the base object this delta applies onto. */
sz = BinaryDelta . getResultSize ( delta ) ;
final long basePos ;
if ( Integer . MAX_VALUE < = sz )
return largeDelta ( posSelf , hdrLen , posBase , curs ) ;
final byte [ ] result ;
Delta ( Delta next , long ofs , int sz , int hdrLen , long baseOffset ) {
try {
this . next = next ;
result = new byte [ ( int ) sz ] ;
this . deltaPos = ofs ;
} catch ( OutOfMemoryError tooBig ) {
this . deltaSize = sz ;
return largeDelta ( posSelf , hdrLen , posBase , curs ) ;
this . hdrLen = hdrLen ;
this . basePos = baseOffset ;
}
}
BinaryDelta . apply ( base , delta , result ) ;
ObjectLoader large ( PackFile pack , WindowCursor wc ) {
return new ObjectLoader . SmallObject ( type , result ) ;
Delta d = this ;
while ( d . next ! = null )
d = d . next ;
return d . newLargeLoader ( pack , wc ) ;
}
}
private LargePackedWholeObject largeWhole ( final WindowCursor curs ,
private ObjectLoader newLargeLoader ( PackFile pack , WindowCursor wc ) {
final long pos , final int type , long sz , int p ) {
return new LargePackedDeltaObject ( deltaPos , basePos , hdrLen ,
return new LargePackedWholeObject ( type , sz , pos , p , this , curs . db ) ;
pack , wc . db ) ;
}
}
private LargePackedDeltaObject largeDelta ( long posObj , int hdrLen ,
long posBase , WindowCursor wc ) {
return new LargePackedDeltaObject ( posObj , posBase , hdrLen , this , wc . db ) ;
}
}
byte [ ] getDeltaHeader ( WindowCursor wc , long pos )
byte [ ] getDeltaHeader ( WindowCursor wc , long pos )