001/* 002 * Copyright (C) 2013 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License 010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 011 * or implied. See the License for the specific language governing permissions and limitations under 012 * the License. 013 */ 014 015package com.google.common.hash; 016 017import static com.google.common.base.Preconditions.checkNotNull; 018import static java.util.Objects.requireNonNull; 019 020import com.google.common.annotations.Beta; 021import com.google.common.annotations.J2ktIncompatible; 022import com.google.errorprone.annotations.CanIgnoreReturnValue; 023import java.io.FilterInputStream; 024import java.io.IOException; 025import java.io.InputStream; 026 027/** 028 * An {@link InputStream} that maintains a hash of the data read from it. 029 * 030 * @author Qian Huang 031 * @since 16.0 032 */ 033@J2ktIncompatible 034@Beta 035public final class HashingInputStream extends FilterInputStream { 036 private final Hasher hasher; 037 038 /** 039 * Creates an input stream that hashes using the given {@link HashFunction} and delegates all data 040 * read from it to the underlying {@link InputStream}. 041 * 042 * <p>The {@link InputStream} should not be read from before or after the hand-off. 043 */ 044 public HashingInputStream(HashFunction hashFunction, InputStream in) { 045 super(checkNotNull(in)); 046 this.hasher = checkNotNull(hashFunction.newHasher()); 047 } 048 049 private InputStream in() { 050 return requireNonNull(in); // guaranteed safe by the constructor 051 } 052 053 /** 054 * Reads the next byte of data from the underlying input stream and updates the hasher with the 055 * byte read. 056 */ 057 @Override 058 @CanIgnoreReturnValue 059 public int read() throws IOException { 060 int b = in().read(); 061 if (b != -1) { 062 hasher.putByte((byte) b); 063 } 064 return b; 065 } 066 067 /** 068 * Reads the specified bytes of data from the underlying input stream and updates the hasher with 069 * the bytes read. 070 */ 071 @Override 072 @CanIgnoreReturnValue 073 public int read(byte[] bytes, int off, int len) throws IOException { 074 int numOfBytesRead = in().read(bytes, off, len); 075 if (numOfBytesRead != -1) { 076 hasher.putBytes(bytes, off, numOfBytesRead); 077 } 078 return numOfBytesRead; 079 } 080 081 /** 082 * mark() is not supported for HashingInputStream 083 * 084 * @return {@code false} always 085 */ 086 @Override 087 public boolean markSupported() { 088 return false; 089 } 090 091 /** mark() is not supported for HashingInputStream */ 092 @Override 093 public void mark(int readlimit) {} 094 095 /** 096 * reset() is not supported for HashingInputStream. 097 * 098 * @throws IOException this operation is not supported 099 */ 100 @Override 101 public void reset() throws IOException { 102 throw new IOException("reset not supported"); 103 } 104 105 /** 106 * Returns the {@link HashCode} based on the data read from this stream. The result is unspecified 107 * if this method is called more than once on the same instance. 108 */ 109 public HashCode hash() { 110 return hasher.hash(); 111 } 112}