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