1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.google.common.hash;
18
19 import static com.google.common.base.Charsets.UTF_16LE;
20
21 import com.google.common.collect.Iterables;
22 import com.google.common.collect.Lists;
23 import com.google.common.hash.AbstractStreamingHashFunction.AbstractStreamingHasher;
24 import com.google.common.hash.HashTestUtils.RandomHasherAction;
25
26 import junit.framework.TestCase;
27
28 import java.io.ByteArrayOutputStream;
29 import java.nio.ByteBuffer;
30 import java.nio.ByteOrder;
31 import java.nio.charset.Charset;
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.List;
35 import java.util.Random;
36
37
38
39
40
41
42 public class AbstractStreamingHasherTest extends TestCase {
43 public void testBytes() {
44 Sink sink = new Sink(4);
45 byte[] expected = { 1, 2, 3, 4, 5, 6, 7, 8 };
46 sink.putByte((byte) 1);
47 sink.putBytes(new byte[] { 2, 3, 4, 5, 6 });
48 sink.putByte((byte) 7);
49 sink.putBytes(new byte[] {});
50 sink.putBytes(new byte[] { 8 });
51 sink.hash();
52 sink.assertInvariants(8);
53 sink.assertBytes(expected);
54 }
55
56 public void testShort() {
57 Sink sink = new Sink(4);
58 sink.putShort((short) 0x0201);
59 sink.hash();
60 sink.assertInvariants(2);
61 sink.assertBytes(new byte[] { 1, 2, 0, 0 });
62 }
63
64 public void testInt() {
65 Sink sink = new Sink(4);
66 sink.putInt(0x04030201);
67 sink.hash();
68 sink.assertInvariants(4);
69 sink.assertBytes(new byte[] { 1, 2, 3, 4 });
70 }
71
72 public void testLong() {
73 Sink sink = new Sink(8);
74 sink.putLong(0x0807060504030201L);
75 sink.hash();
76 sink.assertInvariants(8);
77 sink.assertBytes(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 });
78 }
79
80 public void testChar() {
81 Sink sink = new Sink(4);
82 sink.putChar((char) 0x0201);
83 sink.hash();
84 sink.assertInvariants(2);
85 sink.assertBytes(new byte[] { 1, 2, 0, 0 });
86 }
87
88 public void testString() {
89 Random random = new Random();
90 for (int i = 0; i < 100; i++) {
91 byte[] bytes = new byte[64];
92 random.nextBytes(bytes);
93 String s = new String(bytes, UTF_16LE);
94 assertEquals(
95 new Sink(4).putUnencodedChars(s).hash(),
96 new Sink(4).putBytes(s.getBytes(UTF_16LE)).hash());
97 assertEquals(
98 new Sink(4).putUnencodedChars(s).hash(),
99 new Sink(4).putString(s, UTF_16LE).hash());
100 }
101 }
102
103 public void testFloat() {
104 Sink sink = new Sink(4);
105 sink.putFloat(Float.intBitsToFloat(0x04030201));
106 sink.hash();
107 sink.assertInvariants(4);
108 sink.assertBytes(new byte[] { 1, 2, 3, 4 });
109 }
110
111 public void testDouble() {
112 Sink sink = new Sink(8);
113 sink.putDouble(Double.longBitsToDouble(0x0807060504030201L));
114 sink.hash();
115 sink.assertInvariants(8);
116 sink.assertBytes(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 });
117 }
118
119 public void testCorrectExceptions() {
120 Sink sink = new Sink(4);
121 try {
122 sink.putBytes(new byte[8], -1, 4);
123 fail();
124 } catch (IndexOutOfBoundsException ok) {}
125 try {
126 sink.putBytes(new byte[8], 0, 16);
127 fail();
128 } catch (IndexOutOfBoundsException ok) {}
129 try {
130 sink.putBytes(new byte[8], 0, -1);
131 fail();
132 } catch (IndexOutOfBoundsException ok) {}
133 }
134
135
136
137
138
139
140 public void testExhaustive() throws Exception {
141 Random random = new Random(0);
142 for (int totalInsertions = 0; totalInsertions < 200; totalInsertions++) {
143
144 List<Sink> sinks = Lists.newArrayList();
145 for (int chunkSize = 4; chunkSize <= 32; chunkSize++) {
146 for (int bufferSize = chunkSize; bufferSize <= chunkSize * 4; bufferSize += chunkSize) {
147
148 sinks.add(new Sink(chunkSize, bufferSize));
149
150
151
152 }
153 }
154
155 Control control = new Control();
156 Hasher controlSink = control.newHasher(1024);
157
158 Iterable<Hasher> sinksAndControl =
159 Iterables.concat(sinks, Collections.singleton(controlSink));
160 for (int insertion = 0; insertion < totalInsertions; insertion++) {
161 RandomHasherAction.pickAtRandom(random).performAction(random, sinksAndControl);
162 }
163
164
165 int intToPut = random.nextInt();
166 for (Hasher hasher : sinksAndControl) {
167 hasher.putInt(intToPut);
168 }
169 for (Sink sink : sinks) {
170 sink.hash();
171 }
172
173 byte[] expected = controlSink.hash().asBytes();
174 for (Sink sink : sinks) {
175 sink.assertInvariants(expected.length);
176 sink.assertBytes(expected);
177 }
178 }
179 }
180
181 private static class Sink extends AbstractStreamingHasher {
182 final int chunkSize;
183 final int bufferSize;
184 final ByteArrayOutputStream out = new ByteArrayOutputStream();
185
186 int processCalled = 0;
187 boolean remainingCalled = false;
188
189 Sink(int chunkSize, int bufferSize) {
190 super(chunkSize, bufferSize);
191 this.chunkSize = chunkSize;
192 this.bufferSize = bufferSize;
193 }
194
195 Sink(int chunkSize) {
196 super(chunkSize);
197 this.chunkSize = chunkSize;
198 this.bufferSize = chunkSize;
199 }
200
201 @Override HashCode makeHash() {
202 return HashCode.fromBytes(out.toByteArray());
203 }
204
205 @Override protected void process(ByteBuffer bb) {
206 processCalled++;
207 assertEquals(ByteOrder.LITTLE_ENDIAN, bb.order());
208 assertTrue(bb.remaining() >= chunkSize);
209 for (int i = 0; i < chunkSize; i++) {
210 out.write(bb.get());
211 }
212 }
213
214 @Override protected void processRemaining(ByteBuffer bb) {
215 assertFalse(remainingCalled);
216 remainingCalled = true;
217 assertEquals(ByteOrder.LITTLE_ENDIAN, bb.order());
218 assertTrue(bb.remaining() > 0);
219 assertTrue(bb.remaining() < bufferSize);
220 int before = processCalled;
221 super.processRemaining(bb);
222 int after = processCalled;
223 assertEquals(before + 1, after);
224 processCalled--;
225 }
226
227
228 void assertInvariants(int expectedBytes) {
229
230 assertEquals(out.toByteArray().length, ceilToMultiple(expectedBytes, chunkSize));
231 assertEquals(expectedBytes / chunkSize, processCalled);
232 assertEquals(expectedBytes % chunkSize != 0, remainingCalled);
233 }
234
235
236 private static int ceilToMultiple(int a, int b) {
237 int remainder = a % b;
238 return remainder == 0 ? a : a + b - remainder;
239 }
240
241 void assertBytes(byte[] expected) {
242 byte[] got = out.toByteArray();
243 for (int i = 0; i < expected.length; i++) {
244 assertEquals(expected[i], got[i]);
245 }
246 }
247 }
248
249
250 private static class Control extends AbstractNonStreamingHashFunction {
251 @Override
252 public HashCode hashBytes(byte[] input) {
253 return HashCode.fromBytes(input);
254 }
255
256 @Override
257 public HashCode hashBytes(byte[] input, int off, int len) {
258 return hashBytes(Arrays.copyOfRange(input, off, off + len));
259 }
260
261 @Override
262 public int bits() {
263 throw new UnsupportedOperationException();
264 }
265
266 @Override
267 public HashCode hashString(CharSequence input, Charset charset) {
268 throw new UnsupportedOperationException();
269 }
270
271 @Override
272 public HashCode hashLong(long input) {
273 throw new UnsupportedOperationException();
274 }
275
276 @Override
277 public HashCode hashInt(int input) {
278 throw new UnsupportedOperationException();
279 }
280 }
281 }