CancellableDigestOutputStream.java

  1. /*
  2.  * Copyright (C) 2022, Tencent.
  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.io;

  11. import org.eclipse.jgit.lib.Constants;
  12. import org.eclipse.jgit.lib.ProgressMonitor;

  13. import java.io.IOException;
  14. import java.io.InterruptedIOException;
  15. import java.io.OutputStream;
  16. import java.security.MessageDigest;

  17. /**
  18.  * An OutputStream that keeps a digest and checks every N bytes for
  19.  * cancellation.
  20.  */
  21. public class CancellableDigestOutputStream extends OutputStream {

  22.     /** The OutputStream checks every this value for cancellation **/
  23.     public static final int BYTES_TO_WRITE_BEFORE_CANCEL_CHECK = 128 * 1024;

  24.     private final ProgressMonitor writeMonitor;

  25.     private final OutputStream out;

  26.     private final MessageDigest md = Constants.newMessageDigest();

  27.     private long count;

  28.     private long checkCancelAt;

  29.     /**
  30.      * Initialize a CancellableDigestOutputStream.
  31.      *
  32.      * @param writeMonitor
  33.      *            monitor to update on output progress and check cancel.
  34.      * @param out
  35.      *            target stream to receive all contents.
  36.      */
  37.     public CancellableDigestOutputStream(ProgressMonitor writeMonitor,
  38.             OutputStream out) {
  39.         this.writeMonitor = writeMonitor;
  40.         this.out = out;
  41.         this.checkCancelAt = BYTES_TO_WRITE_BEFORE_CANCEL_CHECK;
  42.     }

  43.     /**
  44.      * Get the monitor which is used to update on output progress and check
  45.      * cancel.
  46.      *
  47.      * @return the monitor
  48.      */
  49.     public final ProgressMonitor getWriteMonitor() {
  50.         return writeMonitor;
  51.     }

  52.     /**
  53.      * Obtain the current SHA-1 digest.
  54.      *
  55.      * @return SHA-1 digest
  56.      */
  57.     public final byte[] getDigest() {
  58.         return md.digest();
  59.     }

  60.     /**
  61.      * Get total number of bytes written since stream start.
  62.      *
  63.      * @return total number of bytes written since stream start.
  64.      */
  65.     public final long length() {
  66.         return count;
  67.     }

  68.     /** {@inheritDoc} */
  69.     @Override
  70.     public final void write(int b) throws IOException {
  71.         if (checkCancelAt <= count) {
  72.             if (writeMonitor.isCancelled()) {
  73.                 throw new InterruptedIOException();
  74.             }
  75.             checkCancelAt = count + BYTES_TO_WRITE_BEFORE_CANCEL_CHECK;
  76.         }

  77.         out.write(b);
  78.         md.update((byte) b);
  79.         count++;
  80.     }

  81.     /** {@inheritDoc} */
  82.     @Override
  83.     public final void write(byte[] b, int off, int len) throws IOException {
  84.         while (0 < len) {
  85.             if (checkCancelAt <= count) {
  86.                 if (writeMonitor.isCancelled()) {
  87.                     throw new InterruptedIOException();
  88.                 }
  89.                 checkCancelAt = count + BYTES_TO_WRITE_BEFORE_CANCEL_CHECK;
  90.             }

  91.             int n = Math.min(len, BYTES_TO_WRITE_BEFORE_CANCEL_CHECK);
  92.             out.write(b, off, n);
  93.             md.update(b, off, n);
  94.             count += n;

  95.             off += n;
  96.             len -= n;
  97.         }
  98.     }

  99.     /** {@inheritDoc} */
  100.     @Override
  101.     public void flush() throws IOException {
  102.         out.flush();
  103.     }
  104. }