blob: ca2095feec1ab842911f78dc813c3cb8bfb3e6da [file] [log] [blame]
/*
* 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();
}
}