DfsPackFile.java

  1. /*
  2.  * Copyright (C) 2008-2011, Google Inc.
  3.  * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
  4.  * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org> and others
  5.  *
  6.  * This program and the accompanying materials are made available under the
  7.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  8.  * https://www.eclipse.org/org/documents/edl-v10.php.
  9.  *
  10.  * SPDX-License-Identifier: BSD-3-Clause
  11.  */

  12. package org.eclipse.jgit.internal.storage.dfs;

  13. import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
  14. import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
  15. import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
  16. import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;

  17. import java.io.BufferedInputStream;
  18. import java.io.EOFException;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.nio.ByteBuffer;
  22. import java.nio.channels.Channels;
  23. import java.text.MessageFormat;
  24. import java.util.Set;
  25. import java.util.zip.CRC32;
  26. import java.util.zip.DataFormatException;
  27. import java.util.zip.Inflater;

  28. import org.eclipse.jgit.errors.CorruptObjectException;
  29. import org.eclipse.jgit.errors.LargeObjectException;
  30. import org.eclipse.jgit.errors.MissingObjectException;
  31. import org.eclipse.jgit.errors.PackInvalidException;
  32. import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
  33. import org.eclipse.jgit.internal.JGitText;
  34. import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
  35. import org.eclipse.jgit.internal.storage.file.PackIndex;
  36. import org.eclipse.jgit.internal.storage.file.PackReverseIndex;
  37. import org.eclipse.jgit.internal.storage.pack.BinaryDelta;
  38. import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
  39. import org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation;
  40. import org.eclipse.jgit.lib.AbbreviatedObjectId;
  41. import org.eclipse.jgit.lib.AnyObjectId;
  42. import org.eclipse.jgit.lib.Constants;
  43. import org.eclipse.jgit.lib.ObjectId;
  44. import org.eclipse.jgit.lib.ObjectLoader;
  45. import org.eclipse.jgit.lib.Repository;
  46. import org.eclipse.jgit.util.LongList;

  47. /**
  48.  * A Git version 2 pack file representation. A pack file contains Git objects in
  49.  * delta packed format yielding high compression of lots of object where some
  50.  * objects are similar.
  51.  */
  52. public final class DfsPackFile extends BlockBasedFile {
  53.     private static final int REC_SIZE = Constants.OBJECT_ID_LENGTH + 8;
  54.     private static final long REF_POSITION = 0;

  55.     /**
  56.      * Lock for initialization of {@link #index} and {@link #corruptObjects}.
  57.      * <p>
  58.      * This lock ensures only one thread can perform the initialization work.
  59.      */
  60.     private final Object initLock = new Object();

  61.     /** Index mapping {@link ObjectId} to position within the pack stream. */
  62.     private volatile PackIndex index;

  63.     /** Reverse version of {@link #index} mapping position to {@link ObjectId}. */
  64.     private volatile PackReverseIndex reverseIndex;

  65.     /** Index of compressed bitmap mapping entire object graph. */
  66.     private volatile PackBitmapIndex bitmapIndex;

  67.     /**
  68.      * Objects we have tried to read, and discovered to be corrupt.
  69.      * <p>
  70.      * The list is allocated after the first corruption is found, and filled in
  71.      * as more entries are discovered. Typically this list is never used, as
  72.      * pack files do not usually contain corrupt objects.
  73.      */
  74.     private volatile LongList corruptObjects;

  75.     /**
  76.      * Construct a reader for an existing, packfile.
  77.      *
  78.      * @param cache
  79.      *            cache that owns the pack data.
  80.      * @param desc
  81.      *            description of the pack within the DFS.
  82.      */
  83.     DfsPackFile(DfsBlockCache cache, DfsPackDescription desc) {
  84.         super(cache, desc, PACK);

  85.         int bs = desc.getBlockSize(PACK);
  86.         if (bs > 0) {
  87.             setBlockSize(bs);
  88.         }

  89.         long sz = desc.getFileSize(PACK);
  90.         length = sz > 0 ? sz : -1;
  91.     }

  92.     /**
  93.      * Get description that was originally used to configure this pack file.
  94.      *
  95.      * @return description that was originally used to configure this pack file.
  96.      */
  97.     public DfsPackDescription getPackDescription() {
  98.         return desc;
  99.     }

  100.     /**
  101.      * Whether the pack index file is loaded and cached in memory.
  102.      *
  103.      * @return whether the pack index file is loaded and cached in memory.
  104.      */
  105.     public boolean isIndexLoaded() {
  106.         return index != null;
  107.     }

  108.     void setPackIndex(PackIndex idx) {
  109.         long objCnt = idx.getObjectCount();
  110.         int recSize = Constants.OBJECT_ID_LENGTH + 8;
  111.         long sz = objCnt * recSize;
  112.         cache.putRef(desc.getStreamKey(INDEX), sz, idx);
  113.         index = idx;
  114.     }

  115.     /**
  116.      * Get the PackIndex for this PackFile.
  117.      *
  118.      * @param ctx
  119.      *            reader context to support reading from the backing store if
  120.      *            the index is not already loaded in memory.
  121.      * @return the PackIndex.
  122.      * @throws java.io.IOException
  123.      *             the pack index is not available, or is corrupt.
  124.      */
  125.     public PackIndex getPackIndex(DfsReader ctx) throws IOException {
  126.         return idx(ctx);
  127.     }

  128.     private PackIndex idx(DfsReader ctx) throws IOException {
  129.         if (index != null) {
  130.             return index;
  131.         }

  132.         if (invalid) {
  133.             throw new PackInvalidException(getFileName(), invalidatingCause);
  134.         }

  135.         Repository.getGlobalListenerList()
  136.                 .dispatch(new BeforeDfsPackIndexLoadedEvent(this));

  137.         synchronized (initLock) {
  138.             if (index != null) {
  139.                 return index;
  140.             }

  141.             try {
  142.                 DfsStreamKey idxKey = desc.getStreamKey(INDEX);
  143.                 DfsBlockCache.Ref<PackIndex> idxref = cache.getOrLoadRef(
  144.                         idxKey,
  145.                         REF_POSITION,
  146.                         () -> loadPackIndex(ctx, idxKey));
  147.                 PackIndex idx = idxref.get();
  148.                 if (index == null && idx != null) {
  149.                     index = idx;
  150.                 }
  151.                 return index;
  152.             } catch (IOException e) {
  153.                 invalid = true;
  154.                 invalidatingCause = e;
  155.                 throw e;
  156.             }
  157.         }
  158.     }

  159.     final boolean isGarbage() {
  160.         return desc.getPackSource() == UNREACHABLE_GARBAGE;
  161.     }

  162.     PackBitmapIndex getBitmapIndex(DfsReader ctx) throws IOException {
  163.         if (invalid || isGarbage() || !desc.hasFileExt(BITMAP_INDEX)) {
  164.             return null;
  165.         }

  166.         if (bitmapIndex != null) {
  167.             return bitmapIndex;
  168.         }

  169.         synchronized (initLock) {
  170.             if (bitmapIndex != null) {
  171.                 return bitmapIndex;
  172.             }

  173.             PackIndex idx = idx(ctx);
  174.             PackReverseIndex revidx = getReverseIdx(ctx);
  175.             DfsStreamKey bitmapKey = desc.getStreamKey(BITMAP_INDEX);
  176.             DfsBlockCache.Ref<PackBitmapIndex> idxref = cache.getOrLoadRef(
  177.                     bitmapKey,
  178.                     REF_POSITION,
  179.                     () -> loadBitmapIndex(ctx, bitmapKey, idx, revidx));
  180.             PackBitmapIndex bmidx = idxref.get();
  181.             if (bitmapIndex == null && bmidx != null) {
  182.                 bitmapIndex = bmidx;
  183.             }
  184.             return bitmapIndex;
  185.         }
  186.     }

  187.     PackReverseIndex getReverseIdx(DfsReader ctx) throws IOException {
  188.         if (reverseIndex != null) {
  189.             return reverseIndex;
  190.         }

  191.         synchronized (initLock) {
  192.             if (reverseIndex != null) {
  193.                 return reverseIndex;
  194.             }

  195.             PackIndex idx = idx(ctx);
  196.             DfsStreamKey revKey = new DfsStreamKey.ForReverseIndex(
  197.                     desc.getStreamKey(INDEX));
  198.             DfsBlockCache.Ref<PackReverseIndex> revref = cache.getOrLoadRef(
  199.                     revKey,
  200.                     REF_POSITION,
  201.                     () -> loadReverseIdx(revKey, idx));
  202.             PackReverseIndex revidx = revref.get();
  203.             if (reverseIndex == null && revidx != null) {
  204.                 reverseIndex = revidx;
  205.             }
  206.             return reverseIndex;
  207.         }
  208.     }

  209.     /**
  210.      * Check if an object is stored within this pack.
  211.      *
  212.      * @param ctx
  213.      *            reader context to support reading from the backing store if
  214.      *            the index is not already loaded in memory.
  215.      * @param id
  216.      *            object to be located.
  217.      * @return true if the object exists in this pack; false if it does not.
  218.      * @throws java.io.IOException
  219.      *             the pack index is not available, or is corrupt.
  220.      */
  221.     public boolean hasObject(DfsReader ctx, AnyObjectId id) throws IOException {
  222.         final long offset = idx(ctx).findOffset(id);
  223.         return 0 < offset && !isCorrupt(offset);
  224.     }

  225.     /**
  226.      * Get an object from this pack.
  227.      *
  228.      * @param ctx
  229.      *            temporary working space associated with the calling thread.
  230.      * @param id
  231.      *            the object to obtain from the pack. Must not be null.
  232.      * @return the object loader for the requested object if it is contained in
  233.      *         this pack; null if the object was not found.
  234.      * @throws IOException
  235.      *             the pack file or the index could not be read.
  236.      */
  237.     ObjectLoader get(DfsReader ctx, AnyObjectId id)
  238.             throws IOException {
  239.         long offset = idx(ctx).findOffset(id);
  240.         return 0 < offset && !isCorrupt(offset) ? load(ctx, offset) : null;
  241.     }

  242.     long findOffset(DfsReader ctx, AnyObjectId id) throws IOException {
  243.         return idx(ctx).findOffset(id);
  244.     }

  245.     void resolve(DfsReader ctx, Set<ObjectId> matches, AbbreviatedObjectId id,
  246.             int matchLimit) throws IOException {
  247.         idx(ctx).resolve(matches, id, matchLimit);
  248.     }

  249.     /**
  250.      * Obtain the total number of objects available in this pack. This method
  251.      * relies on pack index, giving number of effectively available objects.
  252.      *
  253.      * @param ctx
  254.      *            current reader for the calling thread.
  255.      * @return number of objects in index of this pack, likewise in this pack
  256.      * @throws IOException
  257.      *             the index file cannot be loaded into memory.
  258.      */
  259.     long getObjectCount(DfsReader ctx) throws IOException {
  260.         return idx(ctx).getObjectCount();
  261.     }

  262.     private byte[] decompress(long position, int sz, DfsReader ctx)
  263.             throws IOException, DataFormatException {
  264.         byte[] dstbuf;
  265.         try {
  266.             dstbuf = new byte[sz];
  267.         } catch (OutOfMemoryError noMemory) {
  268.             // The size may be larger than our heap allows, return null to
  269.             // let the caller know allocation isn't possible and it should
  270.             // use the large object streaming approach instead.
  271.             //
  272.             // For example, this can occur when sz is 640 MB, and JRE
  273.             // maximum heap size is only 256 MB. Even if the JRE has
  274.             // 200 MB free, it cannot allocate a 640 MB byte array.
  275.             return null;
  276.         }

  277.         if (ctx.inflate(this, position, dstbuf, false) != sz) {
  278.             throw new EOFException(MessageFormat.format(
  279.                     JGitText.get().shortCompressedStreamAt,
  280.                     Long.valueOf(position)));
  281.         }
  282.         return dstbuf;
  283.     }

  284.     void copyPackAsIs(PackOutputStream out, DfsReader ctx) throws IOException {
  285.         // If the length hasn't been determined yet, pin to set it.
  286.         if (length == -1) {
  287.             ctx.pin(this, 0);
  288.             ctx.unpin();
  289.         }
  290.         try (ReadableChannel rc = ctx.db.openFile(desc, PACK)) {
  291.             int sz = ctx.getOptions().getStreamPackBufferSize();
  292.             if (sz > 0) {
  293.                 rc.setReadAheadBytes(sz);
  294.             }
  295.             if (cache.shouldCopyThroughCache(length)) {
  296.                 copyPackThroughCache(out, ctx, rc);
  297.             } else {
  298.                 copyPackBypassCache(out, rc);
  299.             }
  300.         }
  301.     }

  302.     private void copyPackThroughCache(PackOutputStream out, DfsReader ctx,
  303.             ReadableChannel rc) throws IOException {
  304.         long position = 12;
  305.         long remaining = length - (12 + 20);
  306.         while (0 < remaining) {
  307.             DfsBlock b = cache.getOrLoad(this, position, ctx, () -> rc);
  308.             int ptr = (int) (position - b.start);
  309.             if (b.size() <= ptr) {
  310.                 throw packfileIsTruncated();
  311.             }
  312.             int n = (int) Math.min(b.size() - ptr, remaining);
  313.             b.write(out, position, n);
  314.             position += n;
  315.             remaining -= n;
  316.         }
  317.     }

  318.     private long copyPackBypassCache(PackOutputStream out, ReadableChannel rc)
  319.             throws IOException {
  320.         ByteBuffer buf = newCopyBuffer(out, rc);
  321.         long position = 12;
  322.         long remaining = length - (12 + 20);
  323.         boolean packHeadSkipped = false;
  324.         while (0 < remaining) {
  325.             DfsBlock b = cache.get(key, alignToBlock(position));
  326.             if (b != null) {
  327.                 int ptr = (int) (position - b.start);
  328.                 if (b.size() <= ptr) {
  329.                     throw packfileIsTruncated();
  330.                 }
  331.                 int n = (int) Math.min(b.size() - ptr, remaining);
  332.                 b.write(out, position, n);
  333.                 position += n;
  334.                 remaining -= n;
  335.                 rc.position(position);
  336.                 packHeadSkipped = true;
  337.                 continue;
  338.             }

  339.             // Need to skip the 'PACK' header for the first read
  340.             int ptr = packHeadSkipped ? 0 : 12;
  341.             buf.position(0);
  342.             int bufLen = read(rc, buf);
  343.             if (bufLen <= ptr) {
  344.                 throw packfileIsTruncated();
  345.             }
  346.             int n = (int) Math.min(bufLen - ptr, remaining);
  347.             out.write(buf.array(), ptr, n);
  348.             position += n;
  349.             remaining -= n;
  350.             packHeadSkipped = true;
  351.         }
  352.         return position;
  353.     }

  354.     private ByteBuffer newCopyBuffer(PackOutputStream out, ReadableChannel rc) {
  355.         int bs = blockSize(rc);
  356.         byte[] copyBuf = out.getCopyBuffer();
  357.         if (bs > copyBuf.length) {
  358.             copyBuf = new byte[bs];
  359.         }
  360.         return ByteBuffer.wrap(copyBuf, 0, bs);
  361.     }

  362.     void copyAsIs(PackOutputStream out, DfsObjectToPack src,
  363.             boolean validate, DfsReader ctx) throws IOException,
  364.             StoredObjectRepresentationNotAvailableException {
  365.         final CRC32 crc1 = validate ? new CRC32() : null;
  366.         final CRC32 crc2 = validate ? new CRC32() : null;
  367.         final byte[] buf = out.getCopyBuffer();

  368.         // Rip apart the header so we can discover the size.
  369.         //
  370.         try {
  371.             readFully(src.offset, buf, 0, 20, ctx);
  372.         } catch (IOException ioError) {
  373.             throw new StoredObjectRepresentationNotAvailableException(src,
  374.                     ioError);
  375.         }
  376.         int c = buf[0] & 0xff;
  377.         final int typeCode = (c >> 4) & 7;
  378.         long inflatedLength = c & 15;
  379.         int shift = 4;
  380.         int headerCnt = 1;
  381.         while ((c & 0x80) != 0) {
  382.             c = buf[headerCnt++] & 0xff;
  383.             inflatedLength += ((long) (c & 0x7f)) << shift;
  384.             shift += 7;
  385.         }

  386.         if (typeCode == Constants.OBJ_OFS_DELTA) {
  387.             do {
  388.                 c = buf[headerCnt++] & 0xff;
  389.             } while ((c & 128) != 0);
  390.             if (validate) {
  391.                 assert(crc1 != null && crc2 != null);
  392.                 crc1.update(buf, 0, headerCnt);
  393.                 crc2.update(buf, 0, headerCnt);
  394.             }
  395.         } else if (typeCode == Constants.OBJ_REF_DELTA) {
  396.             if (validate) {
  397.                 assert(crc1 != null && crc2 != null);
  398.                 crc1.update(buf, 0, headerCnt);
  399.                 crc2.update(buf, 0, headerCnt);
  400.             }

  401.             readFully(src.offset + headerCnt, buf, 0, 20, ctx);
  402.             if (validate) {
  403.                 assert(crc1 != null && crc2 != null);
  404.                 crc1.update(buf, 0, 20);
  405.                 crc2.update(buf, 0, 20);
  406.             }
  407.             headerCnt += 20;
  408.         } else if (validate) {
  409.             assert(crc1 != null && crc2 != null);
  410.             crc1.update(buf, 0, headerCnt);
  411.             crc2.update(buf, 0, headerCnt);
  412.         }

  413.         final long dataOffset = src.offset + headerCnt;
  414.         final long dataLength = src.length;
  415.         final long expectedCRC;
  416.         final DfsBlock quickCopy;

  417.         // Verify the object isn't corrupt before sending. If it is,
  418.         // we report it missing instead.
  419.         //
  420.         try {
  421.             quickCopy = ctx.quickCopy(this, dataOffset, dataLength);

  422.             if (validate && idx(ctx).hasCRC32Support()) {
  423.                 assert(crc1 != null);
  424.                 // Index has the CRC32 code cached, validate the object.
  425.                 //
  426.                 expectedCRC = idx(ctx).findCRC32(src);
  427.                 if (quickCopy != null) {
  428.                     quickCopy.crc32(crc1, dataOffset, (int) dataLength);
  429.                 } else {
  430.                     long pos = dataOffset;
  431.                     long cnt = dataLength;
  432.                     while (cnt > 0) {
  433.                         final int n = (int) Math.min(cnt, buf.length);
  434.                         readFully(pos, buf, 0, n, ctx);
  435.                         crc1.update(buf, 0, n);
  436.                         pos += n;
  437.                         cnt -= n;
  438.                     }
  439.                 }
  440.                 if (crc1.getValue() != expectedCRC) {
  441.                     setCorrupt(src.offset);
  442.                     throw new CorruptObjectException(MessageFormat.format(
  443.                             JGitText.get().objectAtHasBadZlibStream,
  444.                             Long.valueOf(src.offset), getFileName()));
  445.                 }
  446.             } else if (validate) {
  447.                 assert(crc1 != null);
  448.                 // We don't have a CRC32 code in the index, so compute it
  449.                 // now while inflating the raw data to get zlib to tell us
  450.                 // whether or not the data is safe.
  451.                 //
  452.                 Inflater inf = ctx.inflater();
  453.                 byte[] tmp = new byte[1024];
  454.                 if (quickCopy != null) {
  455.                     quickCopy.check(inf, tmp, dataOffset, (int) dataLength);
  456.                 } else {
  457.                     long pos = dataOffset;
  458.                     long cnt = dataLength;
  459.                     while (cnt > 0) {
  460.                         final int n = (int) Math.min(cnt, buf.length);
  461.                         readFully(pos, buf, 0, n, ctx);
  462.                         crc1.update(buf, 0, n);
  463.                         inf.setInput(buf, 0, n);
  464.                         while (inf.inflate(tmp, 0, tmp.length) > 0) {
  465.                             continue;
  466.                         }
  467.                         pos += n;
  468.                         cnt -= n;
  469.                     }
  470.                 }
  471.                 if (!inf.finished() || inf.getBytesRead() != dataLength) {
  472.                     setCorrupt(src.offset);
  473.                     throw new EOFException(MessageFormat.format(
  474.                             JGitText.get().shortCompressedStreamAt,
  475.                             Long.valueOf(src.offset)));
  476.                 }
  477.                 expectedCRC = crc1.getValue();
  478.             } else {
  479.                 expectedCRC = -1;
  480.             }
  481.         } catch (DataFormatException dataFormat) {
  482.             setCorrupt(src.offset);

  483.             CorruptObjectException corruptObject = new CorruptObjectException(
  484.                     MessageFormat.format(
  485.                             JGitText.get().objectAtHasBadZlibStream,
  486.                             Long.valueOf(src.offset), getFileName()),
  487.                     dataFormat);

  488.             throw new StoredObjectRepresentationNotAvailableException(src,
  489.                     corruptObject);

  490.         } catch (IOException ioError) {
  491.             throw new StoredObjectRepresentationNotAvailableException(src,
  492.                     ioError);
  493.         }

  494.         if (quickCopy != null) {
  495.             // The entire object fits into a single byte array window slice,
  496.             // and we have it pinned.  Write this out without copying.
  497.             //
  498.             out.writeHeader(src, inflatedLength);
  499.             quickCopy.write(out, dataOffset, (int) dataLength);

  500.         } else if (dataLength <= buf.length) {
  501.             // Tiny optimization: Lots of objects are very small deltas or
  502.             // deflated commits that are likely to fit in the copy buffer.
  503.             //
  504.             if (!validate) {
  505.                 long pos = dataOffset;
  506.                 long cnt = dataLength;
  507.                 while (cnt > 0) {
  508.                     final int n = (int) Math.min(cnt, buf.length);
  509.                     readFully(pos, buf, 0, n, ctx);
  510.                     pos += n;
  511.                     cnt -= n;
  512.                 }
  513.             }
  514.             out.writeHeader(src, inflatedLength);
  515.             out.write(buf, 0, (int) dataLength);
  516.         } else {
  517.             // Now we are committed to sending the object. As we spool it out,
  518.             // check its CRC32 code to make sure there wasn't corruption between
  519.             // the verification we did above, and us actually outputting it.
  520.             //
  521.             out.writeHeader(src, inflatedLength);
  522.             long pos = dataOffset;
  523.             long cnt = dataLength;
  524.             while (cnt > 0) {
  525.                 final int n = (int) Math.min(cnt, buf.length);
  526.                 readFully(pos, buf, 0, n, ctx);
  527.                 if (validate) {
  528.                     assert(crc2 != null);
  529.                     crc2.update(buf, 0, n);
  530.                 }
  531.                 out.write(buf, 0, n);
  532.                 pos += n;
  533.                 cnt -= n;
  534.             }
  535.             if (validate) {
  536.                 assert(crc2 != null);
  537.                 if (crc2.getValue() != expectedCRC) {
  538.                     throw new CorruptObjectException(MessageFormat.format(
  539.                             JGitText.get().objectAtHasBadZlibStream,
  540.                             Long.valueOf(src.offset), getFileName()));
  541.                 }
  542.             }
  543.         }
  544.     }

  545.     private IOException packfileIsTruncated() {
  546.         invalid = true;
  547.         IOException exc = new IOException(MessageFormat.format(
  548.                 JGitText.get().packfileIsTruncated, getFileName()));
  549.         invalidatingCause = exc;
  550.         return exc;
  551.     }

  552.     private void readFully(long position, byte[] dstbuf, int dstoff, int cnt,
  553.             DfsReader ctx) throws IOException {
  554.         while (cnt > 0) {
  555.             int copied = ctx.copy(this, position, dstbuf, dstoff, cnt);
  556.             if (copied == 0) {
  557.                 throw new EOFException();
  558.             }
  559.             position += copied;
  560.             dstoff += copied;
  561.             cnt -= copied;
  562.         }
  563.     }

  564.     ObjectLoader load(DfsReader ctx, long pos)
  565.             throws IOException {
  566.         try {
  567.             final byte[] ib = ctx.tempId;
  568.             Delta delta = null;
  569.             byte[] data = null;
  570.             int type = Constants.OBJ_BAD;
  571.             boolean cached = false;

  572.             SEARCH: for (;;) {
  573.                 readFully(pos, ib, 0, 20, ctx);
  574.                 int c = ib[0] & 0xff;
  575.                 final int typeCode = (c >> 4) & 7;
  576.                 long sz = c & 15;
  577.                 int shift = 4;
  578.                 int p = 1;
  579.                 while ((c & 0x80) != 0) {
  580.                     c = ib[p++] & 0xff;
  581.                     sz += ((long) (c & 0x7f)) << shift;
  582.                     shift += 7;
  583.                 }

  584.                 switch (typeCode) {
  585.                 case Constants.OBJ_COMMIT:
  586.                 case Constants.OBJ_TREE:
  587.                 case Constants.OBJ_BLOB:
  588.                 case Constants.OBJ_TAG: {
  589.                     if (delta != null) {
  590.                         data = decompress(pos + p, (int) sz, ctx);
  591.                         type = typeCode;
  592.                         break SEARCH;
  593.                     }

  594.                     if (sz < ctx.getStreamFileThreshold()) {
  595.                         data = decompress(pos + p, (int) sz, ctx);
  596.                         if (data != null) {
  597.                             return new ObjectLoader.SmallObject(typeCode, data);
  598.                         }
  599.                     }
  600.                     return new LargePackedWholeObject(typeCode, sz, pos, p, this, ctx.db);
  601.                 }

  602.                 case Constants.OBJ_OFS_DELTA: {
  603.                     c = ib[p++] & 0xff;
  604.                     long base = c & 127;
  605.                     while ((c & 128) != 0) {
  606.                         base += 1;
  607.                         c = ib[p++] & 0xff;
  608.                         base <<= 7;
  609.                         base += (c & 127);
  610.                     }
  611.                     base = pos - base;
  612.                     delta = new Delta(delta, pos, (int) sz, p, base);
  613.                     if (sz != delta.deltaSize) {
  614.                         break SEARCH;
  615.                     }

  616.                     DeltaBaseCache.Entry e = ctx.getDeltaBaseCache().get(key, base);
  617.                     if (e != null) {
  618.                         type = e.type;
  619.                         data = e.data;
  620.                         cached = true;
  621.                         break SEARCH;
  622.                     }
  623.                     pos = base;
  624.                     continue SEARCH;
  625.                 }

  626.                 case Constants.OBJ_REF_DELTA: {
  627.                     readFully(pos + p, ib, 0, 20, ctx);
  628.                     long base = findDeltaBase(ctx, ObjectId.fromRaw(ib));
  629.                     delta = new Delta(delta, pos, (int) sz, p + 20, base);
  630.                     if (sz != delta.deltaSize) {
  631.                         break SEARCH;
  632.                     }

  633.                     DeltaBaseCache.Entry e = ctx.getDeltaBaseCache().get(key, base);
  634.                     if (e != null) {
  635.                         type = e.type;
  636.                         data = e.data;
  637.                         cached = true;
  638.                         break SEARCH;
  639.                     }
  640.                     pos = base;
  641.                     continue SEARCH;
  642.                 }

  643.                 default:
  644.                     throw new IOException(MessageFormat.format(
  645.                             JGitText.get().unknownObjectType, Integer.valueOf(typeCode)));
  646.                 }
  647.             }

  648.             // At this point there is at least one delta to apply to data.
  649.             // (Whole objects with no deltas to apply return early above.)

  650.             if (data == null)
  651.                 throw new LargeObjectException();

  652.             assert(delta != null);
  653.             do {
  654.                 // Cache only the base immediately before desired object.
  655.                 if (cached) {
  656.                     cached = false;
  657.                 } else if (delta.next == null) {
  658.                     ctx.getDeltaBaseCache().put(key, delta.basePos, type, data);
  659.                 }

  660.                 pos = delta.deltaPos;

  661.                 byte[] cmds = decompress(pos + delta.hdrLen, delta.deltaSize, ctx);
  662.                 if (cmds == null) {
  663.                     data = null; // Discard base in case of OutOfMemoryError
  664.                     throw new LargeObjectException();
  665.                 }

  666.                 final long sz = BinaryDelta.getResultSize(cmds);
  667.                 if (Integer.MAX_VALUE <= sz) {
  668.                     throw new LargeObjectException.ExceedsByteArrayLimit();
  669.                 }

  670.                 final byte[] result;
  671.                 try {
  672.                     result = new byte[(int) sz];
  673.                 } catch (OutOfMemoryError tooBig) {
  674.                     data = null; // Discard base in case of OutOfMemoryError
  675.                     cmds = null;
  676.                     throw new LargeObjectException.OutOfMemory(tooBig);
  677.                 }

  678.                 BinaryDelta.apply(data, cmds, result);
  679.                 data = result;
  680.                 delta = delta.next;
  681.             } while (delta != null);

  682.             return new ObjectLoader.SmallObject(type, data);

  683.         } catch (DataFormatException dfe) {
  684.             throw new CorruptObjectException(
  685.                     MessageFormat.format(
  686.                             JGitText.get().objectAtHasBadZlibStream, Long.valueOf(pos),
  687.                             getFileName()),
  688.                     dfe);
  689.         }
  690.     }

  691.     private long findDeltaBase(DfsReader ctx, ObjectId baseId)
  692.             throws IOException, MissingObjectException {
  693.         long ofs = idx(ctx).findOffset(baseId);
  694.         if (ofs < 0) {
  695.             throw new MissingObjectException(baseId,
  696.                     JGitText.get().missingDeltaBase);
  697.         }
  698.         return ofs;
  699.     }

  700.     private static class Delta {
  701.         /** Child that applies onto this object. */
  702.         final Delta next;

  703.         /** Offset of the delta object. */
  704.         final long deltaPos;

  705.         /** Size of the inflated delta stream. */
  706.         final int deltaSize;

  707.         /** Total size of the delta's pack entry header (including base). */
  708.         final int hdrLen;

  709.         /** Offset of the base object this delta applies onto. */
  710.         final long basePos;

  711.         Delta(Delta next, long ofs, int sz, int hdrLen, long baseOffset) {
  712.             this.next = next;
  713.             this.deltaPos = ofs;
  714.             this.deltaSize = sz;
  715.             this.hdrLen = hdrLen;
  716.             this.basePos = baseOffset;
  717.         }
  718.     }

  719.     byte[] getDeltaHeader(DfsReader wc, long pos)
  720.             throws IOException, DataFormatException {
  721.         // The delta stream starts as two variable length integers. If we
  722.         // assume they are 64 bits each, we need 16 bytes to encode them,
  723.         // plus 2 extra bytes for the variable length overhead. So 18 is
  724.         // the longest delta instruction header.
  725.         //
  726.         final byte[] hdr = new byte[32];
  727.         wc.inflate(this, pos, hdr, true /* header only */);
  728.         return hdr;
  729.     }

  730.     int getObjectType(DfsReader ctx, long pos) throws IOException {
  731.         final byte[] ib = ctx.tempId;
  732.         for (;;) {
  733.             readFully(pos, ib, 0, 20, ctx);
  734.             int c = ib[0] & 0xff;
  735.             final int type = (c >> 4) & 7;

  736.             switch (type) {
  737.             case Constants.OBJ_COMMIT:
  738.             case Constants.OBJ_TREE:
  739.             case Constants.OBJ_BLOB:
  740.             case Constants.OBJ_TAG:
  741.                 return type;

  742.             case Constants.OBJ_OFS_DELTA: {
  743.                 int p = 1;
  744.                 while ((c & 0x80) != 0) {
  745.                     c = ib[p++] & 0xff;
  746.                 }
  747.                 c = ib[p++] & 0xff;
  748.                 long ofs = c & 127;
  749.                 while ((c & 128) != 0) {
  750.                     ofs += 1;
  751.                     c = ib[p++] & 0xff;
  752.                     ofs <<= 7;
  753.                     ofs += (c & 127);
  754.                 }
  755.                 pos = pos - ofs;
  756.                 continue;
  757.             }

  758.             case Constants.OBJ_REF_DELTA: {
  759.                 int p = 1;
  760.                 while ((c & 0x80) != 0) {
  761.                     c = ib[p++] & 0xff;
  762.                 }
  763.                 readFully(pos + p, ib, 0, 20, ctx);
  764.                 pos = findDeltaBase(ctx, ObjectId.fromRaw(ib));
  765.                 continue;
  766.             }

  767.             default:
  768.                 throw new IOException(MessageFormat.format(
  769.                         JGitText.get().unknownObjectType, Integer.valueOf(type)));
  770.             }
  771.         }
  772.     }

  773.     long getObjectSize(DfsReader ctx, AnyObjectId id) throws IOException {
  774.         final long offset = idx(ctx).findOffset(id);
  775.         return 0 < offset ? getObjectSize(ctx, offset) : -1;
  776.     }

  777.     long getObjectSize(DfsReader ctx, long pos)
  778.             throws IOException {
  779.         final byte[] ib = ctx.tempId;
  780.         readFully(pos, ib, 0, 20, ctx);
  781.         int c = ib[0] & 0xff;
  782.         final int type = (c >> 4) & 7;
  783.         long sz = c & 15;
  784.         int shift = 4;
  785.         int p = 1;
  786.         while ((c & 0x80) != 0) {
  787.             c = ib[p++] & 0xff;
  788.             sz += ((long) (c & 0x7f)) << shift;
  789.             shift += 7;
  790.         }

  791.         long deltaAt;
  792.         switch (type) {
  793.         case Constants.OBJ_COMMIT:
  794.         case Constants.OBJ_TREE:
  795.         case Constants.OBJ_BLOB:
  796.         case Constants.OBJ_TAG:
  797.             return sz;

  798.         case Constants.OBJ_OFS_DELTA:
  799.             c = ib[p++] & 0xff;
  800.             while ((c & 128) != 0) {
  801.                 c = ib[p++] & 0xff;
  802.             }
  803.             deltaAt = pos + p;
  804.             break;

  805.         case Constants.OBJ_REF_DELTA:
  806.             deltaAt = pos + p + 20;
  807.             break;

  808.         default:
  809.             throw new IOException(MessageFormat.format(
  810.                     JGitText.get().unknownObjectType, Integer.valueOf(type)));
  811.         }

  812.         try {
  813.             return BinaryDelta.getResultSize(getDeltaHeader(ctx, deltaAt));
  814.         } catch (DataFormatException dfe) {
  815.             throw new CorruptObjectException(
  816.                     MessageFormat.format(
  817.                             JGitText.get().objectAtHasBadZlibStream, Long.valueOf(pos),
  818.                             getFileName()),
  819.                     dfe);
  820.         }
  821.     }

  822.     void representation(DfsObjectRepresentation r, final long pos,
  823.             DfsReader ctx, PackReverseIndex rev)
  824.             throws IOException {
  825.         r.offset = pos;
  826.         final byte[] ib = ctx.tempId;
  827.         readFully(pos, ib, 0, 20, ctx);
  828.         int c = ib[0] & 0xff;
  829.         int p = 1;
  830.         final int typeCode = (c >> 4) & 7;
  831.         while ((c & 0x80) != 0) {
  832.             c = ib[p++] & 0xff;
  833.         }

  834.         long len = rev.findNextOffset(pos, length - 20) - pos;
  835.         switch (typeCode) {
  836.         case Constants.OBJ_COMMIT:
  837.         case Constants.OBJ_TREE:
  838.         case Constants.OBJ_BLOB:
  839.         case Constants.OBJ_TAG:
  840.             r.format = StoredObjectRepresentation.PACK_WHOLE;
  841.             r.baseId = null;
  842.             r.length = len - p;
  843.             return;

  844.         case Constants.OBJ_OFS_DELTA: {
  845.             c = ib[p++] & 0xff;
  846.             long ofs = c & 127;
  847.             while ((c & 128) != 0) {
  848.                 ofs += 1;
  849.                 c = ib[p++] & 0xff;
  850.                 ofs <<= 7;
  851.                 ofs += (c & 127);
  852.             }
  853.             r.format = StoredObjectRepresentation.PACK_DELTA;
  854.             r.baseId = rev.findObject(pos - ofs);
  855.             r.length = len - p;
  856.             return;
  857.         }

  858.         case Constants.OBJ_REF_DELTA: {
  859.             readFully(pos + p, ib, 0, 20, ctx);
  860.             r.format = StoredObjectRepresentation.PACK_DELTA;
  861.             r.baseId = ObjectId.fromRaw(ib);
  862.             r.length = len - p - 20;
  863.             return;
  864.         }

  865.         default:
  866.             throw new IOException(MessageFormat.format(
  867.                     JGitText.get().unknownObjectType, Integer.valueOf(typeCode)));
  868.         }
  869.     }

  870.     boolean isCorrupt(long offset) {
  871.         LongList list = corruptObjects;
  872.         if (list == null) {
  873.             return false;
  874.         }
  875.         synchronized (list) {
  876.             return list.contains(offset);
  877.         }
  878.     }

  879.     private void setCorrupt(long offset) {
  880.         LongList list = corruptObjects;
  881.         if (list == null) {
  882.             synchronized (initLock) {
  883.                 list = corruptObjects;
  884.                 if (list == null) {
  885.                     list = new LongList();
  886.                     corruptObjects = list;
  887.                 }
  888.             }
  889.         }
  890.         synchronized (list) {
  891.             list.add(offset);
  892.         }
  893.     }

  894.     private DfsBlockCache.Ref<PackIndex> loadPackIndex(
  895.             DfsReader ctx, DfsStreamKey idxKey) throws IOException {
  896.         try {
  897.             ctx.stats.readIdx++;
  898.             long start = System.nanoTime();
  899.             try (ReadableChannel rc = ctx.db.openFile(desc, INDEX)) {
  900.                 InputStream in = Channels.newInputStream(rc);
  901.                 int wantSize = 8192;
  902.                 int bs = rc.blockSize();
  903.                 if (0 < bs && bs < wantSize) {
  904.                     bs = (wantSize / bs) * bs;
  905.                 } else if (bs <= 0) {
  906.                     bs = wantSize;
  907.                 }
  908.                 PackIndex idx = PackIndex.read(new BufferedInputStream(in, bs));
  909.                 ctx.stats.readIdxBytes += rc.position();
  910.                 index = idx;
  911.                 return new DfsBlockCache.Ref<>(
  912.                         idxKey,
  913.                         REF_POSITION,
  914.                         idx.getObjectCount() * REC_SIZE,
  915.                         idx);
  916.             } finally {
  917.                 ctx.stats.readIdxMicros += elapsedMicros(start);
  918.             }
  919.         } catch (EOFException e) {
  920.             throw new IOException(MessageFormat.format(
  921.                     DfsText.get().shortReadOfIndex,
  922.                     desc.getFileName(INDEX)), e);
  923.         } catch (IOException e) {
  924.             throw new IOException(MessageFormat.format(
  925.                     DfsText.get().cannotReadIndex,
  926.                     desc.getFileName(INDEX)), e);
  927.         }
  928.     }

  929.     private DfsBlockCache.Ref<PackReverseIndex> loadReverseIdx(
  930.             DfsStreamKey revKey, PackIndex idx) {
  931.         PackReverseIndex revidx = new PackReverseIndex(idx);
  932.         reverseIndex = revidx;
  933.         return new DfsBlockCache.Ref<>(
  934.                 revKey,
  935.                 REF_POSITION,
  936.                 idx.getObjectCount() * 8,
  937.                 revidx);
  938.     }

  939.     private DfsBlockCache.Ref<PackBitmapIndex> loadBitmapIndex(
  940.             DfsReader ctx,
  941.             DfsStreamKey bitmapKey,
  942.             PackIndex idx,
  943.             PackReverseIndex revidx) throws IOException {
  944.         ctx.stats.readBitmap++;
  945.         long start = System.nanoTime();
  946.         try (ReadableChannel rc = ctx.db.openFile(desc, BITMAP_INDEX)) {
  947.             long size;
  948.             PackBitmapIndex bmidx;
  949.             try {
  950.                 InputStream in = Channels.newInputStream(rc);
  951.                 int wantSize = 8192;
  952.                 int bs = rc.blockSize();
  953.                 if (0 < bs && bs < wantSize) {
  954.                     bs = (wantSize / bs) * bs;
  955.                 } else if (bs <= 0) {
  956.                     bs = wantSize;
  957.                 }
  958.                 in = new BufferedInputStream(in, bs);
  959.                 bmidx = PackBitmapIndex.read(in, idx, revidx);
  960.             } finally {
  961.                 size = rc.position();
  962.                 ctx.stats.readIdxBytes += size;
  963.                 ctx.stats.readIdxMicros += elapsedMicros(start);
  964.             }
  965.             bitmapIndex = bmidx;
  966.             return new DfsBlockCache.Ref<>(
  967.                     bitmapKey, REF_POSITION, size, bmidx);
  968.         } catch (EOFException e) {
  969.             throw new IOException(MessageFormat.format(
  970.                     DfsText.get().shortReadOfIndex,
  971.                     desc.getFileName(BITMAP_INDEX)), e);
  972.         } catch (IOException e) {
  973.             throw new IOException(MessageFormat.format(
  974.                     DfsText.get().cannotReadIndex,
  975.                     desc.getFileName(BITMAP_INDEX)), e);
  976.         }
  977.     }
  978. }