You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PackFile.java 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794
  1. /*
  2. * Copyright (C) 2008-2009, Google Inc.
  3. * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
  4. * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
  5. * and other copyright owners as documented in the project's IP log.
  6. *
  7. * This program and the accompanying materials are made available
  8. * under the terms of the Eclipse Distribution License v1.0 which
  9. * accompanies this distribution, is reproduced below, and is
  10. * available at http://www.eclipse.org/org/documents/edl-v10.php
  11. *
  12. * All rights reserved.
  13. *
  14. * Redistribution and use in source and binary forms, with or
  15. * without modification, are permitted provided that the following
  16. * conditions are met:
  17. *
  18. * - Redistributions of source code must retain the above copyright
  19. * notice, this list of conditions and the following disclaimer.
  20. *
  21. * - Redistributions in binary form must reproduce the above
  22. * copyright notice, this list of conditions and the following
  23. * disclaimer in the documentation and/or other materials provided
  24. * with the distribution.
  25. *
  26. * - Neither the name of the Eclipse Foundation, Inc. nor the
  27. * names of its contributors may be used to endorse or promote
  28. * products derived from this software without specific prior
  29. * written permission.
  30. *
  31. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  32. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  33. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  34. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  35. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  36. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  37. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  38. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  39. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  40. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  41. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  42. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  43. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  44. */
  45. package org.eclipse.jgit.storage.file;
  46. import java.io.EOFException;
  47. import java.io.File;
  48. import java.io.IOException;
  49. import java.io.RandomAccessFile;
  50. import java.nio.MappedByteBuffer;
  51. import java.nio.channels.FileChannel.MapMode;
  52. import java.text.MessageFormat;
  53. import java.util.Arrays;
  54. import java.util.Collections;
  55. import java.util.Comparator;
  56. import java.util.Iterator;
  57. import java.util.zip.CRC32;
  58. import java.util.zip.DataFormatException;
  59. import java.util.zip.Inflater;
  60. import org.eclipse.jgit.JGitText;
  61. import org.eclipse.jgit.errors.CorruptObjectException;
  62. import org.eclipse.jgit.errors.MissingObjectException;
  63. import org.eclipse.jgit.errors.PackInvalidException;
  64. import org.eclipse.jgit.errors.PackMismatchException;
  65. import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
  66. import org.eclipse.jgit.lib.AnyObjectId;
  67. import org.eclipse.jgit.lib.Constants;
  68. import org.eclipse.jgit.lib.ObjectId;
  69. import org.eclipse.jgit.storage.pack.BinaryDelta;
  70. import org.eclipse.jgit.storage.pack.ObjectToPack;
  71. import org.eclipse.jgit.storage.pack.PackOutputStream;
  72. import org.eclipse.jgit.util.LongList;
  73. import org.eclipse.jgit.util.NB;
  74. import org.eclipse.jgit.util.RawParseUtils;
  75. /**
  76. * A Git version 2 pack file representation. A pack file contains Git objects in
  77. * delta packed format yielding high compression of lots of object where some
  78. * objects are similar.
  79. */
  80. public class PackFile implements Iterable<PackIndex.MutableEntry> {
  81. /** Sorts PackFiles to be most recently created to least recently created. */
  82. public static Comparator<PackFile> SORT = new Comparator<PackFile>() {
  83. public int compare(final PackFile a, final PackFile b) {
  84. return b.packLastModified - a.packLastModified;
  85. }
  86. };
  87. private final File idxFile;
  88. private final File packFile;
  89. final int hash;
  90. private RandomAccessFile fd;
  91. /** Serializes reads performed against {@link #fd}. */
  92. private final Object readLock = new Object();
  93. long length;
  94. private int activeWindows;
  95. private int activeCopyRawData;
  96. private int packLastModified;
  97. private volatile boolean invalid;
  98. private byte[] packChecksum;
  99. private PackIndex loadedIdx;
  100. private PackReverseIndex reverseIdx;
  101. /**
  102. * Objects we have tried to read, and discovered to be corrupt.
  103. * <p>
  104. * The list is allocated after the first corruption is found, and filled in
  105. * as more entries are discovered. Typically this list is never used, as
  106. * pack files do not usually contain corrupt objects.
  107. */
  108. private volatile LongList corruptObjects;
  109. /**
  110. * Construct a reader for an existing, pre-indexed packfile.
  111. *
  112. * @param idxFile
  113. * path of the <code>.idx</code> file listing the contents.
  114. * @param packFile
  115. * path of the <code>.pack</code> file holding the data.
  116. */
  117. public PackFile(final File idxFile, final File packFile) {
  118. this.idxFile = idxFile;
  119. this.packFile = packFile;
  120. this.packLastModified = (int) (packFile.lastModified() >> 10);
  121. // Multiply by 31 here so we can more directly combine with another
  122. // value in WindowCache.hash(), without doing the multiply there.
  123. //
  124. hash = System.identityHashCode(this) * 31;
  125. length = Long.MAX_VALUE;
  126. }
  127. private synchronized PackIndex idx() throws IOException {
  128. if (loadedIdx == null) {
  129. if (invalid)
  130. throw new PackInvalidException(packFile);
  131. try {
  132. final PackIndex idx = PackIndex.open(idxFile);
  133. if (packChecksum == null)
  134. packChecksum = idx.packChecksum;
  135. else if (!Arrays.equals(packChecksum, idx.packChecksum))
  136. throw new PackMismatchException(JGitText.get().packChecksumMismatch);
  137. loadedIdx = idx;
  138. } catch (IOException e) {
  139. invalid = true;
  140. throw e;
  141. }
  142. }
  143. return loadedIdx;
  144. }
  145. final PackedObjectLoader resolveBase(final WindowCursor curs, final long ofs)
  146. throws IOException {
  147. if (isCorrupt(ofs)) {
  148. throw new CorruptObjectException(MessageFormat.format(JGitText
  149. .get().objectAtHasBadZlibStream, ofs, getPackFile()));
  150. }
  151. return load(curs, ofs);
  152. }
  153. /** @return the File object which locates this pack on disk. */
  154. public File getPackFile() {
  155. return packFile;
  156. }
  157. /**
  158. * Determine if an object is contained within the pack file.
  159. * <p>
  160. * For performance reasons only the index file is searched; the main pack
  161. * content is ignored entirely.
  162. * </p>
  163. *
  164. * @param id
  165. * the object to look for. Must not be null.
  166. * @return true if the object is in this pack; false otherwise.
  167. * @throws IOException
  168. * the index file cannot be loaded into memory.
  169. */
  170. public boolean hasObject(final AnyObjectId id) throws IOException {
  171. final long offset = idx().findOffset(id);
  172. return 0 < offset && !isCorrupt(offset);
  173. }
  174. /**
  175. * Get an object from this pack.
  176. *
  177. * @param curs
  178. * temporary working space associated with the calling thread.
  179. * @param id
  180. * the object to obtain from the pack. Must not be null.
  181. * @return the object loader for the requested object if it is contained in
  182. * this pack; null if the object was not found.
  183. * @throws IOException
  184. * the pack file or the index could not be read.
  185. */
  186. PackedObjectLoader get(final WindowCursor curs, final AnyObjectId id)
  187. throws IOException {
  188. final long offset = idx().findOffset(id);
  189. return 0 < offset && !isCorrupt(offset) ? load(curs, offset) : null;
  190. }
  191. /**
  192. * Close the resources utilized by this repository
  193. */
  194. public void close() {
  195. UnpackedObjectCache.purge(this);
  196. WindowCache.purge(this);
  197. synchronized (this) {
  198. loadedIdx = null;
  199. reverseIdx = null;
  200. }
  201. }
  202. /**
  203. * Provide iterator over entries in associated pack index, that should also
  204. * exist in this pack file. Objects returned by such iterator are mutable
  205. * during iteration.
  206. * <p>
  207. * Iterator returns objects in SHA-1 lexicographical order.
  208. * </p>
  209. *
  210. * @return iterator over entries of associated pack index
  211. *
  212. * @see PackIndex#iterator()
  213. */
  214. public Iterator<PackIndex.MutableEntry> iterator() {
  215. try {
  216. return idx().iterator();
  217. } catch (IOException e) {
  218. return Collections.<PackIndex.MutableEntry> emptyList().iterator();
  219. }
  220. }
  221. /**
  222. * Obtain the total number of objects available in this pack. This method
  223. * relies on pack index, giving number of effectively available objects.
  224. *
  225. * @return number of objects in index of this pack, likewise in this pack
  226. * @throws IOException
  227. * the index file cannot be loaded into memory.
  228. */
  229. long getObjectCount() throws IOException {
  230. return idx().getObjectCount();
  231. }
  232. /**
  233. * Search for object id with the specified start offset in associated pack
  234. * (reverse) index.
  235. *
  236. * @param offset
  237. * start offset of object to find
  238. * @return object id for this offset, or null if no object was found
  239. * @throws IOException
  240. * the index file cannot be loaded into memory.
  241. */
  242. ObjectId findObjectForOffset(final long offset) throws IOException {
  243. return getReverseIdx().findObject(offset);
  244. }
  245. private final UnpackedObjectCache.Entry readCache(final long position) {
  246. return UnpackedObjectCache.get(this, position);
  247. }
  248. private final void saveCache(final long position, final byte[] data, final int type) {
  249. UnpackedObjectCache.store(this, position, data, type);
  250. }
  251. private final byte[] decompress(final long position, final long totalSize,
  252. final WindowCursor curs) throws IOException, DataFormatException {
  253. if (totalSize > Integer.MAX_VALUE)
  254. throw new OutOfMemoryError();
  255. final byte[] dstbuf = new byte[(int) totalSize];
  256. if (curs.inflate(this, position, dstbuf, 0) != totalSize)
  257. throw new EOFException(MessageFormat.format(JGitText.get().shortCompressedStreamAt, position));
  258. return dstbuf;
  259. }
  260. final void copyAsIs(PackOutputStream out, LocalObjectToPack src,
  261. WindowCursor curs) throws IOException,
  262. StoredObjectRepresentationNotAvailableException {
  263. beginCopyAsIs(src);
  264. try {
  265. copyAsIs2(out, src, curs);
  266. } finally {
  267. endCopyAsIs();
  268. }
  269. }
  270. private void copyAsIs2(PackOutputStream out, LocalObjectToPack src,
  271. WindowCursor curs) throws IOException,
  272. StoredObjectRepresentationNotAvailableException {
  273. final CRC32 crc1 = new CRC32();
  274. final CRC32 crc2 = new CRC32();
  275. final byte[] buf = out.getCopyBuffer();
  276. // Rip apart the header so we can discover the size.
  277. //
  278. readFully(src.offset, buf, 0, 20, curs);
  279. int c = buf[0] & 0xff;
  280. final int typeCode = (c >> 4) & 7;
  281. long inflatedLength = c & 15;
  282. int shift = 4;
  283. int headerCnt = 1;
  284. while ((c & 0x80) != 0) {
  285. c = buf[headerCnt++] & 0xff;
  286. inflatedLength += (c & 0x7f) << shift;
  287. shift += 7;
  288. }
  289. if (typeCode == Constants.OBJ_OFS_DELTA) {
  290. do {
  291. c = buf[headerCnt++] & 0xff;
  292. } while ((c & 128) != 0);
  293. crc1.update(buf, 0, headerCnt);
  294. crc2.update(buf, 0, headerCnt);
  295. } else if (typeCode == Constants.OBJ_REF_DELTA) {
  296. crc1.update(buf, 0, headerCnt);
  297. crc2.update(buf, 0, headerCnt);
  298. readFully(src.offset + headerCnt, buf, 0, 20, curs);
  299. crc1.update(buf, 0, 20);
  300. crc2.update(buf, 0, headerCnt);
  301. headerCnt += 20;
  302. } else {
  303. crc1.update(buf, 0, headerCnt);
  304. crc2.update(buf, 0, headerCnt);
  305. }
  306. final long dataOffset = src.offset + headerCnt;
  307. final long dataLength = src.length;
  308. final long expectedCRC;
  309. final ByteArrayWindow quickCopy;
  310. // Verify the object isn't corrupt before sending. If it is,
  311. // we report it missing instead.
  312. //
  313. try {
  314. quickCopy = curs.quickCopy(this, dataOffset, dataLength);
  315. if (idx().hasCRC32Support()) {
  316. // Index has the CRC32 code cached, validate the object.
  317. //
  318. expectedCRC = idx().findCRC32(src);
  319. if (quickCopy != null) {
  320. quickCopy.crc32(crc1, dataOffset, (int) dataLength);
  321. } else {
  322. long pos = dataOffset;
  323. long cnt = dataLength;
  324. while (cnt > 0) {
  325. final int n = (int) Math.min(cnt, buf.length);
  326. readFully(pos, buf, 0, n, curs);
  327. crc1.update(buf, 0, n);
  328. pos += n;
  329. cnt -= n;
  330. }
  331. }
  332. if (crc1.getValue() != expectedCRC) {
  333. setCorrupt(src.offset);
  334. throw new CorruptObjectException(MessageFormat.format(
  335. JGitText.get().objectAtHasBadZlibStream,
  336. src.offset, getPackFile()));
  337. }
  338. } else {
  339. // We don't have a CRC32 code in the index, so compute it
  340. // now while inflating the raw data to get zlib to tell us
  341. // whether or not the data is safe.
  342. //
  343. Inflater inf = curs.inflater();
  344. byte[] tmp = new byte[1024];
  345. if (quickCopy != null) {
  346. quickCopy.check(inf, tmp, dataOffset, (int) dataLength);
  347. } else {
  348. long pos = dataOffset;
  349. long cnt = dataLength;
  350. while (cnt > 0) {
  351. final int n = (int) Math.min(cnt, buf.length);
  352. readFully(pos, buf, 0, n, curs);
  353. crc1.update(buf, 0, n);
  354. inf.setInput(buf, 0, n);
  355. while (inf.inflate(tmp, 0, tmp.length) > 0)
  356. continue;
  357. pos += n;
  358. cnt -= n;
  359. }
  360. }
  361. if (!inf.finished() || inf.getBytesRead() != dataLength) {
  362. setCorrupt(src.offset);
  363. throw new EOFException(MessageFormat.format(
  364. JGitText.get().shortCompressedStreamAt,
  365. src.offset));
  366. }
  367. expectedCRC = crc1.getValue();
  368. }
  369. } catch (DataFormatException dataFormat) {
  370. setCorrupt(src.offset);
  371. CorruptObjectException corruptObject = new CorruptObjectException(
  372. MessageFormat.format(
  373. JGitText.get().objectAtHasBadZlibStream,
  374. src.offset, getPackFile()));
  375. corruptObject.initCause(dataFormat);
  376. StoredObjectRepresentationNotAvailableException gone;
  377. gone = new StoredObjectRepresentationNotAvailableException(src);
  378. gone.initCause(corruptObject);
  379. throw gone;
  380. } catch (IOException ioError) {
  381. StoredObjectRepresentationNotAvailableException gone;
  382. gone = new StoredObjectRepresentationNotAvailableException(src);
  383. gone.initCause(ioError);
  384. throw gone;
  385. }
  386. if (quickCopy != null) {
  387. // The entire object fits into a single byte array window slice,
  388. // and we have it pinned. Write this out without copying.
  389. //
  390. out.writeHeader(src, inflatedLength);
  391. quickCopy.write(out, dataOffset, (int) dataLength);
  392. } else if (dataLength <= buf.length) {
  393. // Tiny optimization: Lots of objects are very small deltas or
  394. // deflated commits that are likely to fit in the copy buffer.
  395. //
  396. out.writeHeader(src, inflatedLength);
  397. out.write(buf, 0, (int) dataLength);
  398. } else {
  399. // Now we are committed to sending the object. As we spool it out,
  400. // check its CRC32 code to make sure there wasn't corruption between
  401. // the verification we did above, and us actually outputting it.
  402. //
  403. out.writeHeader(src, inflatedLength);
  404. long pos = dataOffset;
  405. long cnt = dataLength;
  406. while (cnt > 0) {
  407. final int n = (int) Math.min(cnt, buf.length);
  408. readFully(pos, buf, 0, n, curs);
  409. crc2.update(buf, 0, n);
  410. out.write(buf, 0, n);
  411. pos += n;
  412. cnt -= n;
  413. }
  414. if (crc2.getValue() != expectedCRC) {
  415. throw new CorruptObjectException(MessageFormat.format(JGitText
  416. .get().objectAtHasBadZlibStream, src.offset,
  417. getPackFile()));
  418. }
  419. }
  420. }
  421. boolean invalid() {
  422. return invalid;
  423. }
  424. private void readFully(final long position, final byte[] dstbuf,
  425. int dstoff, final int cnt, final WindowCursor curs)
  426. throws IOException {
  427. if (curs.copy(this, position, dstbuf, dstoff, cnt) != cnt)
  428. throw new EOFException();
  429. }
  430. private synchronized void beginCopyAsIs(ObjectToPack otp)
  431. throws StoredObjectRepresentationNotAvailableException {
  432. if (++activeCopyRawData == 1 && activeWindows == 0) {
  433. try {
  434. doOpen();
  435. } catch (IOException thisPackNotValid) {
  436. StoredObjectRepresentationNotAvailableException gone;
  437. gone = new StoredObjectRepresentationNotAvailableException(otp);
  438. gone.initCause(thisPackNotValid);
  439. throw gone;
  440. }
  441. }
  442. }
  443. private synchronized void endCopyAsIs() {
  444. if (--activeCopyRawData == 0 && activeWindows == 0)
  445. doClose();
  446. }
  447. synchronized boolean beginWindowCache() throws IOException {
  448. if (++activeWindows == 1) {
  449. if (activeCopyRawData == 0)
  450. doOpen();
  451. return true;
  452. }
  453. return false;
  454. }
  455. synchronized boolean endWindowCache() {
  456. final boolean r = --activeWindows == 0;
  457. if (r && activeCopyRawData == 0)
  458. doClose();
  459. return r;
  460. }
  461. private void doOpen() throws IOException {
  462. try {
  463. if (invalid)
  464. throw new PackInvalidException(packFile);
  465. synchronized (readLock) {
  466. fd = new RandomAccessFile(packFile, "r");
  467. length = fd.length();
  468. onOpenPack();
  469. }
  470. } catch (IOException ioe) {
  471. openFail();
  472. throw ioe;
  473. } catch (RuntimeException re) {
  474. openFail();
  475. throw re;
  476. } catch (Error re) {
  477. openFail();
  478. throw re;
  479. }
  480. }
  481. private void openFail() {
  482. activeWindows = 0;
  483. activeCopyRawData = 0;
  484. invalid = true;
  485. doClose();
  486. }
  487. private void doClose() {
  488. synchronized (readLock) {
  489. if (fd != null) {
  490. try {
  491. fd.close();
  492. } catch (IOException err) {
  493. // Ignore a close event. We had it open only for reading.
  494. // There should not be errors related to network buffers
  495. // not flushed, etc.
  496. }
  497. fd = null;
  498. }
  499. }
  500. }
  501. ByteArrayWindow read(final long pos, int size) throws IOException {
  502. synchronized (readLock) {
  503. if (length < pos + size)
  504. size = (int) (length - pos);
  505. final byte[] buf = new byte[size];
  506. fd.seek(pos);
  507. fd.readFully(buf, 0, size);
  508. return new ByteArrayWindow(this, pos, buf);
  509. }
  510. }
  511. ByteWindow mmap(final long pos, int size) throws IOException {
  512. synchronized (readLock) {
  513. if (length < pos + size)
  514. size = (int) (length - pos);
  515. MappedByteBuffer map;
  516. try {
  517. map = fd.getChannel().map(MapMode.READ_ONLY, pos, size);
  518. } catch (IOException ioe1) {
  519. // The most likely reason this failed is the JVM has run out
  520. // of virtual memory. We need to discard quickly, and try to
  521. // force the GC to finalize and release any existing mappings.
  522. //
  523. System.gc();
  524. System.runFinalization();
  525. map = fd.getChannel().map(MapMode.READ_ONLY, pos, size);
  526. }
  527. if (map.hasArray())
  528. return new ByteArrayWindow(this, pos, map.array());
  529. return new ByteBufferWindow(this, pos, map);
  530. }
  531. }
  532. private void onOpenPack() throws IOException {
  533. final PackIndex idx = idx();
  534. final byte[] buf = new byte[20];
  535. fd.seek(0);
  536. fd.readFully(buf, 0, 12);
  537. if (RawParseUtils.match(buf, 0, Constants.PACK_SIGNATURE) != 4)
  538. throw new IOException(JGitText.get().notAPACKFile);
  539. final long vers = NB.decodeUInt32(buf, 4);
  540. final long packCnt = NB.decodeUInt32(buf, 8);
  541. if (vers != 2 && vers != 3)
  542. throw new IOException(MessageFormat.format(JGitText.get().unsupportedPackVersion, vers));
  543. if (packCnt != idx.getObjectCount())
  544. throw new PackMismatchException(MessageFormat.format(
  545. JGitText.get().packObjectCountMismatch, packCnt, idx.getObjectCount(), getPackFile()));
  546. fd.seek(length - 20);
  547. fd.read(buf, 0, 20);
  548. if (!Arrays.equals(buf, packChecksum))
  549. throw new PackMismatchException(MessageFormat.format(
  550. JGitText.get().packObjectCountMismatch
  551. , ObjectId.fromRaw(buf).name()
  552. , ObjectId.fromRaw(idx.packChecksum).name()
  553. , getPackFile()));
  554. }
  555. private PackedObjectLoader load(final WindowCursor curs, final long pos)
  556. throws IOException {
  557. final byte[] ib = curs.tempId;
  558. readFully(pos, ib, 0, 20, curs);
  559. int c = ib[0] & 0xff;
  560. final int type = (c >> 4) & 7;
  561. long sz = c & 15;
  562. int shift = 4;
  563. int p = 1;
  564. while ((c & 0x80) != 0) {
  565. c = ib[p++] & 0xff;
  566. sz += (c & 0x7f) << shift;
  567. shift += 7;
  568. }
  569. try {
  570. switch (type) {
  571. case Constants.OBJ_COMMIT:
  572. case Constants.OBJ_TREE:
  573. case Constants.OBJ_BLOB:
  574. case Constants.OBJ_TAG: {
  575. byte[] data = decompress(pos + p, sz, curs);
  576. return new PackedObjectLoader(type, data);
  577. }
  578. case Constants.OBJ_OFS_DELTA: {
  579. c = ib[p++] & 0xff;
  580. long ofs = c & 127;
  581. while ((c & 128) != 0) {
  582. ofs += 1;
  583. c = ib[p++] & 0xff;
  584. ofs <<= 7;
  585. ofs += (c & 127);
  586. }
  587. return loadDelta(pos + p, sz, pos - ofs, curs);
  588. }
  589. case Constants.OBJ_REF_DELTA: {
  590. readFully(pos + p, ib, 0, 20, curs);
  591. long ofs = findDeltaBase(ObjectId.fromRaw(ib));
  592. return loadDelta(pos + p + 20, sz, ofs, curs);
  593. }
  594. default:
  595. throw new IOException(MessageFormat.format(
  596. JGitText.get().unknownObjectType, type));
  597. }
  598. } catch (DataFormatException dfe) {
  599. CorruptObjectException coe = new CorruptObjectException(
  600. MessageFormat.format(
  601. JGitText.get().objectAtHasBadZlibStream, pos,
  602. getPackFile()));
  603. coe.initCause(dfe);
  604. throw coe;
  605. }
  606. }
  607. private long findDeltaBase(ObjectId baseId) throws IOException,
  608. MissingObjectException {
  609. long ofs = idx().findOffset(baseId);
  610. if (ofs < 0)
  611. throw new MissingObjectException(baseId,
  612. JGitText.get().missingDeltaBase);
  613. return ofs;
  614. }
  615. private PackedObjectLoader loadDelta(final long posData, long sz,
  616. final long posBase, final WindowCursor curs) throws IOException,
  617. DataFormatException {
  618. byte[] data;
  619. int type;
  620. UnpackedObjectCache.Entry e = readCache(posBase);
  621. if (e != null) {
  622. data = e.data;
  623. type = e.type;
  624. } else {
  625. PackedObjectLoader p = load(curs, posBase);
  626. data = p.getCachedBytes();
  627. type = p.getType();
  628. saveCache(posBase, data, type);
  629. }
  630. data = BinaryDelta.apply(data, decompress(posData, sz, curs));
  631. return new PackedObjectLoader(type, data);
  632. }
  633. LocalObjectRepresentation representation(final WindowCursor curs,
  634. final AnyObjectId objectId) throws IOException {
  635. final long pos = idx().findOffset(objectId);
  636. if (pos < 0)
  637. return null;
  638. final byte[] ib = curs.tempId;
  639. readFully(pos, ib, 0, 20, curs);
  640. int c = ib[0] & 0xff;
  641. int p = 1;
  642. final int typeCode = (c >> 4) & 7;
  643. while ((c & 0x80) != 0)
  644. c = ib[p++] & 0xff;
  645. long len = (findEndOffset(pos) - pos);
  646. switch (typeCode) {
  647. case Constants.OBJ_COMMIT:
  648. case Constants.OBJ_TREE:
  649. case Constants.OBJ_BLOB:
  650. case Constants.OBJ_TAG:
  651. return LocalObjectRepresentation.newWhole(this, pos, len - p);
  652. case Constants.OBJ_OFS_DELTA: {
  653. c = ib[p++] & 0xff;
  654. long ofs = c & 127;
  655. while ((c & 128) != 0) {
  656. ofs += 1;
  657. c = ib[p++] & 0xff;
  658. ofs <<= 7;
  659. ofs += (c & 127);
  660. }
  661. ofs = pos - ofs;
  662. return LocalObjectRepresentation.newDelta(this, pos, len - p, ofs);
  663. }
  664. case Constants.OBJ_REF_DELTA: {
  665. len -= p;
  666. len -= Constants.OBJECT_ID_LENGTH;
  667. readFully(pos + p, ib, 0, 20, curs);
  668. ObjectId id = ObjectId.fromRaw(ib);
  669. return LocalObjectRepresentation.newDelta(this, pos, len, id);
  670. }
  671. default:
  672. throw new IOException(MessageFormat.format(
  673. JGitText.get().unknownObjectType, typeCode));
  674. }
  675. }
  676. private long findEndOffset(final long startOffset)
  677. throws IOException, CorruptObjectException {
  678. final long maxOffset = length - 20;
  679. return getReverseIdx().findNextOffset(startOffset, maxOffset);
  680. }
  681. private synchronized PackReverseIndex getReverseIdx() throws IOException {
  682. if (reverseIdx == null)
  683. reverseIdx = new PackReverseIndex(idx());
  684. return reverseIdx;
  685. }
  686. private boolean isCorrupt(long offset) {
  687. LongList list = corruptObjects;
  688. if (list == null)
  689. return false;
  690. synchronized (list) {
  691. return list.contains(offset);
  692. }
  693. }
  694. private void setCorrupt(long offset) {
  695. LongList list = corruptObjects;
  696. if (list == null) {
  697. synchronized (readLock) {
  698. list = corruptObjects;
  699. if (list == null) {
  700. list = new LongList();
  701. corruptObjects = list;
  702. }
  703. }
  704. }
  705. synchronized (list) {
  706. list.add(offset);
  707. }
  708. }
  709. }