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