1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package org.eclipse.jgit.internal.storage.file;
47
48 import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
49 import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
50
51 import java.io.EOFException;
52 import java.io.File;
53 import java.io.IOException;
54 import java.io.InterruptedIOException;
55 import java.io.RandomAccessFile;
56 import java.nio.MappedByteBuffer;
57 import java.nio.channels.FileChannel.MapMode;
58 import java.text.MessageFormat;
59 import java.util.Arrays;
60 import java.util.Collections;
61 import java.util.Comparator;
62 import java.util.Iterator;
63 import java.util.Set;
64 import java.util.zip.CRC32;
65 import java.util.zip.DataFormatException;
66 import java.util.zip.Inflater;
67
68 import org.eclipse.jgit.errors.CorruptObjectException;
69 import org.eclipse.jgit.errors.LargeObjectException;
70 import org.eclipse.jgit.errors.MissingObjectException;
71 import org.eclipse.jgit.errors.PackInvalidException;
72 import org.eclipse.jgit.errors.PackMismatchException;
73 import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
74 import org.eclipse.jgit.internal.JGitText;
75 import org.eclipse.jgit.internal.storage.pack.BinaryDelta;
76 import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
77 import org.eclipse.jgit.internal.storage.pack.PackExt;
78 import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
79 import org.eclipse.jgit.lib.AbbreviatedObjectId;
80 import org.eclipse.jgit.lib.AnyObjectId;
81 import org.eclipse.jgit.lib.Constants;
82 import org.eclipse.jgit.lib.ObjectId;
83 import org.eclipse.jgit.lib.ObjectLoader;
84 import org.eclipse.jgit.util.LongList;
85 import org.eclipse.jgit.util.NB;
86 import org.eclipse.jgit.util.RawParseUtils;
87
88
89
90
91
92
93 public class PackFile implements Iterable<PackIndex.MutableEntry> {
94
95 public static final Comparator<PackFile> SORT = new Comparator<PackFile>() {
96 public int compare(final PackFile a, final PackFile b) {
97 return b.packLastModified - a.packLastModified;
98 }
99 };
100
101 private final File packFile;
102
103 private final int extensions;
104
105 private File keepFile;
106
107 private volatile String packName;
108
109 final int hash;
110
111 private RandomAccessFile fd;
112
113
114 private final Object readLock = new Object();
115
116 long length;
117
118 private int activeWindows;
119
120 private int activeCopyRawData;
121
122 private int packLastModified;
123
124 private volatile boolean invalid;
125
126 private boolean invalidBitmap;
127
128 private byte[] packChecksum;
129
130 private PackIndex loadedIdx;
131
132 private PackReverseIndex reverseIdx;
133
134 private PackBitmapIndex bitmapIdx;
135
136
137
138
139
140
141
142
143 private volatile LongList corruptObjects;
144
145
146
147
148
149
150
151
152
153 public PackFile(final File packFile, int extensions) {
154 this.packFile = packFile;
155 this.packLastModified = (int) (packFile.lastModified() >> 10);
156 this.extensions = extensions;
157
158
159
160
161 hash = System.identityHashCode(this) * 31;
162 length = Long.MAX_VALUE;
163 }
164
165 private synchronized PackIndex idx() throws IOException {
166 if (loadedIdx == null) {
167 if (invalid)
168 throw new PackInvalidException(packFile);
169
170 try {
171 final PackIndex idx = PackIndex.open(extFile(INDEX));
172
173 if (packChecksum == null) {
174 packChecksum = idx.packChecksum;
175 } else if (!Arrays.equals(packChecksum, idx.packChecksum)) {
176 throw new PackMismatchException(MessageFormat.format(
177 JGitText.get().packChecksumMismatch,
178 packFile.getPath()));
179 }
180 loadedIdx = idx;
181 } catch (InterruptedIOException e) {
182
183 throw e;
184 } catch (IOException e) {
185 invalid = true;
186 throw e;
187 }
188 }
189 return loadedIdx;
190 }
191
192
193 public File getPackFile() {
194 return packFile;
195 }
196
197
198
199
200
201 public PackIndex getIndex() throws IOException {
202 return idx();
203 }
204
205
206 public String getPackName() {
207 String name = packName;
208 if (name == null) {
209 name = getPackFile().getName();
210 if (name.startsWith("pack-"))
211 name = name.substring("pack-".length());
212 if (name.endsWith(".pack"))
213 name = name.substring(0, name.length() - ".pack".length());
214 packName = name;
215 }
216 return name;
217 }
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232 public boolean hasObject(final AnyObjectId id) throws IOException {
233 final long offset = idx().findOffset(id);
234 return 0 < offset && !isCorrupt(offset);
235 }
236
237
238
239
240
241
242 public boolean shouldBeKept() {
243 if (keepFile == null)
244 keepFile = new File(packFile.getPath() + ".keep");
245 return keepFile.exists();
246 }
247
248
249
250
251
252
253
254
255
256
257
258
259
260 ObjectLoader get(final WindowCursor curs, final AnyObjectId id)
261 throws IOException {
262 final long offset = idx().findOffset(id);
263 return 0 < offset && !isCorrupt(offset) ? load(curs, offset) : null;
264 }
265
266 void resolve(Set<ObjectId> matches, AbbreviatedObjectId id, int matchLimit)
267 throws IOException {
268 idx().resolve(matches, id, matchLimit);
269 }
270
271
272
273
274 public void close() {
275 WindowCache.purge(this);
276 synchronized (this) {
277 loadedIdx = null;
278 reverseIdx = null;
279 }
280 }
281
282
283
284
285
286
287
288
289
290
291
292
293
294 public Iterator<PackIndex.MutableEntry> iterator() {
295 try {
296 return idx().iterator();
297 } catch (IOException e) {
298 return Collections.<PackIndex.MutableEntry> emptyList().iterator();
299 }
300 }
301
302
303
304
305
306
307
308
309
310 long getObjectCount() throws IOException {
311 return idx().getObjectCount();
312 }
313
314
315
316
317
318
319
320
321
322
323
324 ObjectId findObjectForOffset(final long offset) throws IOException {
325 return getReverseIdx().findObject(offset);
326 }
327
328 private final byte[] decompress(final long position, final int sz,
329 final WindowCursor curs) throws IOException, DataFormatException {
330 byte[] dstbuf;
331 try {
332 dstbuf = new byte[sz];
333 } catch (OutOfMemoryError noMemory) {
334
335
336
337
338
339
340
341 return null;
342 }
343
344 if (curs.inflate(this, position, dstbuf, false) != sz)
345 throw new EOFException(MessageFormat.format(
346 JGitText.get().shortCompressedStreamAt,
347 Long.valueOf(position)));
348 return dstbuf;
349 }
350
351 void copyPackAsIs(PackOutputStream out, WindowCursor curs)
352 throws IOException {
353
354 curs.pin(this, 0);
355 curs.copyPackAsIs(this, length, out);
356 }
357
358 final void copyAsIs(PackOutputStream out, LocalObjectToPack src,
359 boolean validate, WindowCursor curs) throws IOException,
360 StoredObjectRepresentationNotAvailableException {
361 beginCopyAsIs(src);
362 try {
363 copyAsIs2(out, src, validate, curs);
364 } finally {
365 endCopyAsIs();
366 }
367 }
368
369 private void copyAsIs2(PackOutputStream out, LocalObjectToPack src,
370 boolean validate, WindowCursor curs) throws IOException,
371 StoredObjectRepresentationNotAvailableException {
372 final CRC32 crc1 = validate ? new CRC32() : null;
373 final CRC32 crc2 = validate ? new CRC32() : null;
374 final byte[] buf = out.getCopyBuffer();
375
376
377
378 readFully(src.offset, buf, 0, 20, curs);
379 int c = buf[0] & 0xff;
380 final int typeCode = (c >> 4) & 7;
381 long inflatedLength = c & 15;
382 int shift = 4;
383 int headerCnt = 1;
384 while ((c & 0x80) != 0) {
385 c = buf[headerCnt++] & 0xff;
386 inflatedLength += ((long) (c & 0x7f)) << shift;
387 shift += 7;
388 }
389
390 if (typeCode == Constants.OBJ_OFS_DELTA) {
391 do {
392 c = buf[headerCnt++] & 0xff;
393 } while ((c & 128) != 0);
394 if (validate) {
395 assert(crc1 != null && crc2 != null);
396 crc1.update(buf, 0, headerCnt);
397 crc2.update(buf, 0, headerCnt);
398 }
399 } else if (typeCode == Constants.OBJ_REF_DELTA) {
400 if (validate) {
401 assert(crc1 != null && crc2 != null);
402 crc1.update(buf, 0, headerCnt);
403 crc2.update(buf, 0, headerCnt);
404 }
405
406 readFully(src.offset + headerCnt, buf, 0, 20, curs);
407 if (validate) {
408 assert(crc1 != null && crc2 != null);
409 crc1.update(buf, 0, 20);
410 crc2.update(buf, 0, 20);
411 }
412 headerCnt += 20;
413 } else if (validate) {
414 assert(crc1 != null && crc2 != null);
415 crc1.update(buf, 0, headerCnt);
416 crc2.update(buf, 0, headerCnt);
417 }
418
419 final long dataOffset = src.offset + headerCnt;
420 final long dataLength = src.length;
421 final long expectedCRC;
422 final ByteArrayWindow quickCopy;
423
424
425
426
427 try {
428 quickCopy = curs.quickCopy(this, dataOffset, dataLength);
429
430 if (validate && idx().hasCRC32Support()) {
431 assert(crc1 != null);
432
433
434 expectedCRC = idx().findCRC32(src);
435 if (quickCopy != null) {
436 quickCopy.crc32(crc1, dataOffset, (int) dataLength);
437 } else {
438 long pos = dataOffset;
439 long cnt = dataLength;
440 while (cnt > 0) {
441 final int n = (int) Math.min(cnt, buf.length);
442 readFully(pos, buf, 0, n, curs);
443 crc1.update(buf, 0, n);
444 pos += n;
445 cnt -= n;
446 }
447 }
448 if (crc1.getValue() != expectedCRC) {
449 setCorrupt(src.offset);
450 throw new CorruptObjectException(MessageFormat.format(
451 JGitText.get().objectAtHasBadZlibStream,
452 Long.valueOf(src.offset), getPackFile()));
453 }
454 } else if (validate) {
455
456
457
458
459 Inflater inf = curs.inflater();
460 byte[] tmp = new byte[1024];
461 if (quickCopy != null) {
462 quickCopy.check(inf, tmp, dataOffset, (int) dataLength);
463 } else {
464 assert(crc1 != null);
465 long pos = dataOffset;
466 long cnt = dataLength;
467 while (cnt > 0) {
468 final int n = (int) Math.min(cnt, buf.length);
469 readFully(pos, buf, 0, n, curs);
470 crc1.update(buf, 0, n);
471 inf.setInput(buf, 0, n);
472 while (inf.inflate(tmp, 0, tmp.length) > 0)
473 continue;
474 pos += n;
475 cnt -= n;
476 }
477 }
478 if (!inf.finished() || inf.getBytesRead() != dataLength) {
479 setCorrupt(src.offset);
480 throw new EOFException(MessageFormat.format(
481 JGitText.get().shortCompressedStreamAt,
482 Long.valueOf(src.offset)));
483 }
484 assert(crc1 != null);
485 expectedCRC = crc1.getValue();
486 } else {
487 expectedCRC = -1;
488 }
489 } catch (DataFormatException dataFormat) {
490 setCorrupt(src.offset);
491
492 CorruptObjectException corruptObject = new CorruptObjectException(
493 MessageFormat.format(
494 JGitText.get().objectAtHasBadZlibStream,
495 Long.valueOf(src.offset), getPackFile()));
496 corruptObject.initCause(dataFormat);
497
498 StoredObjectRepresentationNotAvailableException gone;
499 gone = new StoredObjectRepresentationNotAvailableException(src);
500 gone.initCause(corruptObject);
501 throw gone;
502
503 } catch (IOException ioError) {
504 StoredObjectRepresentationNotAvailableException gone;
505 gone = new StoredObjectRepresentationNotAvailableException(src);
506 gone.initCause(ioError);
507 throw gone;
508 }
509
510 if (quickCopy != null) {
511
512
513
514 out.writeHeader(src, inflatedLength);
515 quickCopy.write(out, dataOffset, (int) dataLength);
516
517 } else if (dataLength <= buf.length) {
518
519
520
521 if (!validate) {
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, curs);
527 pos += n;
528 cnt -= n;
529 }
530 }
531 out.writeHeader(src, inflatedLength);
532 out.write(buf, 0, (int) dataLength);
533 } else {
534
535
536
537
538 out.writeHeader(src, inflatedLength);
539 long pos = dataOffset;
540 long cnt = dataLength;
541 while (cnt > 0) {
542 final int n = (int) Math.min(cnt, buf.length);
543 readFully(pos, buf, 0, n, curs);
544 if (validate) {
545 assert(crc2 != null);
546 crc2.update(buf, 0, n);
547 }
548 out.write(buf, 0, n);
549 pos += n;
550 cnt -= n;
551 }
552 if (validate) {
553 assert(crc2 != null);
554 if (crc2.getValue() != expectedCRC) {
555 throw new CorruptObjectException(MessageFormat.format(
556 JGitText.get().objectAtHasBadZlibStream,
557 Long.valueOf(src.offset), getPackFile()));
558 }
559 }
560 }
561 }
562
563 boolean invalid() {
564 return invalid;
565 }
566
567 void setInvalid() {
568 invalid = true;
569 }
570
571 private void readFully(final long position, final byte[] dstbuf,
572 int dstoff, final int cnt, final WindowCursor curs)
573 throws IOException {
574 if (curs.copy(this, position, dstbuf, dstoff, cnt) != cnt)
575 throw new EOFException();
576 }
577
578 private synchronized void beginCopyAsIs(ObjectToPack otp)
579 throws StoredObjectRepresentationNotAvailableException {
580 if (++activeCopyRawData == 1 && activeWindows == 0) {
581 try {
582 doOpen();
583 } catch (IOException thisPackNotValid) {
584 StoredObjectRepresentationNotAvailableException gone;
585
586 gone = new StoredObjectRepresentationNotAvailableException(otp);
587 gone.initCause(thisPackNotValid);
588 throw gone;
589 }
590 }
591 }
592
593 private synchronized void endCopyAsIs() {
594 if (--activeCopyRawData == 0 && activeWindows == 0)
595 doClose();
596 }
597
598 synchronized boolean beginWindowCache() throws IOException {
599 if (++activeWindows == 1) {
600 if (activeCopyRawData == 0)
601 doOpen();
602 return true;
603 }
604 return false;
605 }
606
607 synchronized boolean endWindowCache() {
608 final boolean r = --activeWindows == 0;
609 if (r && activeCopyRawData == 0)
610 doClose();
611 return r;
612 }
613
614 private void doOpen() throws IOException {
615 try {
616 if (invalid)
617 throw new PackInvalidException(packFile);
618 synchronized (readLock) {
619 fd = new RandomAccessFile(packFile, "r");
620 length = fd.length();
621 onOpenPack();
622 }
623 } catch (InterruptedIOException e) {
624
625 openFail(false);
626 throw e;
627 } catch (IOException ioe) {
628 openFail(true);
629 throw ioe;
630 } catch (RuntimeException re) {
631 openFail(true);
632 throw re;
633 } catch (Error re) {
634 openFail(true);
635 throw re;
636 }
637 }
638
639 private void openFail(boolean invalidate) {
640 activeWindows = 0;
641 activeCopyRawData = 0;
642 invalid = invalidate;
643 doClose();
644 }
645
646 private void doClose() {
647 synchronized (readLock) {
648 if (fd != null) {
649 try {
650 fd.close();
651 } catch (IOException err) {
652
653
654
655 }
656 fd = null;
657 }
658 }
659 }
660
661 ByteArrayWindow read(final long pos, int size) throws IOException {
662 synchronized (readLock) {
663 if (length < pos + size)
664 size = (int) (length - pos);
665 final byte[] buf = new byte[size];
666 fd.seek(pos);
667 fd.readFully(buf, 0, size);
668 return new ByteArrayWindow(this, pos, buf);
669 }
670 }
671
672 ByteWindow mmap(final long pos, int size) throws IOException {
673 synchronized (readLock) {
674 if (length < pos + size)
675 size = (int) (length - pos);
676
677 MappedByteBuffer map;
678 try {
679 map = fd.getChannel().map(MapMode.READ_ONLY, pos, size);
680 } catch (IOException ioe1) {
681
682
683
684
685 System.gc();
686 System.runFinalization();
687 map = fd.getChannel().map(MapMode.READ_ONLY, pos, size);
688 }
689
690 if (map.hasArray())
691 return new ByteArrayWindow(this, pos, map.array());
692 return new ByteBufferWindow(this, pos, map);
693 }
694 }
695
696 private void onOpenPack() throws IOException {
697 final PackIndex idx = idx();
698 final byte[] buf = new byte[20];
699
700 fd.seek(0);
701 fd.readFully(buf, 0, 12);
702 if (RawParseUtils.match(buf, 0, Constants.PACK_SIGNATURE) != 4)
703 throw new IOException(JGitText.get().notAPACKFile);
704 final long vers = NB.decodeUInt32(buf, 4);
705 final long packCnt = NB.decodeUInt32(buf, 8);
706 if (vers != 2 && vers != 3)
707 throw new IOException(MessageFormat.format(
708 JGitText.get().unsupportedPackVersion, Long.valueOf(vers)));
709
710 if (packCnt != idx.getObjectCount())
711 throw new PackMismatchException(MessageFormat.format(
712 JGitText.get().packObjectCountMismatch,
713 Long.valueOf(packCnt), Long.valueOf(idx.getObjectCount()),
714 getPackFile()));
715
716 fd.seek(length - 20);
717 fd.readFully(buf, 0, 20);
718 if (!Arrays.equals(buf, packChecksum))
719 throw new PackMismatchException(MessageFormat.format(
720 JGitText.get().packObjectCountMismatch
721 , ObjectId.fromRaw(buf).name()
722 , ObjectId.fromRaw(idx.packChecksum).name()
723 , getPackFile()));
724 }
725
726 ObjectLoader load(final WindowCursor curs, long pos)
727 throws IOException, LargeObjectException {
728 try {
729 final byte[] ib = curs.tempId;
730 Delta delta = null;
731 byte[] data = null;
732 int type = Constants.OBJ_BAD;
733 boolean cached = false;
734
735 SEARCH: for (;;) {
736 readFully(pos, ib, 0, 20, curs);
737 int c = ib[0] & 0xff;
738 final int typeCode = (c >> 4) & 7;
739 long sz = c & 15;
740 int shift = 4;
741 int p = 1;
742 while ((c & 0x80) != 0) {
743 c = ib[p++] & 0xff;
744 sz += ((long) (c & 0x7f)) << shift;
745 shift += 7;
746 }
747
748 switch (typeCode) {
749 case Constants.OBJ_COMMIT:
750 case Constants.OBJ_TREE:
751 case Constants.OBJ_BLOB:
752 case Constants.OBJ_TAG: {
753 if (delta != null || sz < curs.getStreamFileThreshold())
754 data = decompress(pos + p, (int) sz, curs);
755
756 if (delta != null) {
757 type = typeCode;
758 break SEARCH;
759 }
760
761 if (data != null)
762 return new ObjectLoader.SmallObject(typeCode, data);
763 else
764 return new LargePackedWholeObject(typeCode, sz, pos, p,
765 this, curs.db);
766 }
767
768 case Constants.OBJ_OFS_DELTA: {
769 c = ib[p++] & 0xff;
770 long base = c & 127;
771 while ((c & 128) != 0) {
772 base += 1;
773 c = ib[p++] & 0xff;
774 base <<= 7;
775 base += (c & 127);
776 }
777 base = pos - base;
778 delta = new Delta(delta, pos, (int) sz, p, base);
779 if (sz != delta.deltaSize)
780 break SEARCH;
781
782 DeltaBaseCache.Entry e = curs.getDeltaBaseCache().get(this, base);
783 if (e != null) {
784 type = e.type;
785 data = e.data;
786 cached = true;
787 break SEARCH;
788 }
789 pos = base;
790 continue SEARCH;
791 }
792
793 case Constants.OBJ_REF_DELTA: {
794 readFully(pos + p, ib, 0, 20, curs);
795 long base = findDeltaBase(ObjectId.fromRaw(ib));
796 delta = new Delta(delta, pos, (int) sz, p + 20, base);
797 if (sz != delta.deltaSize)
798 break SEARCH;
799
800 DeltaBaseCache.Entry e = curs.getDeltaBaseCache().get(this, base);
801 if (e != null) {
802 type = e.type;
803 data = e.data;
804 cached = true;
805 break SEARCH;
806 }
807 pos = base;
808 continue SEARCH;
809 }
810
811 default:
812 throw new IOException(MessageFormat.format(
813 JGitText.get().unknownObjectType,
814 Integer.valueOf(typeCode)));
815 }
816 }
817
818
819
820
821 if (data == null)
822 throw new IOException(JGitText.get().inMemoryBufferLimitExceeded);
823
824 assert(delta != null);
825 do {
826
827 if (cached)
828 cached = false;
829 else if (delta.next == null)
830 curs.getDeltaBaseCache().store(this, delta.basePos, data, type);
831
832 pos = delta.deltaPos;
833
834 final byte[] cmds = decompress(pos + delta.hdrLen,
835 delta.deltaSize, curs);
836 if (cmds == null) {
837 data = null;
838 throw new LargeObjectException.OutOfMemory(new OutOfMemoryError());
839 }
840
841 final long sz = BinaryDelta.getResultSize(cmds);
842 if (Integer.MAX_VALUE <= sz)
843 throw new LargeObjectException.ExceedsByteArrayLimit();
844
845 final byte[] result;
846 try {
847 result = new byte[(int) sz];
848 } catch (OutOfMemoryError tooBig) {
849 data = null;
850 throw new LargeObjectException.OutOfMemory(tooBig);
851 }
852
853 BinaryDelta.apply(data, cmds, result);
854 data = result;
855 delta = delta.next;
856 } while (delta != null);
857
858 return new ObjectLoader.SmallObject(type, data);
859
860 } catch (DataFormatException dfe) {
861 CorruptObjectException coe = new CorruptObjectException(
862 MessageFormat.format(
863 JGitText.get().objectAtHasBadZlibStream,
864 Long.valueOf(pos), getPackFile()));
865 coe.initCause(dfe);
866 throw coe;
867 }
868 }
869
870 private long findDeltaBase(ObjectId baseId) throws IOException,
871 MissingObjectException {
872 long ofs = idx().findOffset(baseId);
873 if (ofs < 0)
874 throw new MissingObjectException(baseId,
875 JGitText.get().missingDeltaBase);
876 return ofs;
877 }
878
879 private static class Delta {
880
881 final Delta next;
882
883
884 final long deltaPos;
885
886
887 final int deltaSize;
888
889
890 final int hdrLen;
891
892
893 final long basePos;
894
895 Delta(Delta next, long ofs, int sz, int hdrLen, long baseOffset) {
896 this.next = next;
897 this.deltaPos = ofs;
898 this.deltaSize = sz;
899 this.hdrLen = hdrLen;
900 this.basePos = baseOffset;
901 }
902 }
903
904 byte[] getDeltaHeader(WindowCursor wc, long pos)
905 throws IOException, DataFormatException {
906
907
908
909
910
911 final byte[] hdr = new byte[18];
912 wc.inflate(this, pos, hdr, true );
913 return hdr;
914 }
915
916 int getObjectType(final WindowCursor curs, long pos) throws IOException {
917 final byte[] ib = curs.tempId;
918 for (;;) {
919 readFully(pos, ib, 0, 20, curs);
920 int c = ib[0] & 0xff;
921 final int type = (c >> 4) & 7;
922
923 switch (type) {
924 case Constants.OBJ_COMMIT:
925 case Constants.OBJ_TREE:
926 case Constants.OBJ_BLOB:
927 case Constants.OBJ_TAG:
928 return type;
929
930 case Constants.OBJ_OFS_DELTA: {
931 int p = 1;
932 while ((c & 0x80) != 0)
933 c = ib[p++] & 0xff;
934 c = ib[p++] & 0xff;
935 long ofs = c & 127;
936 while ((c & 128) != 0) {
937 ofs += 1;
938 c = ib[p++] & 0xff;
939 ofs <<= 7;
940 ofs += (c & 127);
941 }
942 pos = pos - ofs;
943 continue;
944 }
945
946 case Constants.OBJ_REF_DELTA: {
947 int p = 1;
948 while ((c & 0x80) != 0)
949 c = ib[p++] & 0xff;
950 readFully(pos + p, ib, 0, 20, curs);
951 pos = findDeltaBase(ObjectId.fromRaw(ib));
952 continue;
953 }
954
955 default:
956 throw new IOException(
957 MessageFormat.format(JGitText.get().unknownObjectType,
958 Integer.valueOf(type)));
959 }
960 }
961 }
962
963 long getObjectSize(final WindowCursor curs, final AnyObjectId id)
964 throws IOException {
965 final long offset = idx().findOffset(id);
966 return 0 < offset ? getObjectSize(curs, offset) : -1;
967 }
968
969 long getObjectSize(final WindowCursor curs, final long pos)
970 throws IOException {
971 final byte[] ib = curs.tempId;
972 readFully(pos, ib, 0, 20, curs);
973 int c = ib[0] & 0xff;
974 final int type = (c >> 4) & 7;
975 long sz = c & 15;
976 int shift = 4;
977 int p = 1;
978 while ((c & 0x80) != 0) {
979 c = ib[p++] & 0xff;
980 sz += ((long) (c & 0x7f)) << shift;
981 shift += 7;
982 }
983
984 long deltaAt;
985 switch (type) {
986 case Constants.OBJ_COMMIT:
987 case Constants.OBJ_TREE:
988 case Constants.OBJ_BLOB:
989 case Constants.OBJ_TAG:
990 return sz;
991
992 case Constants.OBJ_OFS_DELTA:
993 c = ib[p++] & 0xff;
994 while ((c & 128) != 0)
995 c = ib[p++] & 0xff;
996 deltaAt = pos + p;
997 break;
998
999 case Constants.OBJ_REF_DELTA:
1000 deltaAt = pos + p + 20;
1001 break;
1002
1003 default:
1004 throw new IOException(MessageFormat.format(
1005 JGitText.get().unknownObjectType, Integer.valueOf(type)));
1006 }
1007
1008 try {
1009 return BinaryDelta.getResultSize(getDeltaHeader(curs, deltaAt));
1010 } catch (DataFormatException e) {
1011 throw new CorruptObjectException(MessageFormat.format(
1012 JGitText.get().objectAtHasBadZlibStream, Long.valueOf(pos),
1013 getPackFile()));
1014 }
1015 }
1016
1017 LocalObjectRepresentation representation(final WindowCursor curs,
1018 final AnyObjectId objectId) throws IOException {
1019 final long pos = idx().findOffset(objectId);
1020 if (pos < 0)
1021 return null;
1022
1023 final byte[] ib = curs.tempId;
1024 readFully(pos, ib, 0, 20, curs);
1025 int c = ib[0] & 0xff;
1026 int p = 1;
1027 final int typeCode = (c >> 4) & 7;
1028 while ((c & 0x80) != 0)
1029 c = ib[p++] & 0xff;
1030
1031 long len = (findEndOffset(pos) - pos);
1032 switch (typeCode) {
1033 case Constants.OBJ_COMMIT:
1034 case Constants.OBJ_TREE:
1035 case Constants.OBJ_BLOB:
1036 case Constants.OBJ_TAG:
1037 return LocalObjectRepresentation.newWhole(this, pos, len - p);
1038
1039 case Constants.OBJ_OFS_DELTA: {
1040 c = ib[p++] & 0xff;
1041 long ofs = c & 127;
1042 while ((c & 128) != 0) {
1043 ofs += 1;
1044 c = ib[p++] & 0xff;
1045 ofs <<= 7;
1046 ofs += (c & 127);
1047 }
1048 ofs = pos - ofs;
1049 return LocalObjectRepresentation.newDelta(this, pos, len - p, ofs);
1050 }
1051
1052 case Constants.OBJ_REF_DELTA: {
1053 len -= p;
1054 len -= Constants.OBJECT_ID_LENGTH;
1055 readFully(pos + p, ib, 0, 20, curs);
1056 ObjectId id = ObjectId.fromRaw(ib);
1057 return LocalObjectRepresentation.newDelta(this, pos, len, id);
1058 }
1059
1060 default:
1061 throw new IOException(
1062 MessageFormat.format(JGitText.get().unknownObjectType,
1063 Integer.valueOf(typeCode)));
1064 }
1065 }
1066
1067 private long findEndOffset(final long startOffset)
1068 throws IOException, CorruptObjectException {
1069 final long maxOffset = length - 20;
1070 return getReverseIdx().findNextOffset(startOffset, maxOffset);
1071 }
1072
1073 synchronized PackBitmapIndex getBitmapIndex() throws IOException {
1074 if (invalid || invalidBitmap)
1075 return null;
1076 if (bitmapIdx == null && hasExt(BITMAP_INDEX)) {
1077 final PackBitmapIndex idx = PackBitmapIndex.open(
1078 extFile(BITMAP_INDEX), idx(), getReverseIdx());
1079
1080
1081 if (Arrays.equals(packChecksum, idx.packChecksum))
1082 bitmapIdx = idx;
1083 else
1084 invalidBitmap = true;
1085 }
1086 return bitmapIdx;
1087 }
1088
1089 private synchronized PackReverseIndex getReverseIdx() throws IOException {
1090 if (reverseIdx == null)
1091 reverseIdx = new PackReverseIndex(idx());
1092 return reverseIdx;
1093 }
1094
1095 private boolean isCorrupt(long offset) {
1096 LongList list = corruptObjects;
1097 if (list == null)
1098 return false;
1099 synchronized (list) {
1100 return list.contains(offset);
1101 }
1102 }
1103
1104 private void setCorrupt(long offset) {
1105 LongList list = corruptObjects;
1106 if (list == null) {
1107 synchronized (readLock) {
1108 list = corruptObjects;
1109 if (list == null) {
1110 list = new LongList();
1111 corruptObjects = list;
1112 }
1113 }
1114 }
1115 synchronized (list) {
1116 list.add(offset);
1117 }
1118 }
1119
1120 private File extFile(PackExt ext) {
1121 String p = packFile.getName();
1122 int dot = p.lastIndexOf('.');
1123 String b = (dot < 0) ? p : p.substring(0, dot);
1124 return new File(packFile.getParentFile(), b + '.' + ext.getExtension());
1125 }
1126
1127 private boolean hasExt(PackExt ext) {
1128 return (extensions & ext.getBit()) != 0;
1129 }
1130 }