View Javadoc
1   /*
2    * Copyright (C) 2011 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  
15  package com.google.common.hash;
16  
17  import com.google.common.base.Preconditions;
18  import java.io.ByteArrayOutputStream;
19  import java.nio.ByteBuffer;
20  import java.nio.ByteOrder;
21  import java.nio.charset.Charset;
22  import java.util.Arrays;
23  
24  /**
25   * Skeleton implementation of {@link HashFunction}, appropriate for non-streaming algorithms. All
26   * the hash computation done using {@linkplain #newHasher()} are delegated to the {@linkplain
27   * #hashBytes(byte[], int, int)} method.
28   *
29   * @author Dimitris Andreou
30   */
31  abstract class AbstractNonStreamingHashFunction extends AbstractHashFunction {
32    @Override
33    public Hasher newHasher() {
34      return newHasher(32);
35    }
36  
37    @Override
38    public Hasher newHasher(int expectedInputSize) {
39      Preconditions.checkArgument(expectedInputSize >= 0);
40      return new BufferingHasher(expectedInputSize);
41    }
42  
43    @Override
44    public HashCode hashInt(int input) {
45      return hashBytes(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(input).array());
46    }
47  
48    @Override
49    public HashCode hashLong(long input) {
50      return hashBytes(ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(input).array());
51    }
52  
53    @Override
54    public HashCode hashUnencodedChars(CharSequence input) {
55      int len = input.length();
56      ByteBuffer buffer = ByteBuffer.allocate(len * 2).order(ByteOrder.LITTLE_ENDIAN);
57      for (int i = 0; i < len; i++) {
58        buffer.putChar(input.charAt(i));
59      }
60      return hashBytes(buffer.array());
61    }
62  
63    @Override
64    public HashCode hashString(CharSequence input, Charset charset) {
65      return hashBytes(input.toString().getBytes(charset));
66    }
67  
68    @Override
69    public abstract HashCode hashBytes(byte[] input, int off, int len);
70  
71    @Override
72    public HashCode hashBytes(ByteBuffer input) {
73      return newHasher(input.remaining()).putBytes(input).hash();
74    }
75  
76    /** In-memory stream-based implementation of Hasher. */
77    private final class BufferingHasher extends AbstractHasher {
78      final ExposedByteArrayOutputStream stream;
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, int off, int len) {
92        stream.write(bytes, off, len);
93        return this;
94      }
95  
96      @Override
97      public Hasher putBytes(ByteBuffer bytes) {
98        stream.write(bytes);
99        return this;
100     }
101 
102     @Override
103     public HashCode hash() {
104       return hashBytes(stream.byteArray(), 0, stream.length());
105     }
106   }
107 
108   // Just to access the byte[] without introducing an unnecessary copy
109   private static final class ExposedByteArrayOutputStream extends ByteArrayOutputStream {
110     ExposedByteArrayOutputStream(int expectedInputSize) {
111       super(expectedInputSize);
112     }
113 
114     void write(ByteBuffer input) {
115       int remaining = input.remaining();
116       if (count + remaining > buf.length) {
117         buf = Arrays.copyOf(buf, count + remaining);
118       }
119       input.get(buf, count, remaining);
120       count += remaining;
121     }
122 
123     byte[] byteArray() {
124       return buf;
125     }
126 
127     int length() {
128       return count;
129     }
130   }
131 }