View Javadoc
1   /*
2    * Copyright (C) 2011 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.hash;
18  
19  import com.google.common.base.Preconditions;
20  
21  import java.io.ByteArrayOutputStream;
22  import java.io.IOException;
23  import java.nio.charset.Charset;
24  
25  /**
26   * Skeleton implementation of {@link HashFunction}, appropriate for non-streaming algorithms.
27   * All the hash computation done using {@linkplain #newHasher()} are delegated to the {@linkplain
28   * #hashBytes(byte[], int, int)} method.
29   *
30   * @author Dimitris Andreou
31   */
32  abstract class AbstractNonStreamingHashFunction implements HashFunction {
33    @Override
34    public Hasher newHasher() {
35      return new BufferingHasher(32);
36    }
37  
38    @Override
39    public Hasher newHasher(int expectedInputSize) {
40      Preconditions.checkArgument(expectedInputSize >= 0);
41      return new BufferingHasher(expectedInputSize);
42    }
43  
44    @Override public <T> HashCode hashObject(T instance, Funnel<? super T> funnel) {
45      return newHasher().putObject(instance, funnel).hash();
46    }
47  
48    @Override public HashCode hashUnencodedChars(CharSequence input) {
49      int len = input.length();
50      Hasher hasher = newHasher(len * 2);
51      for (int i = 0; i < len; i++) {
52        hasher.putChar(input.charAt(i));
53      }
54      return hasher.hash();
55    }
56  
57    @Override public HashCode hashString(CharSequence input, Charset charset) {
58      return hashBytes(input.toString().getBytes(charset));
59    }
60  
61    @Override public HashCode hashInt(int input) {
62      return newHasher(4).putInt(input).hash();
63    }
64  
65    @Override public HashCode hashLong(long input) {
66      return newHasher(8).putLong(input).hash();
67    }
68  
69    @Override public HashCode hashBytes(byte[] input) {
70      return hashBytes(input, 0, input.length);
71    }
72  
73    /**
74     * In-memory stream-based implementation of Hasher.
75     */
76    private final class BufferingHasher extends AbstractHasher {
77      final ExposedByteArrayOutputStream stream;
78      static final int BOTTOM_BYTE = 0xFF;
79  
80      BufferingHasher(int expectedInputSize) {
81        this.stream = new ExposedByteArrayOutputStream(expectedInputSize);
82      }
83  
84      @Override
85      public Hasher putByte(byte b) {
86        stream.write(b);
87        return this;
88      }
89  
90      @Override
91      public Hasher putBytes(byte[] bytes) {
92        try {
93          stream.write(bytes);
94        } catch (IOException e) {
95          throw new RuntimeException(e);
96        }
97        return this;
98      }
99  
100     @Override
101     public Hasher putBytes(byte[] bytes, int off, int len) {
102       stream.write(bytes, off, len);
103       return this;
104     }
105 
106     @Override
107     public Hasher putShort(short s) {
108       stream.write(s & BOTTOM_BYTE);
109       stream.write((s >>> 8)  & BOTTOM_BYTE);
110       return this;
111     }
112 
113     @Override
114     public Hasher putInt(int i) {
115       stream.write(i & BOTTOM_BYTE);
116       stream.write((i >>> 8) & BOTTOM_BYTE);
117       stream.write((i >>> 16) & BOTTOM_BYTE);
118       stream.write((i >>> 24) & BOTTOM_BYTE);
119       return this;
120     }
121 
122     @Override
123     public Hasher putLong(long l) {
124       for (int i = 0; i < 64; i += 8) {
125         stream.write((byte) ((l >>> i) & BOTTOM_BYTE));
126       }
127       return this;
128     }
129 
130     @Override
131     public Hasher putChar(char c) {
132       stream.write(c & BOTTOM_BYTE);
133       stream.write((c >>> 8) & BOTTOM_BYTE);
134       return this;
135     }
136 
137     @Override
138     public <T> Hasher putObject(T instance, Funnel<? super T> funnel) {
139       funnel.funnel(instance, this);
140       return this;
141     }
142 
143     @Override
144     public HashCode hash() {
145       return hashBytes(stream.byteArray(), 0, stream.length());
146     }
147   }
148 
149   // Just to access the byte[] without introducing an unnecessary copy
150   private static final class ExposedByteArrayOutputStream extends ByteArrayOutputStream {
151     ExposedByteArrayOutputStream(int expectedInputSize) {
152       super(expectedInputSize);
153     }
154     byte[] byteArray() {
155       return buf;
156     }
157     int length() {
158       return count;
159     }
160   }
161 }