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