jgit: Switch usage of AnyObjectId.toString() to new AnyObjectId.name()
[egit/charleso.git] / org.spearce.jgit / src / org / spearce / jgit / transport / IndexPack.java
blob2c98823f8822db3d531c28d27e7492161f245b3d
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.HashMap;
51 import java.util.List;
52 import java.util.zip.CRC32;
53 import java.util.zip.DataFormatException;
54 import java.util.zip.Deflater;
55 import java.util.zip.Inflater;
57 import org.spearce.jgit.errors.CorruptObjectException;
58 import org.spearce.jgit.errors.MissingObjectException;
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.ObjectId;
64 import org.spearce.jgit.lib.ObjectIdMap;
65 import org.spearce.jgit.lib.ObjectLoader;
66 import org.spearce.jgit.lib.PackIndexWriter;
67 import org.spearce.jgit.lib.ProgressMonitor;
68 import org.spearce.jgit.lib.Repository;
69 import org.spearce.jgit.util.NB;
71 /** Indexes Git pack files for local use. */
72 public class IndexPack {
73 /** Progress message when reading raw data from the pack. */
74 public static final String PROGRESS_DOWNLOAD = "Receiving objects";
76 /** Progress message when computing names of delta compressed objects. */
77 public static final String PROGRESS_RESOLVE_DELTA = "Resolving deltas";
79 private static final int BUFFER_SIZE = 4096;
81 /**
82 * Create an index pack instance to load a new pack into a repository.
83 * <p>
84 * The received pack data and generated index will be saved to temporary
85 * files within the repository's <code>objects</code> directory. To use
86 * the data contained within them call {@link #renameAndOpenPack()} once the
87 * indexing is complete.
89 * @param db
90 * the repository that will receive the new pack.
91 * @param is
92 * stream to read the pack data from.
93 * @return a new index pack instance.
94 * @throws IOException
95 * a temporary file could not be created.
97 public static IndexPack create(final Repository db, final InputStream is)
98 throws IOException {
99 final String suffix = ".pack";
100 final File objdir = db.getObjectsDirectory();
101 final File tmp = File.createTempFile("incoming_", suffix, objdir);
102 final String n = tmp.getName();
103 final File base;
105 base = new File(objdir, n.substring(0, n.length() - suffix.length()));
106 final IndexPack ip = new IndexPack(db, is, base);
107 ip.setIndexVersion(db.getConfig().getCore().getPackIndexVersion());
108 return ip;
111 private final Repository repo;
113 private Inflater inflater;
115 private final MessageDigest objectDigest;
117 private final MutableObjectId tempObjectId;
119 private InputStream in;
121 private byte[] buf;
123 private long bBase;
125 private int bOffset;
127 private int bAvail;
129 private boolean fixThin;
131 private int outputVersion;
133 private final File dstPack;
135 private final File dstIdx;
137 private long objectCount;
139 private PackedObjectInfo[] entries;
141 private int deltaCount;
143 private int entryCount;
145 private final CRC32 crc = new CRC32();
147 private ObjectIdMap<ArrayList<UnresolvedDelta>> baseById;
149 private HashMap<Long, ArrayList<UnresolvedDelta>> baseByPos;
151 private byte[] objectData;
153 private MessageDigest packDigest;
155 private RandomAccessFile packOut;
157 private byte[] packcsum;
159 /** If {@link #fixThin} this is the last byte of the original checksum. */
160 private long originalEOF;
163 * Create a new pack indexer utility.
165 * @param db
166 * @param src
167 * @param dstBase
168 * @throws IOException
169 * the output packfile could not be created.
171 public IndexPack(final Repository db, final InputStream src,
172 final File dstBase) throws IOException {
173 repo = db;
174 in = src;
175 inflater = InflaterCache.get();
176 buf = new byte[BUFFER_SIZE];
177 objectData = new byte[BUFFER_SIZE];
178 objectDigest = Constants.newMessageDigest();
179 tempObjectId = new MutableObjectId();
180 packDigest = Constants.newMessageDigest();
182 if (dstBase != null) {
183 final File dir = dstBase.getParentFile();
184 final String nam = dstBase.getName();
185 dstPack = new File(dir, nam + ".pack");
186 dstIdx = new File(dir, nam + ".idx");
187 packOut = new RandomAccessFile(dstPack, "rw");
188 packOut.setLength(0);
189 } else {
190 dstPack = null;
191 dstIdx = null;
196 * Set the pack index file format version this instance will create.
198 * @param version
199 * the version to write. The special version 0 designates the
200 * oldest (most compatible) format available for the objects.
201 * @see PackIndexWriter
203 public void setIndexVersion(final int version) {
204 outputVersion = version;
208 * Configure this index pack instance to make a thin pack complete.
209 * <p>
210 * Thin packs are sometimes used during network transfers to allow a delta
211 * to be sent without a base object. Such packs are not permitted on disk.
212 * They can be fixed by copying the base object onto the end of the pack.
214 * @param fix
215 * true to enable fixing a thin pack.
217 public void setFixThin(final boolean fix) {
218 fixThin = fix;
222 * Consume data from the input stream until the packfile is indexed.
224 * @param progress
225 * progress feedback
227 * @throws IOException
229 public void index(final ProgressMonitor progress) throws IOException {
230 progress.start(2 /* tasks */);
231 try {
232 try {
233 readPackHeader();
235 entries = new PackedObjectInfo[(int) objectCount];
236 baseById = new ObjectIdMap<ArrayList<UnresolvedDelta>>();
237 baseByPos = new HashMap<Long, ArrayList<UnresolvedDelta>>();
239 progress.beginTask(PROGRESS_DOWNLOAD, (int) objectCount);
240 for (int done = 0; done < objectCount; done++) {
241 indexOneObject();
242 progress.update(1);
243 if (progress.isCancelled())
244 throw new IOException("Download cancelled");
246 readPackFooter();
247 endInput();
248 progress.endTask();
249 if (deltaCount > 0) {
250 if (packOut == null)
251 throw new IOException("need packOut");
252 resolveDeltas(progress);
253 if (entryCount < objectCount) {
254 if (!fixThin) {
255 throw new IOException("pack has "
256 + (objectCount - entryCount)
257 + " unresolved deltas");
259 fixThinPack(progress);
262 if (packOut != null)
263 packOut.getChannel().force(true);
265 packDigest = null;
266 baseById = null;
267 baseByPos = null;
269 if (dstIdx != null)
270 writeIdx();
272 } finally {
273 final Inflater inf = inflater;
274 inflater = null;
275 InflaterCache.release(inf);
277 progress.endTask();
278 if (packOut != null)
279 packOut.close();
282 if (dstPack != null)
283 dstPack.setReadOnly();
284 if (dstIdx != null)
285 dstIdx.setReadOnly();
286 } catch (IOException err) {
287 if (dstPack != null)
288 dstPack.delete();
289 if (dstIdx != null)
290 dstIdx.delete();
291 throw err;
295 private void resolveDeltas(final ProgressMonitor progress)
296 throws IOException {
297 progress.beginTask(PROGRESS_RESOLVE_DELTA, deltaCount);
298 final int last = entryCount;
299 for (int i = 0; i < last; i++) {
300 final int before = entryCount;
301 resolveDeltas(entries[i]);
302 progress.update(entryCount - before);
303 if (progress.isCancelled())
304 throw new IOException("Download cancelled during indexing");
306 progress.endTask();
309 private void resolveDeltas(final PackedObjectInfo oe) throws IOException {
310 final int oldCRC = oe.getCRC();
311 if (baseById.containsKey(oe)
312 || baseByPos.containsKey(new Long(oe.getOffset())))
313 resolveDeltas(oe.getOffset(), oldCRC, Constants.OBJ_BAD, null, oe);
316 private void resolveDeltas(final long pos, final int oldCRC, int type,
317 byte[] data, PackedObjectInfo oe) throws IOException {
318 crc.reset();
319 position(pos);
320 int c = readFromFile();
321 final int typeCode = (c >> 4) & 7;
322 long sz = c & 15;
323 int shift = 4;
324 while ((c & 0x80) != 0) {
325 c = readFromFile();
326 sz += (c & 0x7f) << shift;
327 shift += 7;
330 switch (typeCode) {
331 case Constants.OBJ_COMMIT:
332 case Constants.OBJ_TREE:
333 case Constants.OBJ_BLOB:
334 case Constants.OBJ_TAG:
335 type = typeCode;
336 data = inflateFromFile((int) sz);
337 break;
338 case Constants.OBJ_OFS_DELTA: {
339 c = readFromFile() & 0xff;
340 while ((c & 128) != 0)
341 c = readFromFile() & 0xff;
342 data = BinaryDelta.apply(data, inflateFromFile((int) sz));
343 break;
345 case Constants.OBJ_REF_DELTA: {
346 crc.update(buf, fillFromFile(20), 20);
347 use(20);
348 data = BinaryDelta.apply(data, inflateFromFile((int) sz));
349 break;
351 default:
352 throw new IOException("Unknown object type " + typeCode + ".");
355 final int crc32 = (int) crc.getValue();
356 if (oldCRC != crc32)
357 throw new IOException("Corruption detected re-reading at " + pos);
358 if (oe == null) {
359 objectDigest.update(Constants.encodedTypeString(type));
360 objectDigest.update((byte) ' ');
361 objectDigest.update(Constants.encodeASCII(data.length));
362 objectDigest.update((byte) 0);
363 objectDigest.update(data);
364 tempObjectId.fromRaw(objectDigest.digest(), 0);
366 oe = new PackedObjectInfo(pos, crc32, tempObjectId);
367 entries[entryCount++] = oe;
370 resolveChildDeltas(pos, type, data, oe);
373 private void resolveChildDeltas(final long pos, int type, byte[] data,
374 PackedObjectInfo oe) throws IOException {
375 final ArrayList<UnresolvedDelta> a = baseById.remove(oe);
376 final ArrayList<UnresolvedDelta> b = baseByPos.remove(new Long(pos));
377 int ai = 0, bi = 0;
378 if (a != null && b != null) {
379 while (ai < a.size() && bi < b.size()) {
380 final UnresolvedDelta ad = a.get(ai);
381 final UnresolvedDelta bd = b.get(bi);
382 if (ad.position < bd.position) {
383 resolveDeltas(ad.position, ad.crc, type, data, null);
384 ai++;
385 } else {
386 resolveDeltas(bd.position, bd.crc, type, data, null);
387 bi++;
391 if (a != null)
392 while (ai < a.size()) {
393 final UnresolvedDelta ad = a.get(ai++);
394 resolveDeltas(ad.position, ad.crc, type, data, null);
396 if (b != null)
397 while (bi < b.size()) {
398 final UnresolvedDelta bd = b.get(bi++);
399 resolveDeltas(bd.position, bd.crc, type, data, null);
403 private void fixThinPack(final ProgressMonitor progress) throws IOException {
404 growEntries();
406 packDigest.reset();
407 originalEOF = packOut.length() - 20;
408 final Deflater def = new Deflater(Deflater.DEFAULT_COMPRESSION, false);
409 long end = originalEOF;
410 for (final ObjectId baseId : new ArrayList<ObjectId>(baseById.keySet())) {
411 final ObjectLoader ldr = repo.openObject(baseId);
412 if (ldr == null)
413 continue;
414 final byte[] data = ldr.getBytes();
415 final int typeCode = ldr.getType();
416 final PackedObjectInfo oe;
418 crc.reset();
419 packOut.seek(end);
420 writeWhole(def, typeCode, data);
421 oe = new PackedObjectInfo(end, (int) crc.getValue(), baseId);
422 entries[entryCount++] = oe;
423 end = packOut.getFilePointer();
425 resolveChildDeltas(oe.getOffset(), typeCode, data, oe);
426 if (progress.isCancelled())
427 throw new IOException("Download cancelled during indexing");
429 def.end();
431 if (!baseById.isEmpty()) {
432 final ObjectId need = baseById.keySet().iterator().next();
433 throw new MissingObjectException(need, "delta base");
436 fixHeaderFooter(packcsum, packDigest.digest());
439 private void writeWhole(final Deflater def, final int typeCode,
440 final byte[] data) throws IOException {
441 int sz = data.length;
442 int hdrlen = 0;
443 buf[hdrlen++] = (byte) ((typeCode << 4) | sz & 15);
444 sz >>>= 4;
445 while (sz > 0) {
446 buf[hdrlen - 1] |= 0x80;
447 buf[hdrlen++] = (byte) (sz & 0x7f);
448 sz >>>= 7;
450 packDigest.update(buf, 0, hdrlen);
451 crc.update(buf, 0, hdrlen);
452 packOut.write(buf, 0, hdrlen);
453 def.reset();
454 def.setInput(data);
455 def.finish();
456 while (!def.finished()) {
457 final int datlen = def.deflate(buf);
458 packDigest.update(buf, 0, datlen);
459 crc.update(buf, 0, datlen);
460 packOut.write(buf, 0, datlen);
464 private void fixHeaderFooter(final byte[] origcsum, final byte[] tailcsum)
465 throws IOException {
466 final MessageDigest origDigest = Constants.newMessageDigest();
467 final MessageDigest tailDigest = Constants.newMessageDigest();
468 long origRemaining = originalEOF;
470 packOut.seek(0);
471 bAvail = 0;
472 bOffset = 0;
473 fillFromFile(12);
476 final int origCnt = (int) Math.min(bAvail, origRemaining);
477 origDigest.update(buf, 0, origCnt);
478 origRemaining -= origCnt;
479 if (origRemaining == 0)
480 tailDigest.update(buf, origCnt, bAvail - origCnt);
483 NB.encodeInt32(buf, 8, entryCount);
484 packOut.seek(0);
485 packOut.write(buf, 0, 12);
486 packOut.seek(bAvail);
488 packDigest.reset();
489 packDigest.update(buf, 0, bAvail);
490 for (;;) {
491 final int n = packOut.read(buf);
492 if (n < 0)
493 break;
494 if (origRemaining != 0) {
495 final int origCnt = (int) Math.min(n, origRemaining);
496 origDigest.update(buf, 0, origCnt);
497 origRemaining -= origCnt;
498 if (origRemaining == 0)
499 tailDigest.update(buf, origCnt, n - origCnt);
500 } else
501 tailDigest.update(buf, 0, n);
503 packDigest.update(buf, 0, n);
506 if (!Arrays.equals(origDigest.digest(), origcsum)
507 || !Arrays.equals(tailDigest.digest(), tailcsum))
508 throw new IOException("Pack corrupted while writing to filesystem");
510 packcsum = packDigest.digest();
511 packOut.write(packcsum);
514 private void growEntries() {
515 final PackedObjectInfo[] ne;
517 ne = new PackedObjectInfo[(int) objectCount + baseById.size()];
518 System.arraycopy(entries, 0, ne, 0, entryCount);
519 entries = ne;
522 private void writeIdx() throws IOException {
523 Arrays.sort(entries, 0, entryCount);
524 List<PackedObjectInfo> list = Arrays.asList(entries);
525 if (entryCount < entries.length)
526 list = list.subList(0, entryCount);
528 final FileOutputStream os = new FileOutputStream(dstIdx);
529 try {
530 final PackIndexWriter iw;
531 if (outputVersion <= 0)
532 iw = PackIndexWriter.createOldestPossible(os, list);
533 else
534 iw = PackIndexWriter.createVersion(os, outputVersion);
535 iw.write(list, packcsum);
536 os.getChannel().force(true);
537 } finally {
538 os.close();
542 private void readPackHeader() throws IOException {
543 final int hdrln = Constants.PACK_SIGNATURE.length + 4 + 4;
544 final int p = fillFromInput(hdrln);
545 for (int k = 0; k < Constants.PACK_SIGNATURE.length; k++)
546 if (buf[p + k] != Constants.PACK_SIGNATURE[k])
547 throw new IOException("Not a PACK file.");
549 final long vers = NB.decodeUInt32(buf, p + 4);
550 if (vers != 2 && vers != 3)
551 throw new IOException("Unsupported pack version " + vers + ".");
552 objectCount = NB.decodeUInt32(buf, p + 8);
553 use(hdrln);
556 private void readPackFooter() throws IOException {
557 sync();
558 final byte[] cmpcsum = packDigest.digest();
559 final int c = fillFromInput(20);
560 packcsum = new byte[20];
561 System.arraycopy(buf, c, packcsum, 0, 20);
562 use(20);
563 if (packOut != null)
564 packOut.write(packcsum);
566 if (!Arrays.equals(cmpcsum, packcsum))
567 throw new CorruptObjectException("Packfile checksum incorrect.");
570 // Cleanup all resources associated with our input parsing.
571 private void endInput() {
572 in = null;
573 objectData = null;
576 // Read one entire object or delta from the input.
577 private void indexOneObject() throws IOException {
578 final long pos = position();
580 crc.reset();
581 int c = readFromInput();
582 final int typeCode = (c >> 4) & 7;
583 long sz = c & 15;
584 int shift = 4;
585 while ((c & 0x80) != 0) {
586 c = readFromInput();
587 sz += (c & 0x7f) << shift;
588 shift += 7;
591 switch (typeCode) {
592 case Constants.OBJ_COMMIT:
593 case Constants.OBJ_TREE:
594 case Constants.OBJ_BLOB:
595 case Constants.OBJ_TAG:
596 whole(typeCode, pos, sz);
597 break;
598 case Constants.OBJ_OFS_DELTA: {
599 c = readFromInput();
600 long ofs = c & 127;
601 while ((c & 128) != 0) {
602 ofs += 1;
603 c = readFromInput();
604 ofs <<= 7;
605 ofs += (c & 127);
607 final Long base = new Long(pos - ofs);
608 ArrayList<UnresolvedDelta> r = baseByPos.get(base);
609 if (r == null) {
610 r = new ArrayList<UnresolvedDelta>(8);
611 baseByPos.put(base, r);
613 inflateFromInput(false);
614 r.add(new UnresolvedDelta(pos, (int) crc.getValue()));
615 deltaCount++;
616 break;
618 case Constants.OBJ_REF_DELTA: {
619 c = fillFromInput(20);
620 crc.update(buf, c, 20);
621 final ObjectId base = ObjectId.fromRaw(buf, c);
622 use(20);
623 ArrayList<UnresolvedDelta> r = baseById.get(base);
624 if (r == null) {
625 r = new ArrayList<UnresolvedDelta>(8);
626 baseById.put(base, r);
628 inflateFromInput(false);
629 r.add(new UnresolvedDelta(pos, (int) crc.getValue()));
630 deltaCount++;
631 break;
633 default:
634 throw new IOException("Unknown object type " + typeCode + ".");
638 private void whole(final int type, final long pos, final long sz)
639 throws IOException {
640 objectDigest.update(Constants.encodedTypeString(type));
641 objectDigest.update((byte) ' ');
642 objectDigest.update(Constants.encodeASCII(sz));
643 objectDigest.update((byte) 0);
644 inflateFromInput(true);
645 tempObjectId.fromRaw(objectDigest.digest(), 0);
647 final int crc32 = (int) crc.getValue();
648 entries[entryCount++] = new PackedObjectInfo(pos, crc32, tempObjectId);
651 // Current position of {@link #bOffset} within the entire file.
652 private long position() {
653 return bBase + bOffset;
656 private void position(final long pos) throws IOException {
657 packOut.seek(pos);
658 bBase = pos;
659 bOffset = 0;
660 bAvail = 0;
663 // Consume exactly one byte from the buffer and return it.
664 private int readFromInput() throws IOException {
665 if (bAvail == 0)
666 fillFromInput(1);
667 bAvail--;
668 final int b = buf[bOffset++] & 0xff;
669 crc.update(b);
670 return b;
673 // Consume exactly one byte from the buffer and return it.
674 private int readFromFile() throws IOException {
675 if (bAvail == 0)
676 fillFromFile(1);
677 bAvail--;
678 final int b = buf[bOffset++] & 0xff;
679 crc.update(b);
680 return b;
683 // Consume cnt bytes from the buffer.
684 private void use(final int cnt) {
685 bOffset += cnt;
686 bAvail -= cnt;
689 // Ensure at least need bytes are available in in {@link #buf}.
690 private int fillFromInput(final int need) throws IOException {
691 while (bAvail < need) {
692 int next = bOffset + bAvail;
693 int free = buf.length - next;
694 if (free + bAvail < need) {
695 sync();
696 next = bAvail;
697 free = buf.length - next;
699 next = in.read(buf, next, free);
700 if (next <= 0)
701 throw new EOFException("Packfile is truncated.");
702 bAvail += next;
704 return bOffset;
707 // Ensure at least need bytes are available in in {@link #buf}.
708 private int fillFromFile(final int need) throws IOException {
709 if (bAvail < need) {
710 int next = bOffset + bAvail;
711 int free = buf.length - next;
712 if (free + bAvail < need) {
713 if (bAvail > 0)
714 System.arraycopy(buf, bOffset, buf, 0, bAvail);
715 bOffset = 0;
716 next = bAvail;
717 free = buf.length - next;
719 next = packOut.read(buf, next, free);
720 if (next <= 0)
721 throw new EOFException("Packfile is truncated.");
722 bAvail += next;
724 return bOffset;
727 // Store consumed bytes in {@link #buf} up to {@link #bOffset}.
728 private void sync() throws IOException {
729 packDigest.update(buf, 0, bOffset);
730 if (packOut != null)
731 packOut.write(buf, 0, bOffset);
732 if (bAvail > 0)
733 System.arraycopy(buf, bOffset, buf, 0, bAvail);
734 bBase += bOffset;
735 bOffset = 0;
738 private void inflateFromInput(final boolean digest) throws IOException {
739 final Inflater inf = inflater;
740 try {
741 final byte[] dst = objectData;
742 int n = 0;
743 int p = -1;
744 while (!inf.finished()) {
745 if (inf.needsInput()) {
746 if (p >= 0) {
747 crc.update(buf, p, bAvail);
748 use(bAvail);
750 p = fillFromInput(1);
751 inf.setInput(buf, p, bAvail);
754 int free = dst.length - n;
755 if (free < 8) {
756 if (digest)
757 objectDigest.update(dst, 0, n);
758 n = 0;
759 free = dst.length;
762 n += inf.inflate(dst, n, free);
764 if (digest)
765 objectDigest.update(dst, 0, n);
766 n = bAvail - inf.getRemaining();
767 if (n > 0) {
768 crc.update(buf, p, n);
769 use(n);
771 } catch (DataFormatException dfe) {
772 throw corrupt(dfe);
773 } finally {
774 inf.reset();
778 private byte[] inflateFromFile(final int sz) throws IOException {
779 final Inflater inf = inflater;
780 try {
781 final byte[] dst = new byte[sz];
782 int n = 0;
783 int p = -1;
784 while (!inf.finished()) {
785 if (inf.needsInput()) {
786 if (p >= 0) {
787 crc.update(buf, p, bAvail);
788 use(bAvail);
790 p = fillFromFile(1);
791 inf.setInput(buf, p, bAvail);
793 n += inf.inflate(dst, n, sz - n);
795 n = bAvail - inf.getRemaining();
796 if (n > 0) {
797 crc.update(buf, p, n);
798 use(n);
800 return dst;
801 } catch (DataFormatException dfe) {
802 throw corrupt(dfe);
803 } finally {
804 inf.reset();
808 private static CorruptObjectException corrupt(final DataFormatException dfe) {
809 return new CorruptObjectException("Packfile corruption detected: "
810 + dfe.getMessage());
813 private static class UnresolvedDelta {
814 final long position;
816 final int crc;
818 UnresolvedDelta(final long headerOffset, final int crc32) {
819 position = headerOffset;
820 crc = crc32;
825 * Rename the pack to it's final name and location and open it.
826 * <p>
827 * If the call completes successfully the repository this IndexPack instance
828 * was created with will have the objects in the pack available for reading
829 * and use, without needing to scan for packs.
831 * @throws IOException
832 * The pack could not be inserted into the repository's objects
833 * directory. The pack no longer exists on disk, as it was
834 * removed prior to throwing the exception to the caller.
836 public void renameAndOpenPack() throws IOException {
837 final MessageDigest d = Constants.newMessageDigest();
838 final byte[] oeBytes = new byte[Constants.OBJECT_ID_LENGTH];
839 for (int i = 0; i < entryCount; i++) {
840 final PackedObjectInfo oe = entries[i];
841 oe.copyRawTo(oeBytes, 0);
842 d.update(oeBytes);
845 final String name = ObjectId.fromRaw(d.digest()).name();
846 final File packDir = new File(repo.getObjectsDirectory(), "pack");
847 final File finalPack = new File(packDir, "pack-" + name + ".pack");
848 final File finalIdx = new File(packDir, "pack-" + name + ".idx");
850 if (finalPack.exists()) {
851 // If the pack is already present we should never replace it.
853 cleanupTemporaryFiles();
854 return;
857 if (!dstPack.renameTo(finalPack)) {
858 cleanupTemporaryFiles();
859 throw new IOException("Cannot move pack to " + finalPack);
862 if (!dstIdx.renameTo(finalIdx)) {
863 cleanupTemporaryFiles();
864 if (!finalPack.delete())
865 finalPack.deleteOnExit();
866 throw new IOException("Cannot move index to " + finalIdx);
869 try {
870 repo.openPack(finalPack, finalIdx);
871 } catch (IOException err) {
872 finalPack.delete();
873 finalIdx.delete();
874 throw err;
878 private void cleanupTemporaryFiles() {
879 if (!dstIdx.delete())
880 dstIdx.deleteOnExit();
881 if (!dstPack.delete())
882 dstPack.deleteOnExit();