View Javadoc
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  
11  package org.eclipse.jgit.internal.storage.io;
12  
13  import org.eclipse.jgit.lib.Constants;
14  import org.eclipse.jgit.lib.ProgressMonitor;
15  
16  import java.io.IOException;
17  import java.io.InterruptedIOException;
18  import java.io.OutputStream;
19  import java.security.MessageDigest;
20  
21  /**
22   * An OutputStream that keeps a digest and checks every N bytes for
23   * cancellation.
24   */
25  public class CancellableDigestOutputStream extends OutputStream {
26  
27  	/** The OutputStream checks every this value for cancellation **/
28  	public static final int BYTES_TO_WRITE_BEFORE_CANCEL_CHECK = 128 * 1024;
29  
30  	private final ProgressMonitor writeMonitor;
31  
32  	private final OutputStream out;
33  
34  	private final MessageDigest md = Constants.newMessageDigest();
35  
36  	private long count;
37  
38  	private long checkCancelAt;
39  
40  	/**
41  	 * Initialize a CancellableDigestOutputStream.
42  	 *
43  	 * @param writeMonitor
44  	 *            monitor to update on output progress and check cancel.
45  	 * @param out
46  	 *            target stream to receive all contents.
47  	 */
48  	public CancellableDigestOutputStream(ProgressMonitor writeMonitor,
49  			OutputStream out) {
50  		this.writeMonitor = writeMonitor;
51  		this.out = out;
52  		this.checkCancelAt = BYTES_TO_WRITE_BEFORE_CANCEL_CHECK;
53  	}
54  
55  	/**
56  	 * Get the monitor which is used to update on output progress and check
57  	 * cancel.
58  	 *
59  	 * @return the monitor
60  	 */
61  	public final ProgressMonitor getWriteMonitor() {
62  		return writeMonitor;
63  	}
64  
65  	/**
66  	 * Obtain the current SHA-1 digest.
67  	 *
68  	 * @return SHA-1 digest
69  	 */
70  	public final byte[] getDigest() {
71  		return md.digest();
72  	}
73  
74  	/**
75  	 * Get total number of bytes written since stream start.
76  	 *
77  	 * @return total number of bytes written since stream start.
78  	 */
79  	public final long length() {
80  		return count;
81  	}
82  
83  	/** {@inheritDoc} */
84  	@Override
85  	public final void write(int b) throws IOException {
86  		if (checkCancelAt <= count) {
87  			if (writeMonitor.isCancelled()) {
88  				throw new InterruptedIOException();
89  			}
90  			checkCancelAt = count + BYTES_TO_WRITE_BEFORE_CANCEL_CHECK;
91  		}
92  
93  		out.write(b);
94  		md.update((byte) b);
95  		count++;
96  	}
97  
98  	/** {@inheritDoc} */
99  	@Override
100 	public final void write(byte[] b, int off, int len) throws IOException {
101 		while (0 < len) {
102 			if (checkCancelAt <= count) {
103 				if (writeMonitor.isCancelled()) {
104 					throw new InterruptedIOException();
105 				}
106 				checkCancelAt = count + BYTES_TO_WRITE_BEFORE_CANCEL_CHECK;
107 			}
108 
109 			int n = Math.min(len, BYTES_TO_WRITE_BEFORE_CANCEL_CHECK);
110 			out.write(b, off, n);
111 			md.update(b, off, n);
112 			count += n;
113 
114 			off += n;
115 			len -= n;
116 		}
117 	}
118 
119 	/** {@inheritDoc} */
120 	@Override
121 	public void flush() throws IOException {
122 		out.flush();
123 	}
124 }