Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

PackOutputStream.java 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. /*
  2. * Copyright (C) 2008-2009, Google Inc.
  3. * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> and others
  4. *
  5. * This program and the accompanying materials are made available under the
  6. * terms of the Eclipse Distribution License v. 1.0 which is available at
  7. * https://www.eclipse.org/org/documents/edl-v10.php.
  8. *
  9. * SPDX-License-Identifier: BSD-3-Clause
  10. */
  11. package org.eclipse.jgit.internal.storage.pack;
  12. import static org.eclipse.jgit.lib.Constants.OBJ_OFS_DELTA;
  13. import static org.eclipse.jgit.lib.Constants.OBJ_REF_DELTA;
  14. import static org.eclipse.jgit.lib.Constants.PACK_SIGNATURE;
  15. import java.io.IOException;
  16. import java.io.OutputStream;
  17. import java.security.MessageDigest;
  18. import org.eclipse.jgit.internal.JGitText;
  19. import org.eclipse.jgit.lib.Constants;
  20. import org.eclipse.jgit.lib.ProgressMonitor;
  21. import org.eclipse.jgit.util.NB;
  22. /**
  23. * Custom output stream to support
  24. * {@link org.eclipse.jgit.internal.storage.pack.PackWriter}.
  25. */
  26. public final class PackOutputStream extends OutputStream {
  27. private static final int BYTES_TO_WRITE_BEFORE_CANCEL_CHECK = 128 * 1024;
  28. private final ProgressMonitor writeMonitor;
  29. private final OutputStream out;
  30. private final PackWriter packWriter;
  31. private final MessageDigest md = Constants.newMessageDigest();
  32. private long count;
  33. private final byte[] headerBuffer = new byte[32];
  34. private final byte[] copyBuffer = new byte[64 << 10];
  35. private long checkCancelAt;
  36. private boolean ofsDelta;
  37. /**
  38. * Initialize a pack output stream.
  39. * <p>
  40. * This constructor is exposed to support debugging the JGit library only.
  41. * Application or storage level code should not create a PackOutputStream,
  42. * instead use {@link org.eclipse.jgit.internal.storage.pack.PackWriter},
  43. * and let the writer create the stream.
  44. *
  45. * @param writeMonitor
  46. * monitor to update on object output progress.
  47. * @param out
  48. * target stream to receive all object contents.
  49. * @param pw
  50. * packer that is going to perform the output.
  51. */
  52. public PackOutputStream(final ProgressMonitor writeMonitor,
  53. final OutputStream out, final PackWriter pw) {
  54. this.writeMonitor = writeMonitor;
  55. this.out = out;
  56. this.packWriter = pw;
  57. this.checkCancelAt = BYTES_TO_WRITE_BEFORE_CANCEL_CHECK;
  58. }
  59. /** {@inheritDoc} */
  60. @Override
  61. public final void write(int b) throws IOException {
  62. count++;
  63. out.write(b);
  64. md.update((byte) b);
  65. }
  66. /** {@inheritDoc} */
  67. @Override
  68. public final void write(byte[] b, int off, int len)
  69. throws IOException {
  70. while (0 < len) {
  71. final int n = Math.min(len, BYTES_TO_WRITE_BEFORE_CANCEL_CHECK);
  72. count += n;
  73. if (checkCancelAt <= count) {
  74. if (writeMonitor.isCancelled()) {
  75. throw new IOException(
  76. JGitText.get().packingCancelledDuringObjectsWriting);
  77. }
  78. checkCancelAt = count + BYTES_TO_WRITE_BEFORE_CANCEL_CHECK;
  79. }
  80. out.write(b, off, n);
  81. md.update(b, off, n);
  82. off += n;
  83. len -= n;
  84. }
  85. }
  86. /** {@inheritDoc} */
  87. @Override
  88. public void flush() throws IOException {
  89. out.flush();
  90. }
  91. final void writeFileHeader(int version, long objectCount)
  92. throws IOException {
  93. System.arraycopy(PACK_SIGNATURE, 0, headerBuffer, 0, 4);
  94. NB.encodeInt32(headerBuffer, 4, version);
  95. NB.encodeInt32(headerBuffer, 8, (int) objectCount);
  96. write(headerBuffer, 0, 12);
  97. ofsDelta = packWriter.isDeltaBaseAsOffset();
  98. }
  99. /**
  100. * Write one object.
  101. *
  102. * If the object was already written, this method does nothing and returns
  103. * quickly. This case occurs whenever an object was written out of order in
  104. * order to ensure the delta base occurred before the object that needs it.
  105. *
  106. * @param otp
  107. * the object to write.
  108. * @throws java.io.IOException
  109. * the object cannot be read from the object reader, or the
  110. * output stream is no longer accepting output. Caller must
  111. * examine the type of exception and possibly its message to
  112. * distinguish between these cases.
  113. */
  114. public final void writeObject(ObjectToPack otp) throws IOException {
  115. packWriter.writeObject(this, otp);
  116. }
  117. /**
  118. * Commits the object header onto the stream.
  119. * <p>
  120. * Once the header has been written, the object representation must be fully
  121. * output, or packing must abort abnormally.
  122. *
  123. * @param otp
  124. * the object to pack. Header information is obtained.
  125. * @param rawLength
  126. * number of bytes of the inflated content. For an object that is
  127. * in whole object format, this is the same as the object size.
  128. * For an object that is in a delta format, this is the size of
  129. * the inflated delta instruction stream.
  130. * @throws java.io.IOException
  131. * the underlying stream refused to accept the header.
  132. */
  133. @SuppressWarnings("ShortCircuitBoolean")
  134. public final void writeHeader(ObjectToPack otp, long rawLength)
  135. throws IOException {
  136. ObjectToPack b = otp.getDeltaBase();
  137. if (b != null && (b.isWritten() & ofsDelta)) { // Non-short-circuit logic is intentional
  138. int n = objectHeader(rawLength, OBJ_OFS_DELTA, headerBuffer);
  139. n = ofsDelta(count - b.getOffset(), headerBuffer, n);
  140. write(headerBuffer, 0, n);
  141. } else if (otp.isDeltaRepresentation()) {
  142. int n = objectHeader(rawLength, OBJ_REF_DELTA, headerBuffer);
  143. otp.getDeltaBaseId().copyRawTo(headerBuffer, n);
  144. write(headerBuffer, 0, n + 20);
  145. } else {
  146. int n = objectHeader(rawLength, otp.getType(), headerBuffer);
  147. write(headerBuffer, 0, n);
  148. }
  149. }
  150. private static final int objectHeader(long len, int type, byte[] buf) {
  151. byte b = (byte) ((type << 4) | (len & 0x0F));
  152. int n = 0;
  153. for (len >>>= 4; len != 0; len >>>= 7) {
  154. buf[n++] = (byte) (0x80 | b);
  155. b = (byte) (len & 0x7F);
  156. }
  157. buf[n++] = b;
  158. return n;
  159. }
  160. private static final int ofsDelta(long diff, byte[] buf, int p) {
  161. p += ofsDeltaVarIntLength(diff);
  162. int n = p;
  163. buf[--n] = (byte) (diff & 0x7F);
  164. while ((diff >>>= 7) != 0)
  165. buf[--n] = (byte) (0x80 | (--diff & 0x7F));
  166. return p;
  167. }
  168. private static final int ofsDeltaVarIntLength(long v) {
  169. int n = 1;
  170. for (; (v >>>= 7) != 0; n++)
  171. --v;
  172. return n;
  173. }
  174. /**
  175. * Get a temporary buffer writers can use to copy data with.
  176. *
  177. * @return a temporary buffer writers can use to copy data with.
  178. */
  179. public final byte[] getCopyBuffer() {
  180. return copyBuffer;
  181. }
  182. void endObject() {
  183. writeMonitor.update(1);
  184. }
  185. /**
  186. * Get total number of bytes written since stream start.
  187. *
  188. * @return total number of bytes written since stream start.
  189. */
  190. public final long length() {
  191. return count;
  192. }
  193. /** @return obtain the current SHA-1 digest. */
  194. final byte[] getDigest() {
  195. return md.digest();
  196. }
  197. }