1
2
3
4
5
6
7
8
9
10
11
12
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 }