選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

DeltaStream.java 9.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. /*
  2. * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
  3. * Copyright (C) 2006-2007, Shawn O. Pearce <spearce@spearce.org>
  4. * Copyright (C) 2010, Google Inc.
  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.pack;
  46. import java.io.EOFException;
  47. import java.io.IOException;
  48. import java.io.InputStream;
  49. import org.eclipse.jgit.JGitText;
  50. import org.eclipse.jgit.errors.CorruptObjectException;
  51. import org.eclipse.jgit.util.IO;
  52. /**
  53. * Inflates a delta in an incremental way.
  54. * <p>
  55. * Implementations must provide a means to access a stream for the base object.
  56. * This stream may be accessed multiple times, in order to randomly position it
  57. * to match the copy instructions. A {@code DeltaStream} performs an efficient
  58. * skip by only moving through the delta stream, making restarts of stacked
  59. * deltas reasonably efficient.
  60. */
  61. public abstract class DeltaStream extends InputStream {
  62. private static final int CMD_COPY = 0;
  63. private static final int CMD_INSERT = 1;
  64. private static final int CMD_EOF = 2;
  65. private final InputStream deltaStream;
  66. private long baseSize;
  67. private long resultSize;
  68. private final byte[] cmdbuf = new byte[512];
  69. private int cmdptr;
  70. private int cmdcnt;
  71. /** Stream to read from the base object. */
  72. private InputStream baseStream;
  73. /** Current position within {@link #baseStream}. */
  74. private long baseOffset;
  75. private int curcmd;
  76. /** If {@code curcmd == CMD_COPY}, position the base has to be at. */
  77. private long copyOffset;
  78. /** Total number of bytes in this current command. */
  79. private int copySize;
  80. /**
  81. * Construct a delta application stream, reading instructions.
  82. *
  83. * @param deltaStream
  84. * the stream to read delta instructions from.
  85. * @throws IOException
  86. * the delta instruction stream cannot be read, or is
  87. * inconsistent with the the base object information.
  88. */
  89. public DeltaStream(final InputStream deltaStream) throws IOException {
  90. this.deltaStream = deltaStream;
  91. if (!fill(cmdbuf.length))
  92. throw new EOFException();
  93. // Length of the base object.
  94. //
  95. int c, shift = 0;
  96. do {
  97. c = cmdbuf[cmdptr++] & 0xff;
  98. baseSize |= (c & 0x7f) << shift;
  99. shift += 7;
  100. } while ((c & 0x80) != 0);
  101. // Length of the resulting object.
  102. //
  103. shift = 0;
  104. do {
  105. c = cmdbuf[cmdptr++] & 0xff;
  106. resultSize |= (c & 0x7f) << shift;
  107. shift += 7;
  108. } while ((c & 0x80) != 0);
  109. curcmd = next();
  110. }
  111. /**
  112. * Open the base stream.
  113. * <p>
  114. * The {@code DeltaStream} may close and reopen the base stream multiple
  115. * times if copy instructions use offsets out of order. This can occur if a
  116. * large block in the file was moved from near the top, to near the bottom.
  117. * In such cases the reopened stream is skipped to the target offset, so
  118. * {@code skip(long)} should be as efficient as possible.
  119. *
  120. * @return stream to read from the base object. This stream should not be
  121. * buffered (or should be only minimally buffered), and does not
  122. * need to support mark/reset.
  123. * @throws IOException
  124. * the base object cannot be opened for reading.
  125. */
  126. protected abstract InputStream openBase() throws IOException;
  127. /**
  128. * @return length of the base object, in bytes.
  129. * @throws IOException
  130. * the length of the base cannot be determined.
  131. */
  132. protected abstract long getBaseSize() throws IOException;
  133. /** @return total size of this stream, in bytes. */
  134. public long getSize() {
  135. return resultSize;
  136. }
  137. @Override
  138. public int read() throws IOException {
  139. byte[] buf = new byte[1];
  140. int n = read(buf, 0, 1);
  141. return n == 1 ? buf[0] & 0xff : -1;
  142. }
  143. @Override
  144. public void close() throws IOException {
  145. deltaStream.close();
  146. if (baseStream != null)
  147. baseStream.close();
  148. }
  149. @Override
  150. public long skip(long len) throws IOException {
  151. long act = 0;
  152. while (0 < len) {
  153. long n = Math.min(len, copySize);
  154. switch (curcmd) {
  155. case CMD_COPY:
  156. copyOffset += n;
  157. break;
  158. case CMD_INSERT:
  159. cmdptr += n;
  160. break;
  161. case CMD_EOF:
  162. return act;
  163. default:
  164. throw new CorruptObjectException(
  165. JGitText.get().unsupportedCommand0);
  166. }
  167. act += n;
  168. len -= n;
  169. copySize -= n;
  170. if (copySize == 0)
  171. curcmd = next();
  172. }
  173. return act;
  174. }
  175. @Override
  176. public int read(byte[] buf, int off, int len) throws IOException {
  177. int act = 0;
  178. while (0 < len) {
  179. int n = Math.min(len, copySize);
  180. switch (curcmd) {
  181. case CMD_COPY:
  182. seekBase();
  183. n = baseStream.read(buf, off, n);
  184. if (n < 0)
  185. throw new CorruptObjectException(
  186. JGitText.get().baseLengthIncorrect);
  187. copyOffset += n;
  188. baseOffset = copyOffset;
  189. break;
  190. case CMD_INSERT:
  191. System.arraycopy(cmdbuf, cmdptr, buf, off, n);
  192. cmdptr += n;
  193. break;
  194. case CMD_EOF:
  195. return 0 < act ? act : -1;
  196. default:
  197. throw new CorruptObjectException(
  198. JGitText.get().unsupportedCommand0);
  199. }
  200. act += n;
  201. off += n;
  202. len -= n;
  203. copySize -= n;
  204. if (copySize == 0)
  205. curcmd = next();
  206. }
  207. return act;
  208. }
  209. private boolean fill(final int need) throws IOException {
  210. int n = have();
  211. if (need < n)
  212. return true;
  213. if (n == 0) {
  214. cmdptr = 0;
  215. cmdcnt = 0;
  216. } else if (cmdbuf.length - cmdptr < need) {
  217. // There isn't room for the entire worst-case copy command,
  218. // so shift the array down to make sure we can use the entire
  219. // command without having it span across the end of the array.
  220. //
  221. System.arraycopy(cmdbuf, cmdptr, cmdbuf, 0, n);
  222. cmdptr = 0;
  223. cmdcnt = n;
  224. }
  225. do {
  226. n = deltaStream.read(cmdbuf, cmdcnt, cmdbuf.length - cmdcnt);
  227. if (n < 0)
  228. return 0 < have();
  229. cmdcnt += n;
  230. } while (cmdcnt < cmdbuf.length);
  231. return true;
  232. }
  233. private int next() throws IOException {
  234. if (!fill(8))
  235. return CMD_EOF;
  236. final int cmd = cmdbuf[cmdptr++] & 0xff;
  237. if ((cmd & 0x80) != 0) {
  238. // Determine the segment of the base which should
  239. // be copied into the output. The segment is given
  240. // as an offset and a length.
  241. //
  242. copyOffset = 0;
  243. if ((cmd & 0x01) != 0)
  244. copyOffset = cmdbuf[cmdptr++] & 0xff;
  245. if ((cmd & 0x02) != 0)
  246. copyOffset |= (cmdbuf[cmdptr++] & 0xff) << 8;
  247. if ((cmd & 0x04) != 0)
  248. copyOffset |= (cmdbuf[cmdptr++] & 0xff) << 16;
  249. if ((cmd & 0x08) != 0)
  250. copyOffset |= (cmdbuf[cmdptr++] & 0xff) << 24;
  251. copySize = 0;
  252. if ((cmd & 0x10) != 0)
  253. copySize = cmdbuf[cmdptr++] & 0xff;
  254. if ((cmd & 0x20) != 0)
  255. copySize |= (cmdbuf[cmdptr++] & 0xff) << 8;
  256. if ((cmd & 0x40) != 0)
  257. copySize |= (cmdbuf[cmdptr++] & 0xff) << 16;
  258. if (copySize == 0)
  259. copySize = 0x10000;
  260. return CMD_COPY;
  261. } else if (cmd != 0) {
  262. // Anything else the data is literal within the delta
  263. // itself. Page the entire thing into the cmdbuf, if
  264. // its not already there.
  265. //
  266. fill(cmd);
  267. copySize = cmd;
  268. return CMD_INSERT;
  269. } else {
  270. // cmd == 0 has been reserved for future encoding but
  271. // for now its not acceptable.
  272. //
  273. throw new CorruptObjectException(JGitText.get().unsupportedCommand0);
  274. }
  275. }
  276. private int have() {
  277. return cmdcnt - cmdptr;
  278. }
  279. private void seekBase() throws IOException {
  280. if (baseStream == null) {
  281. baseStream = openBase();
  282. if (getBaseSize() != baseSize)
  283. throw new CorruptObjectException(
  284. JGitText.get().baseLengthIncorrect);
  285. IO.skipFully(baseStream, copyOffset);
  286. baseOffset = copyOffset;
  287. } else if (baseOffset < copyOffset) {
  288. IO.skipFully(baseStream, copyOffset - baseOffset);
  289. baseOffset = copyOffset;
  290. } else if (baseOffset > copyOffset) {
  291. baseStream.close();
  292. baseStream = openBase();
  293. IO.skipFully(baseStream, copyOffset);
  294. baseOffset = copyOffset;
  295. }
  296. }
  297. }