View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  
21  /*
22   * Licensed under the Apache License, Version 2.0 (the "License");
23   * you may not use this file except in compliance with the License.
24   * You may obtain a copy of the License at
25   *
26   *     http://www.apache.org/licenses/LICENSE-2.0
27   *
28   * Unless required by applicable law or agreed to in writing, software
29   * distributed under the License is distributed on an "AS IS" BASIS,
30   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31   * See the License for the specific language governing permissions and
32   * limitations under the License.
33   */
34  package org.waarp.compress.zstdunsafe;
35  
36  import static java.lang.Long.*;
37  
38  // forked from https://github.com/airlift/slice
39  final class XxHash64 {
40    private static final long PRIME64_1 = 0x9E3779B185EBCA87L;
41    private static final long PRIME64_2 = 0xC2B2AE3D27D4EB4FL;
42    private static final long PRIME64_3 = 0x165667B19E3779F9L;
43    private static final long PRIME64_4 = 0x85EBCA77C2b2AE63L;
44    private static final long PRIME64_5 = 0x27D4EB2F165667C5L;
45  
46    private XxHash64() {
47    }
48  
49    public static long hash(final long seed, final Object base,
50                            final long address, final int length) {
51      long hash;
52      if (length >= 32) {
53        hash = updateBody(seed, base, address, length);
54      } else {
55        hash = seed + PRIME64_5;
56      }
57  
58      hash += length;
59  
60      // round to the closest 32 byte boundary
61      // this is the point up to which updateBody() processed
62      final int index = length & 0xFFFFFFE0;
63  
64      return updateTail(hash, base, address, index, length);
65    }
66  
67    private static long updateTail(long hash, final Object base,
68                                   final long address, int index,
69                                   final int length) {
70      while (index <= length - 8) {
71        hash = updateTail(hash, UnsafeUtil.UNSAFE.getLong(base, address + index));
72        index += 8;
73      }
74  
75      if (index <= length - 4) {
76        hash = updateTail(hash, UnsafeUtil.UNSAFE.getInt(base, address + index));
77        index += 4;
78      }
79  
80      while (index < length) {
81        hash = updateTail(hash, UnsafeUtil.UNSAFE.getByte(base, address + index));
82        index++;
83      }
84  
85      hash = finalShuffle(hash);
86  
87      return hash;
88    }
89  
90    private static long updateBody(final long seed, final Object base,
91                                   long address, final int length) {
92      long v1 = seed + PRIME64_1 + PRIME64_2;
93      long v2 = seed + PRIME64_2;
94      long v3 = seed;
95      long v4 = seed - PRIME64_1;
96  
97      int remaining = length;
98      while (remaining >= 32) {
99        v1 = mix(v1, UnsafeUtil.UNSAFE.getLong(base, address));
100       v2 = mix(v2, UnsafeUtil.UNSAFE.getLong(base, address + 8));
101       v3 = mix(v3, UnsafeUtil.UNSAFE.getLong(base, address + 16));
102       v4 = mix(v4, UnsafeUtil.UNSAFE.getLong(base, address + 24));
103 
104       address += 32;
105       remaining -= 32;
106     }
107 
108     long hash = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) +
109                 rotateLeft(v4, 18);
110 
111     hash = update(hash, v1);
112     hash = update(hash, v2);
113     hash = update(hash, v3);
114     hash = update(hash, v4);
115 
116     return hash;
117   }
118 
119   private static long mix(final long current, final long value) {
120     return rotateLeft(current + value * PRIME64_2, 31) * PRIME64_1;
121   }
122 
123   private static long update(final long hash, final long value) {
124     final long temp = hash ^ mix(0, value);
125     return temp * PRIME64_1 + PRIME64_4;
126   }
127 
128   private static long updateTail(final long hash, final long value) {
129     final long temp = hash ^ mix(0, value);
130     return rotateLeft(temp, 27) * PRIME64_1 + PRIME64_4;
131   }
132 
133   private static long updateTail(final long hash, final int value) {
134     final long unsigned = value & 0xFFFFFFFFL;
135     final long temp = hash ^ (unsigned * PRIME64_1);
136     return rotateLeft(temp, 23) * PRIME64_2 + PRIME64_3;
137   }
138 
139   private static long updateTail(final long hash, final byte value) {
140     final int unsigned = value & 0xFF;
141     final long temp = hash ^ (unsigned * PRIME64_5);
142     return rotateLeft(temp, 11) * PRIME64_1;
143   }
144 
145   private static long finalShuffle(long hash) {
146     hash ^= hash >>> 33;
147     hash *= PRIME64_2;
148     hash ^= hash >>> 29;
149     hash *= PRIME64_3;
150     hash ^= hash >>> 32;
151     return hash;
152   }
153 }