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