1   /*
2    * RDFpro - An extensible tool for building stream-oriented RDF processing libraries.
3    * 
4    * Written in 2014 by Francesco Corcoglioniti with support by Marco Amadori, Michele Mostarda,
5    * Alessio Palmero Aprosio and Marco Rospocher. Contact info on http://rdfpro.fbk.eu/
6    * 
7    * To the extent possible under law, the authors have dedicated all copyright and related and
8    * neighboring rights to this software to the public domain worldwide. This software is
9    * distributed without any warranty.
10   * 
11   * You should have received a copy of the CC0 Public Domain Dedication along with this software.
12   * If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
13   */
14  package eu.fbk.rdfpro.util;
15  
16  import java.io.IOException;
17  import java.io.Serializable;
18  
19  public final class Hash implements Serializable, Comparable<Hash> {
20  
21      private static final long serialVersionUID = 1L;
22  
23      private final long high;
24  
25      private final long low;
26  
27      private Hash(final long high, final long low) {
28          this.high = high;
29          this.low = low;
30      }
31  
32      public static Hash combine(final Hash... hashes) {
33          if (hashes.length == 1) {
34              return hashes[0];
35          }
36          long hi = 0;
37          long lo = 0;
38          for (final Hash hash : hashes) {
39              hi ^= hash.getHigh() + 0x9e3779b9 + (hi << 6) + (hi >> 2);
40              lo ^= hash.getLow() + 0x9e3779b9 + (lo << 6) + (lo >> 2);
41          }
42          return fromLongs(hi, lo);
43      }
44  
45      public static Hash fromLongs(final long high, final long low) {
46          return new Hash(high, low);
47      }
48  
49      public static Hash murmur3(final CharSequence... args) {
50  
51          long h1 = 0;
52          long h2 = 0;
53          int length = 0;
54  
55          long l1 = 0;
56          long l2 = 0;
57          int index = 0;
58  
59          long cur = 0;
60          for (int i = 0; i < args.length; ++i) {
61              final boolean lastArg = i == args.length - 1;
62              final CharSequence arg = args[i];
63              for (int j = 0; j < arg.length(); ++j) {
64                  final long c = arg.charAt(j) & 0xFFFFL;
65                  cur = cur | c << index % 4 * 16;
66                  boolean process = false;
67                  if (lastArg && j == arg.length() - 1) {
68                      l1 = index <= 3 ? cur : l1;
69                      l2 = index > 3 ? cur : l2;
70                      cur = 0;
71                      process = true;
72                  } else if (index == 3) {
73                      l1 = cur;
74                      cur = 0;
75                  } else if (index == 7) {
76                      l2 = cur;
77                      cur = 0;
78                      process = true;
79                  }
80                  if (process) {
81                      l1 *= 0x87c37b91114253d5L;
82                      l1 = Long.rotateLeft(l1, 31);
83                      l1 *= 0x4cf5ad432745937fL;
84                      h1 ^= l1;
85                      h1 = Long.rotateLeft(h1, 27);
86                      h1 += h2;
87                      h1 = h1 * 5 + 0x52dce729;
88                      l2 *= 0x4cf5ad432745937fL;
89                      l2 = Long.rotateLeft(l2, 33);
90                      l2 *= 0x87c37b91114253d5L;
91                      h2 ^= l2;
92                      h2 = Long.rotateLeft(h2, 31);
93                      h2 += h1;
94                      h2 = h2 * 5 + 0x38495ab5;
95                      length += 16;
96                      l1 = 0;
97                      l2 = 0;
98                      index = 0;
99                      process = false;
100                 } else {
101                     ++index;
102                 }
103             }
104         }
105 
106         h1 ^= length;
107         h2 ^= length;
108         h1 += h2;
109         h2 += h1;
110 
111         h1 ^= h1 >>> 33;
112         h1 *= 0xff51afd7ed558ccdL;
113         h1 ^= h1 >>> 33;
114         h1 *= 0xc4ceb9fe1a85ec53L;
115         h1 ^= h1 >>> 33;
116 
117         h2 ^= h2 >>> 33;
118         h2 *= 0xff51afd7ed558ccdL;
119         h2 ^= h2 >>> 33;
120         h2 *= 0xc4ceb9fe1a85ec53L;
121         h2 ^= h2 >>> 33;
122 
123         h1 += h2;
124         h2 += h1;
125 
126         return new Hash(h1, h2);
127     }
128 
129     public long getHigh() {
130         return this.high;
131     }
132 
133     public long getLow() {
134         return this.low;
135     }
136 
137     @Override
138     public int compareTo(final Hash other) {
139         int result = Long.compare(this.high, other.high);
140         if (result == 0) {
141             result = Long.compare(this.low, other.low);
142         }
143         return result;
144     }
145 
146     @Override
147     public boolean equals(final Object object) {
148         if (object == this) {
149             return true;
150         }
151         if (!(object instanceof Hash)) {
152             return false;
153         }
154         final Hash other = (Hash) object;
155         return this.low == other.low && this.high == other.high;
156     }
157 
158     @Override
159     public int hashCode() {
160         final int hh = (int) (this.high >>> 32);
161         final int hl = (int) this.high;
162         final int lh = (int) (this.low >>> 32);
163         final int ll = (int) this.low;
164         return ((hh * 37 + hl) * 37 + lh) * 37 + ll;
165     }
166 
167     public void toString(final Appendable out) throws IOException {
168         toStringHelper(out, this.high);
169         toStringHelper(out, this.low);
170     }
171 
172     @Override
173     public String toString() {
174         try {
175             final StringBuilder builder = new StringBuilder(22);
176             toString(builder);
177             return builder.toString();
178         } catch (final IOException ex) {
179             throw new Error("Unexpected error (!)", ex);
180         }
181     }
182 
183     private static void toStringHelper(final Appendable out, final long l) throws IOException {
184         for (int i = 60; i >= 0; i -= 6) {
185             final int n = (int) (l >>> i) & 0x3F;
186             if (n < 26) {
187                 out.append((char) (65 + n));
188             } else if (n < 52) {
189                 out.append((char) (71 + n));
190             } else if (n < 62) {
191                 out.append((char) (n - 4));
192             } else if (n == 62) {
193                 out.append('_');
194             } else {
195                 out.append('-');
196             }
197         }
198     }
199 
200 }