Replace inefficient new Long(long) constructor to silence FindBugs
[egit/imyousuf.git] / org.spearce.jgit / src / org / spearce / jgit / transport / IndexPack.java
blob59fdeaefa5f9dc277663bf9385a87cf14356c3f6
1 /*
2 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
9 * conditions are met:
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
19 * - Neither the name of the Git Development Community nor the
20 * names of its contributors may be used to endorse or promote
21 * products derived from this software without specific prior
22 * written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 package org.spearce.jgit.transport;
41 import java.io.EOFException;
42 import java.io.File;
43 import java.io.FileOutputStream;
44 import java.io.IOException;
45 import java.io.InputStream;
46 import java.io.RandomAccessFile;
47 import java.security.MessageDigest;
48 import java.util.ArrayList;
49 import java.util.Arrays;
50 import java.util.List;
51 import java.util.zip.CRC32;
52 import java.util.zip.DataFormatException;
53 import java.util.zip.Deflater;
54 import java.util.zip.Inflater;
56 import org.spearce.jgit.errors.CorruptObjectException;
57 import org.spearce.jgit.errors.MissingObjectException;
58 import org.spearce.jgit.lib.AnyObjectId;
59 import org.spearce.jgit.lib.BinaryDelta;
60 import org.spearce.jgit.lib.Constants;
61 import org.spearce.jgit.lib.InflaterCache;
62 import org.spearce.jgit.lib.MutableObjectId;
63 import org.spearce.jgit.lib.ObjectChecker;
64 import org.spearce.jgit.lib.ObjectId;
65 import org.spearce.jgit.lib.ObjectIdMap;
66 import org.spearce.jgit.lib.ObjectLoader;
67 import org.spearce.jgit.lib.PackIndexWriter;
68 import org.spearce.jgit.lib.ProgressMonitor;
69 import org.spearce.jgit.lib.Repository;
70 import org.spearce.jgit.lib.WindowCursor;
71 import org.spearce.jgit.util.NB;
73 /** Indexes Git pack files for local use. */
74 public class IndexPack {
75 /** Progress message when reading raw data from the pack. */
76 public static final String PROGRESS_DOWNLOAD = "Receiving objects";
78 /** Progress message when computing names of delta compressed objects. */
79 public static final String PROGRESS_RESOLVE_DELTA = "Resolving deltas";
81 /**
82 * Size of the internal stream buffer.
83 * <p>
84 * If callers are going to be supplying IndexPack a BufferedInputStream they
85 * should use this buffer size as the size of the buffer for that
86 * BufferedInputStream, and any other its may be wrapping. This way the
87 * buffers will cascade efficiently and only the IndexPack buffer will be
88 * receiving the bulk of the data stream.
90 public static final int BUFFER_SIZE = 8192;
92 /**
93 * Create an index pack instance to load a new pack into a repository.
94 * <p>
95 * The received pack data and generated index will be saved to temporary
96 * files within the repository's <code>objects</code> directory. To use the
97 * data contained within them call {@link #renameAndOpenPack()} once the
98 * indexing is complete.
100 * @param db
101 * the repository that will receive the new pack.
102 * @param is
103 * stream to read the pack data from. If the stream is buffered
104 * use {@link #BUFFER_SIZE} as the buffer size for the stream.
105 * @return a new index pack instance.
106 * @throws IOException
107 * a temporary file could not be created.
109 public static IndexPack create(final Repository db, final InputStream is)
110 throws IOException {
111 final String suffix = ".pack";
112 final File objdir = db.getObjectsDirectory();
113 final File tmp = File.createTempFile("incoming_", suffix, objdir);
114 final String n = tmp.getName();
115 final File base;
117 base = new File(objdir, n.substring(0, n.length() - suffix.length()));
118 final IndexPack ip = new IndexPack(db, is, base);
119 ip.setIndexVersion(db.getConfig().getCore().getPackIndexVersion());
120 return ip;
123 private final Repository repo;
125 private Inflater inflater;
127 private final MessageDigest objectDigest;
129 private final MutableObjectId tempObjectId;
131 private InputStream in;
133 private byte[] buf;
135 private long bBase;
137 private int bOffset;
139 private int bAvail;
141 private ObjectChecker objCheck;
143 private boolean fixThin;
145 private boolean keepEmpty;
147 private int outputVersion;
149 private final File dstPack;
151 private final File dstIdx;
153 private long objectCount;
155 private PackedObjectInfo[] entries;
157 private int deltaCount;
159 private int entryCount;
161 private final CRC32 crc = new CRC32();
163 private ObjectIdMap<ArrayList<UnresolvedDelta>> baseById;
165 private LongMap<ArrayList<UnresolvedDelta>> baseByPos;
167 private byte[] objectData;
169 private MessageDigest packDigest;
171 private RandomAccessFile packOut;
173 private byte[] packcsum;
175 /** If {@link #fixThin} this is the last byte of the original checksum. */
176 private long originalEOF;
178 private WindowCursor readCurs;
181 * Create a new pack indexer utility.
183 * @param db
184 * @param src
185 * stream to read the pack data from. If the stream is buffered
186 * use {@link #BUFFER_SIZE} as the buffer size for the stream.
187 * @param dstBase
188 * @throws IOException
189 * the output packfile could not be created.
191 public IndexPack(final Repository db, final InputStream src,
192 final File dstBase) throws IOException {
193 repo = db;
194 in = src;
195 inflater = InflaterCache.get();
196 readCurs = new WindowCursor();
197 buf = new byte[BUFFER_SIZE];
198 objectData = new byte[BUFFER_SIZE];
199 objectDigest = Constants.newMessageDigest();
200 tempObjectId = new MutableObjectId();
201 packDigest = Constants.newMessageDigest();
203 if (dstBase != null) {
204 final File dir = dstBase.getParentFile();
205 final String nam = dstBase.getName();
206 dstPack = new File(dir, nam + ".pack");
207 dstIdx = new File(dir, nam + ".idx");
208 packOut = new RandomAccessFile(dstPack, "rw");
209 packOut.setLength(0);
210 } else {
211 dstPack = null;
212 dstIdx = null;
217 * Set the pack index file format version this instance will create.
219 * @param version
220 * the version to write. The special version 0 designates the
221 * oldest (most compatible) format available for the objects.
222 * @see PackIndexWriter
224 public void setIndexVersion(final int version) {
225 outputVersion = version;
229 * Configure this index pack instance to make a thin pack complete.
230 * <p>
231 * Thin packs are sometimes used during network transfers to allow a delta
232 * to be sent without a base object. Such packs are not permitted on disk.
233 * They can be fixed by copying the base object onto the end of the pack.
235 * @param fix
236 * true to enable fixing a thin pack.
238 public void setFixThin(final boolean fix) {
239 fixThin = fix;
243 * Configure this index pack instance to keep an empty pack.
244 * <p>
245 * By default an empty pack (a pack with no objects) is not kept, as doing
246 * so is completely pointless. With no objects in the pack there is no data
247 * stored by it, so the pack is unnecessary.
249 * @param empty true to enable keeping an empty pack.
251 public void setKeepEmpty(final boolean empty) {
252 keepEmpty = empty;
256 * Configure the checker used to validate received objects.
257 * <p>
258 * Usually object checking isn't necessary, as Git implementations only
259 * create valid objects in pack files. However, additional checking may be
260 * useful if processing data from an untrusted source.
262 * @param oc
263 * the checker instance; null to disable object checking.
265 public void setObjectChecker(final ObjectChecker oc) {
266 objCheck = oc;
270 * Configure the checker used to validate received objects.
271 * <p>
272 * Usually object checking isn't necessary, as Git implementations only
273 * create valid objects in pack files. However, additional checking may be
274 * useful if processing data from an untrusted source.
275 * <p>
276 * This is shorthand for:
278 * <pre>
279 * setObjectChecker(on ? new ObjectChecker() : null);
280 * </pre>
282 * @param on
283 * true to enable the default checker; false to disable it.
285 public void setObjectChecking(final boolean on) {
286 setObjectChecker(on ? new ObjectChecker() : null);
290 * Consume data from the input stream until the packfile is indexed.
292 * @param progress
293 * progress feedback
295 * @throws IOException
297 public void index(final ProgressMonitor progress) throws IOException {
298 progress.start(2 /* tasks */);
299 try {
300 try {
301 readPackHeader();
303 entries = new PackedObjectInfo[(int) objectCount];
304 baseById = new ObjectIdMap<ArrayList<UnresolvedDelta>>();
305 baseByPos = new LongMap<ArrayList<UnresolvedDelta>>();
307 progress.beginTask(PROGRESS_DOWNLOAD, (int) objectCount);
308 for (int done = 0; done < objectCount; done++) {
309 indexOneObject();
310 progress.update(1);
311 if (progress.isCancelled())
312 throw new IOException("Download cancelled");
314 readPackFooter();
315 endInput();
316 progress.endTask();
317 if (deltaCount > 0) {
318 if (packOut == null)
319 throw new IOException("need packOut");
320 resolveDeltas(progress);
321 if (entryCount < objectCount) {
322 if (!fixThin) {
323 throw new IOException("pack has "
324 + (objectCount - entryCount)
325 + " unresolved deltas");
327 fixThinPack(progress);
330 if (packOut != null && (keepEmpty || entryCount > 0))
331 packOut.getChannel().force(true);
333 packDigest = null;
334 baseById = null;
335 baseByPos = null;
337 if (dstIdx != null && (keepEmpty || entryCount > 0))
338 writeIdx();
340 } finally {
341 try {
342 InflaterCache.release(inflater);
343 } finally {
344 inflater = null;
346 readCurs = WindowCursor.release(readCurs);
348 progress.endTask();
349 if (packOut != null)
350 packOut.close();
353 if (keepEmpty || entryCount > 0) {
354 if (dstPack != null)
355 dstPack.setReadOnly();
356 if (dstIdx != null)
357 dstIdx.setReadOnly();
359 } catch (IOException err) {
360 if (dstPack != null)
361 dstPack.delete();
362 if (dstIdx != null)
363 dstIdx.delete();
364 throw err;
368 private void resolveDeltas(final ProgressMonitor progress)
369 throws IOException {
370 progress.beginTask(PROGRESS_RESOLVE_DELTA, deltaCount);
371 final int last = entryCount;
372 for (int i = 0; i < last; i++) {
373 final int before = entryCount;
374 resolveDeltas(entries[i]);
375 progress.update(entryCount - before);
376 if (progress.isCancelled())
377 throw new IOException("Download cancelled during indexing");
379 progress.endTask();
382 private void resolveDeltas(final PackedObjectInfo oe) throws IOException {
383 final int oldCRC = oe.getCRC();
384 if (baseById.containsKey(oe) || baseByPos.containsKey(oe.getOffset()))
385 resolveDeltas(oe.getOffset(), oldCRC, Constants.OBJ_BAD, null, oe);
388 private void resolveDeltas(final long pos, final int oldCRC, int type,
389 byte[] data, PackedObjectInfo oe) throws IOException {
390 crc.reset();
391 position(pos);
392 int c = readFromFile();
393 final int typeCode = (c >> 4) & 7;
394 long sz = c & 15;
395 int shift = 4;
396 while ((c & 0x80) != 0) {
397 c = readFromFile();
398 sz += (c & 0x7f) << shift;
399 shift += 7;
402 switch (typeCode) {
403 case Constants.OBJ_COMMIT:
404 case Constants.OBJ_TREE:
405 case Constants.OBJ_BLOB:
406 case Constants.OBJ_TAG:
407 type = typeCode;
408 data = inflateFromFile((int) sz);
409 break;
410 case Constants.OBJ_OFS_DELTA: {
411 c = readFromFile() & 0xff;
412 while ((c & 128) != 0)
413 c = readFromFile() & 0xff;
414 data = BinaryDelta.apply(data, inflateFromFile((int) sz));
415 break;
417 case Constants.OBJ_REF_DELTA: {
418 crc.update(buf, fillFromFile(20), 20);
419 use(20);
420 data = BinaryDelta.apply(data, inflateFromFile((int) sz));
421 break;
423 default:
424 throw new IOException("Unknown object type " + typeCode + ".");
427 final int crc32 = (int) crc.getValue();
428 if (oldCRC != crc32)
429 throw new IOException("Corruption detected re-reading at " + pos);
430 if (oe == null) {
431 objectDigest.update(Constants.encodedTypeString(type));
432 objectDigest.update((byte) ' ');
433 objectDigest.update(Constants.encodeASCII(data.length));
434 objectDigest.update((byte) 0);
435 objectDigest.update(data);
436 tempObjectId.fromRaw(objectDigest.digest(), 0);
438 verifySafeObject(tempObjectId, type, data);
439 oe = new PackedObjectInfo(pos, crc32, tempObjectId);
440 entries[entryCount++] = oe;
443 resolveChildDeltas(pos, type, data, oe);
446 private void resolveChildDeltas(final long pos, int type, byte[] data,
447 PackedObjectInfo oe) throws IOException {
448 final ArrayList<UnresolvedDelta> a = baseById.remove(oe);
449 final ArrayList<UnresolvedDelta> b = baseByPos.remove(pos);
450 int ai = 0, bi = 0;
451 if (a != null && b != null) {
452 while (ai < a.size() && bi < b.size()) {
453 final UnresolvedDelta ad = a.get(ai);
454 final UnresolvedDelta bd = b.get(bi);
455 if (ad.position < bd.position) {
456 resolveDeltas(ad.position, ad.crc, type, data, null);
457 ai++;
458 } else {
459 resolveDeltas(bd.position, bd.crc, type, data, null);
460 bi++;
464 if (a != null)
465 while (ai < a.size()) {
466 final UnresolvedDelta ad = a.get(ai++);
467 resolveDeltas(ad.position, ad.crc, type, data, null);
469 if (b != null)
470 while (bi < b.size()) {
471 final UnresolvedDelta bd = b.get(bi++);
472 resolveDeltas(bd.position, bd.crc, type, data, null);
476 private void fixThinPack(final ProgressMonitor progress) throws IOException {
477 growEntries();
479 packDigest.reset();
480 originalEOF = packOut.length() - 20;
481 final Deflater def = new Deflater(Deflater.DEFAULT_COMPRESSION, false);
482 long end = originalEOF;
483 for (final ObjectId baseId : new ArrayList<ObjectId>(baseById.keySet())) {
484 final ObjectLoader ldr = repo.openObject(readCurs, baseId);
485 if (ldr == null)
486 continue;
487 final byte[] data = ldr.getBytes();
488 final int typeCode = ldr.getType();
489 final PackedObjectInfo oe;
491 crc.reset();
492 packOut.seek(end);
493 writeWhole(def, typeCode, data);
494 oe = new PackedObjectInfo(end, (int) crc.getValue(), baseId);
495 entries[entryCount++] = oe;
496 end = packOut.getFilePointer();
498 resolveChildDeltas(oe.getOffset(), typeCode, data, oe);
499 if (progress.isCancelled())
500 throw new IOException("Download cancelled during indexing");
502 def.end();
504 if (!baseById.isEmpty()) {
505 final ObjectId need = baseById.keySet().iterator().next();
506 throw new MissingObjectException(need, "delta base");
509 fixHeaderFooter(packcsum, packDigest.digest());
512 private void writeWhole(final Deflater def, final int typeCode,
513 final byte[] data) throws IOException {
514 int sz = data.length;
515 int hdrlen = 0;
516 buf[hdrlen++] = (byte) ((typeCode << 4) | sz & 15);
517 sz >>>= 4;
518 while (sz > 0) {
519 buf[hdrlen - 1] |= 0x80;
520 buf[hdrlen++] = (byte) (sz & 0x7f);
521 sz >>>= 7;
523 packDigest.update(buf, 0, hdrlen);
524 crc.update(buf, 0, hdrlen);
525 packOut.write(buf, 0, hdrlen);
526 def.reset();
527 def.setInput(data);
528 def.finish();
529 while (!def.finished()) {
530 final int datlen = def.deflate(buf);
531 packDigest.update(buf, 0, datlen);
532 crc.update(buf, 0, datlen);
533 packOut.write(buf, 0, datlen);
537 private void fixHeaderFooter(final byte[] origcsum, final byte[] tailcsum)
538 throws IOException {
539 final MessageDigest origDigest = Constants.newMessageDigest();
540 final MessageDigest tailDigest = Constants.newMessageDigest();
541 long origRemaining = originalEOF;
543 packOut.seek(0);
544 bAvail = 0;
545 bOffset = 0;
546 fillFromFile(12);
549 final int origCnt = (int) Math.min(bAvail, origRemaining);
550 origDigest.update(buf, 0, origCnt);
551 origRemaining -= origCnt;
552 if (origRemaining == 0)
553 tailDigest.update(buf, origCnt, bAvail - origCnt);
556 NB.encodeInt32(buf, 8, entryCount);
557 packOut.seek(0);
558 packOut.write(buf, 0, 12);
559 packOut.seek(bAvail);
561 packDigest.reset();
562 packDigest.update(buf, 0, bAvail);
563 for (;;) {
564 final int n = packOut.read(buf);
565 if (n < 0)
566 break;
567 if (origRemaining != 0) {
568 final int origCnt = (int) Math.min(n, origRemaining);
569 origDigest.update(buf, 0, origCnt);
570 origRemaining -= origCnt;
571 if (origRemaining == 0)
572 tailDigest.update(buf, origCnt, n - origCnt);
573 } else
574 tailDigest.update(buf, 0, n);
576 packDigest.update(buf, 0, n);
579 if (!Arrays.equals(origDigest.digest(), origcsum)
580 || !Arrays.equals(tailDigest.digest(), tailcsum))
581 throw new IOException("Pack corrupted while writing to filesystem");
583 packcsum = packDigest.digest();
584 packOut.write(packcsum);
587 private void growEntries() {
588 final PackedObjectInfo[] ne;
590 ne = new PackedObjectInfo[(int) objectCount + baseById.size()];
591 System.arraycopy(entries, 0, ne, 0, entryCount);
592 entries = ne;
595 private void writeIdx() throws IOException {
596 Arrays.sort(entries, 0, entryCount);
597 List<PackedObjectInfo> list = Arrays.asList(entries);
598 if (entryCount < entries.length)
599 list = list.subList(0, entryCount);
601 final FileOutputStream os = new FileOutputStream(dstIdx);
602 try {
603 final PackIndexWriter iw;
604 if (outputVersion <= 0)
605 iw = PackIndexWriter.createOldestPossible(os, list);
606 else
607 iw = PackIndexWriter.createVersion(os, outputVersion);
608 iw.write(list, packcsum);
609 os.getChannel().force(true);
610 } finally {
611 os.close();
615 private void readPackHeader() throws IOException {
616 final int hdrln = Constants.PACK_SIGNATURE.length + 4 + 4;
617 final int p = fillFromInput(hdrln);
618 for (int k = 0; k < Constants.PACK_SIGNATURE.length; k++)
619 if (buf[p + k] != Constants.PACK_SIGNATURE[k])
620 throw new IOException("Not a PACK file.");
622 final long vers = NB.decodeUInt32(buf, p + 4);
623 if (vers != 2 && vers != 3)
624 throw new IOException("Unsupported pack version " + vers + ".");
625 objectCount = NB.decodeUInt32(buf, p + 8);
626 use(hdrln);
629 private void readPackFooter() throws IOException {
630 sync();
631 final byte[] cmpcsum = packDigest.digest();
632 final int c = fillFromInput(20);
633 packcsum = new byte[20];
634 System.arraycopy(buf, c, packcsum, 0, 20);
635 use(20);
636 if (packOut != null)
637 packOut.write(packcsum);
639 if (!Arrays.equals(cmpcsum, packcsum))
640 throw new CorruptObjectException("Packfile checksum incorrect.");
643 // Cleanup all resources associated with our input parsing.
644 private void endInput() {
645 in = null;
646 objectData = null;
649 // Read one entire object or delta from the input.
650 private void indexOneObject() throws IOException {
651 final long pos = position();
653 crc.reset();
654 int c = readFromInput();
655 final int typeCode = (c >> 4) & 7;
656 long sz = c & 15;
657 int shift = 4;
658 while ((c & 0x80) != 0) {
659 c = readFromInput();
660 sz += (c & 0x7f) << shift;
661 shift += 7;
664 switch (typeCode) {
665 case Constants.OBJ_COMMIT:
666 case Constants.OBJ_TREE:
667 case Constants.OBJ_BLOB:
668 case Constants.OBJ_TAG:
669 whole(typeCode, pos, sz);
670 break;
671 case Constants.OBJ_OFS_DELTA: {
672 c = readFromInput();
673 long ofs = c & 127;
674 while ((c & 128) != 0) {
675 ofs += 1;
676 c = readFromInput();
677 ofs <<= 7;
678 ofs += (c & 127);
680 final long base = pos - ofs;
681 ArrayList<UnresolvedDelta> r = baseByPos.get(base);
682 if (r == null) {
683 r = new ArrayList<UnresolvedDelta>(8);
684 baseByPos.put(base, r);
686 skipInflateFromInput(sz);
687 r.add(new UnresolvedDelta(pos, (int) crc.getValue()));
688 deltaCount++;
689 break;
691 case Constants.OBJ_REF_DELTA: {
692 c = fillFromInput(20);
693 crc.update(buf, c, 20);
694 final ObjectId base = ObjectId.fromRaw(buf, c);
695 use(20);
696 ArrayList<UnresolvedDelta> r = baseById.get(base);
697 if (r == null) {
698 r = new ArrayList<UnresolvedDelta>(8);
699 baseById.put(base, r);
701 skipInflateFromInput(sz);
702 r.add(new UnresolvedDelta(pos, (int) crc.getValue()));
703 deltaCount++;
704 break;
706 default:
707 throw new IOException("Unknown object type " + typeCode + ".");
711 private void whole(final int type, final long pos, final long sz)
712 throws IOException {
713 final byte[] data = inflateFromInput(sz);
714 objectDigest.update(Constants.encodedTypeString(type));
715 objectDigest.update((byte) ' ');
716 objectDigest.update(Constants.encodeASCII(sz));
717 objectDigest.update((byte) 0);
718 objectDigest.update(data);
719 tempObjectId.fromRaw(objectDigest.digest(), 0);
721 verifySafeObject(tempObjectId, type, data);
722 final int crc32 = (int) crc.getValue();
723 entries[entryCount++] = new PackedObjectInfo(pos, crc32, tempObjectId);
726 private void verifySafeObject(final AnyObjectId id, final int type,
727 final byte[] data) throws IOException {
728 if (objCheck != null) {
729 try {
730 objCheck.check(type, data);
731 } catch (CorruptObjectException e) {
732 throw new IOException("Invalid "
733 + Constants.encodedTypeString(type) + " " + id.name()
734 + ":" + e.getMessage());
738 final ObjectLoader ldr = repo.openObject(readCurs, id);
739 if (ldr != null) {
740 final byte[] existingData = ldr.getCachedBytes();
741 if (ldr.getType() != type || !Arrays.equals(data, existingData)) {
742 throw new IOException("Collision on " + id.name());
747 // Current position of {@link #bOffset} within the entire file.
748 private long position() {
749 return bBase + bOffset;
752 private void position(final long pos) throws IOException {
753 packOut.seek(pos);
754 bBase = pos;
755 bOffset = 0;
756 bAvail = 0;
759 // Consume exactly one byte from the buffer and return it.
760 private int readFromInput() throws IOException {
761 if (bAvail == 0)
762 fillFromInput(1);
763 bAvail--;
764 final int b = buf[bOffset++] & 0xff;
765 crc.update(b);
766 return b;
769 // Consume exactly one byte from the buffer and return it.
770 private int readFromFile() throws IOException {
771 if (bAvail == 0)
772 fillFromFile(1);
773 bAvail--;
774 final int b = buf[bOffset++] & 0xff;
775 crc.update(b);
776 return b;
779 // Consume cnt bytes from the buffer.
780 private void use(final int cnt) {
781 bOffset += cnt;
782 bAvail -= cnt;
785 // Ensure at least need bytes are available in in {@link #buf}.
786 private int fillFromInput(final int need) throws IOException {
787 while (bAvail < need) {
788 int next = bOffset + bAvail;
789 int free = buf.length - next;
790 if (free + bAvail < need) {
791 sync();
792 next = bAvail;
793 free = buf.length - next;
795 next = in.read(buf, next, free);
796 if (next <= 0)
797 throw new EOFException("Packfile is truncated.");
798 bAvail += next;
800 return bOffset;
803 // Ensure at least need bytes are available in in {@link #buf}.
804 private int fillFromFile(final int need) throws IOException {
805 if (bAvail < need) {
806 int next = bOffset + bAvail;
807 int free = buf.length - next;
808 if (free + bAvail < need) {
809 if (bAvail > 0)
810 System.arraycopy(buf, bOffset, buf, 0, bAvail);
811 bOffset = 0;
812 next = bAvail;
813 free = buf.length - next;
815 next = packOut.read(buf, next, free);
816 if (next <= 0)
817 throw new EOFException("Packfile is truncated.");
818 bAvail += next;
820 return bOffset;
823 // Store consumed bytes in {@link #buf} up to {@link #bOffset}.
824 private void sync() throws IOException {
825 packDigest.update(buf, 0, bOffset);
826 if (packOut != null)
827 packOut.write(buf, 0, bOffset);
828 if (bAvail > 0)
829 System.arraycopy(buf, bOffset, buf, 0, bAvail);
830 bBase += bOffset;
831 bOffset = 0;
834 private void skipInflateFromInput(long sz) throws IOException {
835 final Inflater inf = inflater;
836 try {
837 final byte[] dst = objectData;
838 int n = 0;
839 int p = -1;
840 while (!inf.finished()) {
841 if (inf.needsInput()) {
842 if (p >= 0) {
843 crc.update(buf, p, bAvail);
844 use(bAvail);
846 p = fillFromInput(1);
847 inf.setInput(buf, p, bAvail);
850 int free = dst.length - n;
851 if (free < 8) {
852 sz -= n;
853 n = 0;
854 free = dst.length;
856 n += inf.inflate(dst, n, free);
858 if (n != sz)
859 throw new DataFormatException("wrong decompressed length");
860 n = bAvail - inf.getRemaining();
861 if (n > 0) {
862 crc.update(buf, p, n);
863 use(n);
865 } catch (DataFormatException dfe) {
866 throw corrupt(dfe);
867 } finally {
868 inf.reset();
872 private byte[] inflateFromInput(final long sz) throws IOException {
873 final byte[] dst = new byte[(int) sz];
874 final Inflater inf = inflater;
875 try {
876 int n = 0;
877 int p = -1;
878 while (!inf.finished()) {
879 if (inf.needsInput()) {
880 if (p >= 0) {
881 crc.update(buf, p, bAvail);
882 use(bAvail);
884 p = fillFromInput(1);
885 inf.setInput(buf, p, bAvail);
888 n += inf.inflate(dst, n, dst.length - n);
890 if (n != sz)
891 throw new DataFormatException("wrong decompressed length");
892 n = bAvail - inf.getRemaining();
893 if (n > 0) {
894 crc.update(buf, p, n);
895 use(n);
897 return dst;
898 } catch (DataFormatException dfe) {
899 throw corrupt(dfe);
900 } finally {
901 inf.reset();
905 private byte[] inflateFromFile(final int sz) throws IOException {
906 final Inflater inf = inflater;
907 try {
908 final byte[] dst = new byte[sz];
909 int n = 0;
910 int p = -1;
911 while (!inf.finished()) {
912 if (inf.needsInput()) {
913 if (p >= 0) {
914 crc.update(buf, p, bAvail);
915 use(bAvail);
917 p = fillFromFile(1);
918 inf.setInput(buf, p, bAvail);
920 n += inf.inflate(dst, n, sz - n);
922 n = bAvail - inf.getRemaining();
923 if (n > 0) {
924 crc.update(buf, p, n);
925 use(n);
927 return dst;
928 } catch (DataFormatException dfe) {
929 throw corrupt(dfe);
930 } finally {
931 inf.reset();
935 private static CorruptObjectException corrupt(final DataFormatException dfe) {
936 return new CorruptObjectException("Packfile corruption detected: "
937 + dfe.getMessage());
940 private static class UnresolvedDelta {
941 final long position;
943 final int crc;
945 UnresolvedDelta(final long headerOffset, final int crc32) {
946 position = headerOffset;
947 crc = crc32;
952 * Rename the pack to it's final name and location and open it.
953 * <p>
954 * If the call completes successfully the repository this IndexPack instance
955 * was created with will have the objects in the pack available for reading
956 * and use, without needing to scan for packs.
958 * @throws IOException
959 * The pack could not be inserted into the repository's objects
960 * directory. The pack no longer exists on disk, as it was
961 * removed prior to throwing the exception to the caller.
963 public void renameAndOpenPack() throws IOException {
964 if (!keepEmpty && entryCount == 0) {
965 cleanupTemporaryFiles();
966 return;
969 final MessageDigest d = Constants.newMessageDigest();
970 final byte[] oeBytes = new byte[Constants.OBJECT_ID_LENGTH];
971 for (int i = 0; i < entryCount; i++) {
972 final PackedObjectInfo oe = entries[i];
973 oe.copyRawTo(oeBytes, 0);
974 d.update(oeBytes);
977 final String name = ObjectId.fromRaw(d.digest()).name();
978 final File packDir = new File(repo.getObjectsDirectory(), "pack");
979 final File finalPack = new File(packDir, "pack-" + name + ".pack");
980 final File finalIdx = new File(packDir, "pack-" + name + ".idx");
982 if (finalPack.exists()) {
983 // If the pack is already present we should never replace it.
985 cleanupTemporaryFiles();
986 return;
989 if (!dstPack.renameTo(finalPack)) {
990 cleanupTemporaryFiles();
991 throw new IOException("Cannot move pack to " + finalPack);
994 if (!dstIdx.renameTo(finalIdx)) {
995 cleanupTemporaryFiles();
996 if (!finalPack.delete())
997 finalPack.deleteOnExit();
998 throw new IOException("Cannot move index to " + finalIdx);
1001 try {
1002 repo.openPack(finalPack, finalIdx);
1003 } catch (IOException err) {
1004 finalPack.delete();
1005 finalIdx.delete();
1006 throw err;
1010 private void cleanupTemporaryFiles() {
1011 if (!dstIdx.delete())
1012 dstIdx.deleteOnExit();
1013 if (!dstPack.delete())
1014 dstPack.deleteOnExit();