public static String getMD5(InputStream is, String name, long expectedLength,
			TaskMonitor monitor) throws IOException, CancelledException {
		try {
			long startms = System.currentTimeMillis();
			long prevElapsed = startms;

			monitor.initialize(expectedLength, "Hashing %s".formatted(name));

			MessageDigest messageDigest = MessageDigest.getInstance(HashUtilities.MD5_ALGORITHM);
			int bufSize = (int) Math.max(1024, Math.min(expectedLength, 1024 * 1024));
			byte[] buf = new byte[bufSize];
			int bytesRead;
			long totalBytesRead = 0;
			while ((bytesRead = is.read(buf)) >= 0) {
				messageDigest.update(buf, 0, bytesRead);
				totalBytesRead += bytesRead;
				monitor.increment(bytesRead);

				long now = System.currentTimeMillis();
				if (now - prevElapsed > 5000 /*5 seconds*/ && totalBytesRead > bufSize) {
					prevElapsed = now;
					long elapsed = now - startms;
					long rate = (long) (totalBytesRead / (elapsed / 1000f));
					monitor.setMessage(
						"Hashing %s %s/s".formatted(name, FileUtilities.formatLength(rate)));
				}
			}
			return NumericUtilities.convertBytesToString(messageDigest.digest());
		}
		catch (NoSuchAlgorithmException e) {
			throw new IOException(e);
		}
	}
Calculate the hash of an {@link InputStream}.

@param is {@link InputStream}
@param name of the inputstream
@param expectedLength the length of the inputstream
@param monitor {@link TaskMonitor} to update
@return md5 as a hex encoded string, never null
@throws IOException if error
@throws CancelledException if cancelled
Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/FSUtilities.java