View Javadoc
1   /*
2    * Copyright 2025 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.util.internal;
17  
18  import java.util.Arrays;
19  
20  public final class LongLongHashMap {
21      private static final int MASK_TEMPLATE = ~1;
22      private int mask;
23      private long[] array;
24      private int maxProbe;
25      private long zeroVal;
26      private final long emptyVal;
27  
28      public LongLongHashMap(long emptyVal) {
29          this.emptyVal = emptyVal;
30          zeroVal = emptyVal;
31          int initialSize = 32;
32          array = new long[initialSize];
33          mask = initialSize - 1;
34          computeMaskAndProbe();
35      }
36  
37      public LongLongHashMap(LongLongHashMap other) {
38          this.mask = other.mask;
39          this.array = Arrays.copyOf(other.array, other.array.length);
40          this.maxProbe = other.maxProbe;
41          this.zeroVal = other.zeroVal;
42          this.emptyVal = other.emptyVal;
43      }
44  
45      public long put(long key, long value) {
46          if (key == 0) {
47              long prev = zeroVal;
48              zeroVal = value;
49              return prev;
50          }
51  
52          for (;;) {
53              int index = index(key);
54              for (int i = 0; i < maxProbe; i++) {
55                  long existing = array[index];
56                  if (existing == key || existing == 0) {
57                      long prev = existing == 0? emptyVal : array[index + 1];
58                      array[index] = key;
59                      array[index + 1] = value;
60                      for (; i < maxProbe; i++) { // Nerf any existing misplaced entries.
61                          index = index + 2 & mask;
62                          if (array[index] == key) {
63                              array[index] = 0;
64                              prev = array[index + 1];
65                              break;
66                          }
67                      }
68                      return prev;
69                  }
70                  index = index + 2 & mask;
71              }
72              expand(); // Grow array and re-hash.
73          }
74      }
75  
76      public void remove(long key) {
77          if (key == 0) {
78              zeroVal = emptyVal;
79              return;
80          }
81          int index = index(key);
82          for (int i = 0; i < maxProbe; i++) {
83              long existing = array[index];
84              if (existing == key) {
85                  array[index] = 0;
86                  break;
87              }
88              index = index + 2 & mask;
89          }
90      }
91  
92      public long get(long key) {
93          if (key == 0) {
94              return zeroVal;
95          }
96          int index = index(key);
97          for (int i = 0; i < maxProbe; i++) {
98              long existing = array[index];
99              if (existing == key) {
100                 return array[index + 1];
101             }
102             index = index + 2 & mask;
103         }
104         return emptyVal;
105     }
106 
107     private int index(long key) {
108         // Hash with murmur64, and mask.
109         key ^= key >>> 33;
110         key *= 0xff51afd7ed558ccdL;
111         key ^= key >>> 33;
112         key *= 0xc4ceb9fe1a85ec53L;
113         key ^= key >>> 33;
114         return (int) key & mask;
115     }
116 
117     private void expand() {
118         long[] prev = array;
119         array = new long[prev.length * 2];
120         computeMaskAndProbe();
121         for (int i = 0; i < prev.length; i += 2) {
122             long key = prev[i];
123             if (key != 0) {
124                 long val = prev[i + 1];
125                 put(key, val);
126             }
127         }
128     }
129 
130     private void computeMaskAndProbe() {
131         int length = array.length;
132         mask = length - 1 & MASK_TEMPLATE;
133         maxProbe = (int) Math.log(length);
134     }
135 }