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.

UnpackedObjectTest.java 16KB

Increase core.streamFileThreshold default to 50 MiB Projects like org.eclipse.mdt contain large XML files about 6 MiB in size. So does the Android project platform/frameworks/base. Doing a clone of either project with JGit takes forever to checkout the files into the working directory, because delta decompression tends to be very expensive as we need to constantly reposition the base stream for each copy instruction. This can be made worse by a very bad ordering of offsets, possibly due to an XML editor that doesn't preserve the order of elements in the file very well. Increasing the threshold to the same limit PackWriter uses when doing delta compression (50 MiB) permits a default configured JGit to decompress these XML file objects using the faster random-access arrays, rather than re-seeking through an inflate stream, significantly reducing checkout time after a clone. Since this new limit may be dangerously close to the JVM maximum heap size, every allocation attempt is now wrapped in a try/catch so that JGit can degrade by switching to the large object stream mode when the allocation is refused. It will run slower, but the operation will still complete. The large stream mode will run very well for big objects that aren't delta compressed, and is acceptable for delta compressed objects that are using only forward referencing copy instructions. Copies using prior offsets are still going to be horrible, and there is nothing we can do about it except increase core.streamFileThreshold. We might in the future want to consider changing the way the delta generators work in JGit and native C Git to avoid prior offsets once an object reaches a certain size, even if that causes the delta instruction stream to be slightly larger. Unfortunately native C Git won't want to do that until its also able to stream objects rather than malloc them as contiguous blocks. Change-Id: Ief7a3896afce15073e80d3691bed90c6a3897307 Signed-off-by: Shawn O. Pearce <spearce@spearce.org> Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
13 년 전
Increase core.streamFileThreshold default to 50 MiB Projects like org.eclipse.mdt contain large XML files about 6 MiB in size. So does the Android project platform/frameworks/base. Doing a clone of either project with JGit takes forever to checkout the files into the working directory, because delta decompression tends to be very expensive as we need to constantly reposition the base stream for each copy instruction. This can be made worse by a very bad ordering of offsets, possibly due to an XML editor that doesn't preserve the order of elements in the file very well. Increasing the threshold to the same limit PackWriter uses when doing delta compression (50 MiB) permits a default configured JGit to decompress these XML file objects using the faster random-access arrays, rather than re-seeking through an inflate stream, significantly reducing checkout time after a clone. Since this new limit may be dangerously close to the JVM maximum heap size, every allocation attempt is now wrapped in a try/catch so that JGit can degrade by switching to the large object stream mode when the allocation is refused. It will run slower, but the operation will still complete. The large stream mode will run very well for big objects that aren't delta compressed, and is acceptable for delta compressed objects that are using only forward referencing copy instructions. Copies using prior offsets are still going to be horrible, and there is nothing we can do about it except increase core.streamFileThreshold. We might in the future want to consider changing the way the delta generators work in JGit and native C Git to avoid prior offsets once an object reaches a certain size, even if that causes the delta instruction stream to be slightly larger. Unfortunately native C Git won't want to do that until its also able to stream objects rather than malloc them as contiguous blocks. Change-Id: Ief7a3896afce15073e80d3691bed90c6a3897307 Signed-off-by: Shawn O. Pearce <spearce@spearce.org> Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
13 년 전
Increase core.streamFileThreshold default to 50 MiB Projects like org.eclipse.mdt contain large XML files about 6 MiB in size. So does the Android project platform/frameworks/base. Doing a clone of either project with JGit takes forever to checkout the files into the working directory, because delta decompression tends to be very expensive as we need to constantly reposition the base stream for each copy instruction. This can be made worse by a very bad ordering of offsets, possibly due to an XML editor that doesn't preserve the order of elements in the file very well. Increasing the threshold to the same limit PackWriter uses when doing delta compression (50 MiB) permits a default configured JGit to decompress these XML file objects using the faster random-access arrays, rather than re-seeking through an inflate stream, significantly reducing checkout time after a clone. Since this new limit may be dangerously close to the JVM maximum heap size, every allocation attempt is now wrapped in a try/catch so that JGit can degrade by switching to the large object stream mode when the allocation is refused. It will run slower, but the operation will still complete. The large stream mode will run very well for big objects that aren't delta compressed, and is acceptable for delta compressed objects that are using only forward referencing copy instructions. Copies using prior offsets are still going to be horrible, and there is nothing we can do about it except increase core.streamFileThreshold. We might in the future want to consider changing the way the delta generators work in JGit and native C Git to avoid prior offsets once an object reaches a certain size, even if that causes the delta instruction stream to be slightly larger. Unfortunately native C Git won't want to do that until its also able to stream objects rather than malloc them as contiguous blocks. Change-Id: Ief7a3896afce15073e80d3691bed90c6a3897307 Signed-off-by: Shawn O. Pearce <spearce@spearce.org> Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
13 년 전
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. /*
  2. * Copyright (C) 2010, Google Inc. and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.internal.storage.file;
  11. import static org.junit.Assert.assertEquals;
  12. import static org.junit.Assert.assertFalse;
  13. import static org.junit.Assert.assertNotNull;
  14. import static org.junit.Assert.assertTrue;
  15. import static org.junit.Assert.fail;
  16. import java.io.ByteArrayInputStream;
  17. import java.io.ByteArrayOutputStream;
  18. import java.io.File;
  19. import java.io.FileInputStream;
  20. import java.io.FileOutputStream;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.text.MessageFormat;
  24. import java.util.Arrays;
  25. import java.util.zip.DeflaterOutputStream;
  26. import org.eclipse.jgit.errors.CorruptObjectException;
  27. import org.eclipse.jgit.errors.LargeObjectException;
  28. import org.eclipse.jgit.internal.JGitText;
  29. import org.eclipse.jgit.junit.JGitTestUtil;
  30. import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
  31. import org.eclipse.jgit.junit.TestRng;
  32. import org.eclipse.jgit.lib.Constants;
  33. import org.eclipse.jgit.lib.ObjectId;
  34. import org.eclipse.jgit.lib.ObjectInserter;
  35. import org.eclipse.jgit.lib.ObjectLoader;
  36. import org.eclipse.jgit.lib.ObjectStream;
  37. import org.eclipse.jgit.storage.file.WindowCacheConfig;
  38. import org.eclipse.jgit.util.FileUtils;
  39. import org.eclipse.jgit.util.IO;
  40. import org.junit.After;
  41. import org.junit.Before;
  42. import org.junit.Test;
  43. public class UnpackedObjectTest extends LocalDiskRepositoryTestCase {
  44. private int streamThreshold = 16 * 1024;
  45. private TestRng rng;
  46. private FileRepository repo;
  47. private WindowCursor wc;
  48. private TestRng getRng() {
  49. if (rng == null)
  50. rng = new TestRng(JGitTestUtil.getName());
  51. return rng;
  52. }
  53. @Override
  54. @Before
  55. public void setUp() throws Exception {
  56. super.setUp();
  57. WindowCacheConfig cfg = new WindowCacheConfig();
  58. cfg.setStreamFileThreshold(streamThreshold);
  59. cfg.install();
  60. repo = createBareRepository();
  61. wc = (WindowCursor) repo.newObjectReader();
  62. }
  63. @Override
  64. @After
  65. public void tearDown() throws Exception {
  66. if (wc != null)
  67. wc.close();
  68. new WindowCacheConfig().install();
  69. super.tearDown();
  70. }
  71. @Test
  72. public void testStandardFormat_SmallObject() throws Exception {
  73. final int type = Constants.OBJ_BLOB;
  74. byte[] data = getRng().nextBytes(300);
  75. byte[] gz = compressStandardFormat(type, data);
  76. ObjectId id = ObjectId.zeroId();
  77. ObjectLoader ol = UnpackedObject.open(new ByteArrayInputStream(gz),
  78. path(id), id, wc);
  79. assertNotNull("created loader", ol);
  80. assertEquals(type, ol.getType());
  81. assertEquals(data.length, ol.getSize());
  82. assertFalse("is not large", ol.isLarge());
  83. assertTrue("same content", Arrays.equals(data, ol.getCachedBytes()));
  84. try (ObjectStream in = ol.openStream()) {
  85. assertNotNull("have stream", in);
  86. assertEquals(type, in.getType());
  87. assertEquals(data.length, in.getSize());
  88. byte[] data2 = new byte[data.length];
  89. IO.readFully(in, data2, 0, data.length);
  90. assertTrue("same content", Arrays.equals(data2, data));
  91. assertEquals("stream at EOF", -1, in.read());
  92. }
  93. }
  94. @Test
  95. public void testStandardFormat_LargeObject() throws Exception {
  96. final int type = Constants.OBJ_BLOB;
  97. byte[] data = getRng().nextBytes(streamThreshold + 5);
  98. ObjectId id = getId(type, data);
  99. write(id, compressStandardFormat(type, data));
  100. ObjectLoader ol;
  101. {
  102. try (FileInputStream fs = new FileInputStream(path(id))) {
  103. ol = UnpackedObject.open(fs, path(id), id, wc);
  104. }
  105. }
  106. assertNotNull("created loader", ol);
  107. assertEquals(type, ol.getType());
  108. assertEquals(data.length, ol.getSize());
  109. assertTrue("is large", ol.isLarge());
  110. try {
  111. ol.getCachedBytes();
  112. fail("Should have thrown LargeObjectException");
  113. } catch (LargeObjectException tooBig) {
  114. assertEquals(MessageFormat.format(
  115. JGitText.get().largeObjectException, id.name()), tooBig
  116. .getMessage());
  117. }
  118. try (ObjectStream in = ol.openStream()) {
  119. assertNotNull("have stream", in);
  120. assertEquals(type, in.getType());
  121. assertEquals(data.length, in.getSize());
  122. byte[] data2 = new byte[data.length];
  123. IO.readFully(in, data2, 0, data.length);
  124. assertTrue("same content", Arrays.equals(data2, data));
  125. assertEquals("stream at EOF", -1, in.read());
  126. }
  127. }
  128. @Test
  129. public void testStandardFormat_NegativeSize() throws Exception {
  130. ObjectId id = ObjectId.zeroId();
  131. byte[] data = getRng().nextBytes(300);
  132. try {
  133. byte[] gz = compressStandardFormat("blob", "-1", data);
  134. UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
  135. fail("Did not throw CorruptObjectException");
  136. } catch (CorruptObjectException coe) {
  137. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  138. id.name(), JGitText.get().corruptObjectNegativeSize), coe
  139. .getMessage());
  140. }
  141. }
  142. @Test
  143. public void testStandardFormat_InvalidType() throws Exception {
  144. ObjectId id = ObjectId.zeroId();
  145. byte[] data = getRng().nextBytes(300);
  146. try {
  147. byte[] gz = compressStandardFormat("not.a.type", "1", data);
  148. UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
  149. fail("Did not throw CorruptObjectException");
  150. } catch (CorruptObjectException coe) {
  151. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  152. id.name(), JGitText.get().corruptObjectInvalidType), coe
  153. .getMessage());
  154. }
  155. }
  156. @Test
  157. public void testStandardFormat_NoHeader() throws Exception {
  158. ObjectId id = ObjectId.zeroId();
  159. byte[] data = {};
  160. try {
  161. byte[] gz = compressStandardFormat("", "", data);
  162. UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
  163. fail("Did not throw CorruptObjectException");
  164. } catch (CorruptObjectException coe) {
  165. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  166. id.name(), JGitText.get().corruptObjectNoHeader), coe
  167. .getMessage());
  168. }
  169. }
  170. @Test
  171. public void testStandardFormat_GarbageAfterSize() throws Exception {
  172. ObjectId id = ObjectId.zeroId();
  173. byte[] data = getRng().nextBytes(300);
  174. try {
  175. byte[] gz = compressStandardFormat("blob", "1foo", data);
  176. UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
  177. fail("Did not throw CorruptObjectException");
  178. } catch (CorruptObjectException coe) {
  179. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  180. id.name(), JGitText.get().corruptObjectGarbageAfterSize),
  181. coe.getMessage());
  182. }
  183. }
  184. @Test
  185. public void testStandardFormat_SmallObject_CorruptZLibStream()
  186. throws Exception {
  187. ObjectId id = ObjectId.zeroId();
  188. byte[] data = getRng().nextBytes(300);
  189. try {
  190. byte[] gz = compressStandardFormat(Constants.OBJ_BLOB, data);
  191. for (int i = 5; i < gz.length; i++)
  192. gz[i] = 0;
  193. UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
  194. fail("Did not throw CorruptObjectException");
  195. } catch (CorruptObjectException coe) {
  196. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  197. id.name(), JGitText.get().corruptObjectBadStream), coe
  198. .getMessage());
  199. }
  200. }
  201. @Test
  202. public void testStandardFormat_SmallObject_TruncatedZLibStream()
  203. throws Exception {
  204. ObjectId id = ObjectId.zeroId();
  205. byte[] data = getRng().nextBytes(300);
  206. try {
  207. byte[] gz = compressStandardFormat(Constants.OBJ_BLOB, data);
  208. byte[] tr = new byte[gz.length - 1];
  209. System.arraycopy(gz, 0, tr, 0, tr.length);
  210. UnpackedObject.open(new ByteArrayInputStream(tr), path(id), id, wc);
  211. fail("Did not throw CorruptObjectException");
  212. } catch (CorruptObjectException coe) {
  213. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  214. id.name(), JGitText.get().corruptObjectBadStream), coe
  215. .getMessage());
  216. }
  217. }
  218. @Test
  219. public void testStandardFormat_SmallObject_TrailingGarbage()
  220. throws Exception {
  221. ObjectId id = ObjectId.zeroId();
  222. byte[] data = getRng().nextBytes(300);
  223. try {
  224. byte[] gz = compressStandardFormat(Constants.OBJ_BLOB, data);
  225. byte[] tr = new byte[gz.length + 1];
  226. System.arraycopy(gz, 0, tr, 0, gz.length);
  227. UnpackedObject.open(new ByteArrayInputStream(tr), path(id), id, wc);
  228. fail("Did not throw CorruptObjectException");
  229. } catch (CorruptObjectException coe) {
  230. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  231. id.name(), JGitText.get().corruptObjectBadStream), coe
  232. .getMessage());
  233. }
  234. }
  235. @Test
  236. public void testStandardFormat_LargeObject_CorruptZLibStream()
  237. throws Exception {
  238. final int type = Constants.OBJ_BLOB;
  239. byte[] data = getRng().nextBytes(streamThreshold + 5);
  240. ObjectId id = getId(type, data);
  241. byte[] gz = compressStandardFormat(type, data);
  242. gz[gz.length - 1] = 0;
  243. gz[gz.length - 2] = 0;
  244. write(id, gz);
  245. ObjectLoader ol;
  246. try (FileInputStream fs = new FileInputStream(path(id))) {
  247. ol = UnpackedObject.open(fs, path(id), id, wc);
  248. }
  249. byte[] tmp = new byte[data.length];
  250. try (InputStream in = ol.openStream()) {
  251. IO.readFully(in, tmp, 0, tmp.length);
  252. fail("Did not throw CorruptObjectException");
  253. } catch (CorruptObjectException coe) {
  254. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  255. id.name(), JGitText.get().corruptObjectBadStream), coe
  256. .getMessage());
  257. }
  258. }
  259. @Test
  260. public void testStandardFormat_LargeObject_TruncatedZLibStream()
  261. throws Exception {
  262. final int type = Constants.OBJ_BLOB;
  263. byte[] data = getRng().nextBytes(streamThreshold + 5);
  264. ObjectId id = getId(type, data);
  265. byte[] gz = compressStandardFormat(type, data);
  266. byte[] tr = new byte[gz.length - 1];
  267. System.arraycopy(gz, 0, tr, 0, tr.length);
  268. write(id, tr);
  269. ObjectLoader ol;
  270. try (FileInputStream fs = new FileInputStream(path(id))) {
  271. ol = UnpackedObject.open(fs, path(id), id, wc);
  272. }
  273. byte[] tmp = new byte[data.length];
  274. @SuppressWarnings("resource") // We are testing that the close() method throws
  275. InputStream in = ol.openStream();
  276. IO.readFully(in, tmp, 0, tmp.length);
  277. try {
  278. in.close();
  279. fail("close did not throw CorruptObjectException");
  280. } catch (CorruptObjectException coe) {
  281. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  282. id.name(), JGitText.get().corruptObjectBadStream), coe
  283. .getMessage());
  284. }
  285. }
  286. @Test
  287. public void testStandardFormat_LargeObject_TrailingGarbage()
  288. throws Exception {
  289. final int type = Constants.OBJ_BLOB;
  290. byte[] data = getRng().nextBytes(streamThreshold + 5);
  291. ObjectId id = getId(type, data);
  292. byte[] gz = compressStandardFormat(type, data);
  293. byte[] tr = new byte[gz.length + 1];
  294. System.arraycopy(gz, 0, tr, 0, gz.length);
  295. write(id, tr);
  296. ObjectLoader ol;
  297. try (FileInputStream fs = new FileInputStream(path(id))) {
  298. ol = UnpackedObject.open(fs, path(id), id, wc);
  299. }
  300. byte[] tmp = new byte[data.length];
  301. @SuppressWarnings("resource") // We are testing that the close() method throws
  302. InputStream in = ol.openStream();
  303. IO.readFully(in, tmp, 0, tmp.length);
  304. try {
  305. in.close();
  306. fail("close did not throw CorruptObjectException");
  307. } catch (CorruptObjectException coe) {
  308. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  309. id.name(), JGitText.get().corruptObjectBadStream), coe
  310. .getMessage());
  311. }
  312. }
  313. @Test
  314. public void testPackFormat_SmallObject() throws Exception {
  315. final int type = Constants.OBJ_BLOB;
  316. byte[] data = getRng().nextBytes(300);
  317. byte[] gz = compressPackFormat(type, data);
  318. ObjectId id = ObjectId.zeroId();
  319. ObjectLoader ol = UnpackedObject.open(new ByteArrayInputStream(gz),
  320. path(id), id, wc);
  321. assertNotNull("created loader", ol);
  322. assertEquals(type, ol.getType());
  323. assertEquals(data.length, ol.getSize());
  324. assertFalse("is not large", ol.isLarge());
  325. assertTrue("same content", Arrays.equals(data, ol.getCachedBytes()));
  326. try (ObjectStream in = ol.openStream()) {
  327. assertNotNull("have stream", in);
  328. assertEquals(type, in.getType());
  329. assertEquals(data.length, in.getSize());
  330. byte[] data2 = new byte[data.length];
  331. IO.readFully(in, data2, 0, data.length);
  332. assertTrue("same content",
  333. Arrays.equals(data, ol.getCachedBytes()));
  334. }
  335. }
  336. @Test
  337. public void testPackFormat_LargeObject() throws Exception {
  338. final int type = Constants.OBJ_BLOB;
  339. byte[] data = getRng().nextBytes(streamThreshold + 5);
  340. ObjectId id = getId(type, data);
  341. write(id, compressPackFormat(type, data));
  342. ObjectLoader ol;
  343. try (FileInputStream fs = new FileInputStream(path(id))) {
  344. ol = UnpackedObject.open(fs, path(id), id, wc);
  345. }
  346. assertNotNull("created loader", ol);
  347. assertEquals(type, ol.getType());
  348. assertEquals(data.length, ol.getSize());
  349. assertTrue("is large", ol.isLarge());
  350. try {
  351. ol.getCachedBytes();
  352. fail("Should have thrown LargeObjectException");
  353. } catch (LargeObjectException tooBig) {
  354. assertEquals(MessageFormat.format(
  355. JGitText.get().largeObjectException, id.name()), tooBig
  356. .getMessage());
  357. }
  358. try (ObjectStream in = ol.openStream()) {
  359. assertNotNull("have stream", in);
  360. assertEquals(type, in.getType());
  361. assertEquals(data.length, in.getSize());
  362. byte[] data2 = new byte[data.length];
  363. IO.readFully(in, data2, 0, data.length);
  364. assertTrue("same content", Arrays.equals(data2, data));
  365. assertEquals("stream at EOF", -1, in.read());
  366. }
  367. }
  368. @Test
  369. public void testPackFormat_DeltaNotAllowed() throws Exception {
  370. ObjectId id = ObjectId.zeroId();
  371. byte[] data = getRng().nextBytes(300);
  372. try {
  373. byte[] gz = compressPackFormat(Constants.OBJ_OFS_DELTA, data);
  374. UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
  375. fail("Did not throw CorruptObjectException");
  376. } catch (CorruptObjectException coe) {
  377. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  378. id.name(), JGitText.get().corruptObjectInvalidType), coe
  379. .getMessage());
  380. }
  381. try {
  382. byte[] gz = compressPackFormat(Constants.OBJ_REF_DELTA, data);
  383. UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
  384. fail("Did not throw CorruptObjectException");
  385. } catch (CorruptObjectException coe) {
  386. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  387. id.name(), JGitText.get().corruptObjectInvalidType), coe
  388. .getMessage());
  389. }
  390. try {
  391. byte[] gz = compressPackFormat(Constants.OBJ_TYPE_5, data);
  392. UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
  393. fail("Did not throw CorruptObjectException");
  394. } catch (CorruptObjectException coe) {
  395. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  396. id.name(), JGitText.get().corruptObjectInvalidType), coe
  397. .getMessage());
  398. }
  399. try {
  400. byte[] gz = compressPackFormat(Constants.OBJ_EXT, data);
  401. UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
  402. fail("Did not throw CorruptObjectException");
  403. } catch (CorruptObjectException coe) {
  404. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  405. id.name(), JGitText.get().corruptObjectInvalidType), coe
  406. .getMessage());
  407. }
  408. }
  409. private static byte[] compressStandardFormat(int type, byte[] data)
  410. throws IOException {
  411. String typeString = Constants.typeString(type);
  412. String length = String.valueOf(data.length);
  413. return compressStandardFormat(typeString, length, data);
  414. }
  415. private static byte[] compressStandardFormat(String type, String length,
  416. byte[] data) throws IOException {
  417. ByteArrayOutputStream out = new ByteArrayOutputStream();
  418. DeflaterOutputStream d = new DeflaterOutputStream(out);
  419. d.write(Constants.encodeASCII(type));
  420. d.write(' ');
  421. d.write(Constants.encodeASCII(length));
  422. d.write(0);
  423. d.write(data);
  424. d.finish();
  425. return out.toByteArray();
  426. }
  427. private static byte[] compressPackFormat(int type, byte[] data)
  428. throws IOException {
  429. byte[] hdr = new byte[64];
  430. int rawLength = data.length;
  431. int nextLength = rawLength >>> 4;
  432. hdr[0] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (type << 4) | (rawLength & 0x0F));
  433. rawLength = nextLength;
  434. int n = 1;
  435. while (rawLength > 0) {
  436. nextLength >>>= 7;
  437. hdr[n++] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (rawLength & 0x7F));
  438. rawLength = nextLength;
  439. }
  440. final ByteArrayOutputStream out = new ByteArrayOutputStream();
  441. out.write(hdr, 0, n);
  442. DeflaterOutputStream d = new DeflaterOutputStream(out);
  443. d.write(data);
  444. d.finish();
  445. return out.toByteArray();
  446. }
  447. private File path(ObjectId id) {
  448. return repo.getObjectDatabase().fileFor(id);
  449. }
  450. private void write(ObjectId id, byte[] data) throws IOException {
  451. File path = path(id);
  452. FileUtils.mkdirs(path.getParentFile());
  453. try (FileOutputStream out = new FileOutputStream(path)) {
  454. out.write(data);
  455. }
  456. }
  457. private ObjectId getId(int type, byte[] data) {
  458. try (ObjectInserter.Formatter formatter = new ObjectInserter.Formatter()) {
  459. return formatter.idFor(type, data);
  460. }
  461. }
  462. }