1// Copyright 2011 Google Inc. All Rights Reserved.
2
3package com.google.common.hash;
4
5import com.google.common.base.Preconditions;
6import com.google.common.base.Throwables;
7
8import java.io.ByteArrayOutputStream;
9import java.io.IOException;
10
11/**
12 * Skeleton implementation of {@link HashFunction}, appropriate for non-streaming algorithms.
13 * All the hash computation done using {@linkplain #newHasher()} are delegated to the {@linkplain
14 * #hashBytes(byte[], int, int)} method.
15 *
16 * @author andreou@google.com (Dimitris Andreou)
17 */
18abstract class AbstractNonStreamingHashFunction implements HashFunction {
19  @Override
20  public Hasher newHasher() {
21    return new BufferingHasher(32);
22  }
23
24  @Override
25  public Hasher newHasher(int expectedInputSize) {
26    Preconditions.checkArgument(expectedInputSize >= 0);
27    return new BufferingHasher(expectedInputSize);
28  }
29
30  /**
31   * In-memory stream-based implementation of Hasher.
32   */
33  private final class BufferingHasher extends AbstractHasher {
34    final ExposedByteArrayOutputStream stream;
35    static final int BOTTOM_BYTE = 0xFF;
36
37    BufferingHasher(int expectedInputSize) {
38      this.stream = new ExposedByteArrayOutputStream(expectedInputSize);
39    }
40
41    @Override
42    public Hasher putByte(byte b) {
43      stream.write(b);
44      return this;
45    }
46
47    @Override
48    public Hasher putBytes(byte[] bytes) {
49      try {
50        stream.write(bytes);
51      } catch (IOException e) {
52        throw Throwables.propagate(e);
53      }
54      return this;
55    }
56
57    @Override
58    public Hasher putBytes(byte[] bytes, int off, int len) {
59      stream.write(bytes, off, len);
60      return this;
61    }
62
63    @Override
64    public Hasher putShort(short s) {
65      stream.write(s & BOTTOM_BYTE);
66      stream.write((s >>> 8)  & BOTTOM_BYTE);
67      return this;
68    }
69
70    @Override
71    public Hasher putInt(int i) {
72      stream.write(i & BOTTOM_BYTE);
73      stream.write((i >>> 8) & BOTTOM_BYTE);
74      stream.write((i >>> 16) & BOTTOM_BYTE);
75      stream.write((i >>> 24) & BOTTOM_BYTE);
76      return this;
77    }
78
79    @Override
80    public Hasher putLong(long l) {
81      for (int i = 0; i < 64; i += 8) {
82        stream.write((byte) ((l >>> i) & BOTTOM_BYTE));
83      }
84      return this;
85    }
86
87    @Override
88    public Hasher putChar(char c) {
89      stream.write(c & BOTTOM_BYTE);
90      stream.write((c >>> 8) & BOTTOM_BYTE);
91      return this;
92    }
93
94    @Override
95    public <T> Hasher putObject(T instance, Funnel<? super T> funnel) {
96      funnel.funnel(instance, this);
97      return this;
98    }
99
100    @Override
101    public HashCode hash() {
102      return hashBytes(stream.byteArray(), 0, stream.length());
103    }
104  }
105
106  // Just to access the byte[] without introducing an unnecessary copy
107  private static final class ExposedByteArrayOutputStream extends ByteArrayOutputStream {
108    ExposedByteArrayOutputStream(int expectedInputSize) {
109      super(expectedInputSize);
110    }
111    byte[] byteArray() {
112      return buf;
113    }
114    int length() {
115      return count;
116    }
117  }
118}
119