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