| /* |
| * Copyright (C) 2022, Tencent. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Distribution License v. 1.0 which is available at |
| * https://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| package org.eclipse.jgit.internal.storage.io; |
| |
| import org.eclipse.jgit.lib.Constants; |
| import org.eclipse.jgit.lib.ProgressMonitor; |
| |
| import java.io.IOException; |
| import java.io.InterruptedIOException; |
| import java.io.OutputStream; |
| import java.security.MessageDigest; |
| |
| /** |
| * An OutputStream that keeps a digest and checks every N bytes for |
| * cancellation. |
| */ |
| public class CancellableDigestOutputStream extends OutputStream { |
| |
| /** The OutputStream checks every this value for cancellation **/ |
| public static final int BYTES_TO_WRITE_BEFORE_CANCEL_CHECK = 128 * 1024; |
| |
| private final ProgressMonitor writeMonitor; |
| |
| private final OutputStream out; |
| |
| private final MessageDigest md = Constants.newMessageDigest(); |
| |
| private long count; |
| |
| private long checkCancelAt; |
| |
| /** |
| * Initialize a CancellableDigestOutputStream. |
| * |
| * @param writeMonitor |
| * monitor to update on output progress and check cancel. |
| * @param out |
| * target stream to receive all contents. |
| */ |
| public CancellableDigestOutputStream(ProgressMonitor writeMonitor, |
| OutputStream out) { |
| this.writeMonitor = writeMonitor; |
| this.out = out; |
| this.checkCancelAt = BYTES_TO_WRITE_BEFORE_CANCEL_CHECK; |
| } |
| |
| /** |
| * Get the monitor which is used to update on output progress and check |
| * cancel. |
| * |
| * @return the monitor |
| */ |
| public final ProgressMonitor getWriteMonitor() { |
| return writeMonitor; |
| } |
| |
| /** |
| * Obtain the current SHA-1 digest. |
| * |
| * @return SHA-1 digest |
| */ |
| public final byte[] getDigest() { |
| return md.digest(); |
| } |
| |
| /** |
| * Get total number of bytes written since stream start. |
| * |
| * @return total number of bytes written since stream start. |
| */ |
| public final long length() { |
| return count; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public final void write(int b) throws IOException { |
| if (checkCancelAt <= count) { |
| if (writeMonitor.isCancelled()) { |
| throw new InterruptedIOException(); |
| } |
| checkCancelAt = count + BYTES_TO_WRITE_BEFORE_CANCEL_CHECK; |
| } |
| |
| out.write(b); |
| md.update((byte) b); |
| count++; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public final void write(byte[] b, int off, int len) throws IOException { |
| while (0 < len) { |
| if (checkCancelAt <= count) { |
| if (writeMonitor.isCancelled()) { |
| throw new InterruptedIOException(); |
| } |
| checkCancelAt = count + BYTES_TO_WRITE_BEFORE_CANCEL_CHECK; |
| } |
| |
| int n = Math.min(len, BYTES_TO_WRITE_BEFORE_CANCEL_CHECK); |
| out.write(b, off, n); |
| md.update(b, off, n); |
| count += n; |
| |
| off += n; |
| len -= n; |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void flush() throws IOException { |
| out.flush(); |
| } |
| } |