Coverage Summary for Class: CompactHashing (com.google.common.collect)
| Class | Class, % | Method, % | Line, % |
|---|---|---|---|
| CompactHashing | 0% (0/1) | 0% (0/11) | 0% (0/50) |
1 /* 2 * Copyright (C) 2019 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.collect; 18 19 import com.google.common.annotations.GwtIncompatible; 20 import com.google.common.base.Objects; 21 import com.google.common.primitives.Ints; 22 import java.util.Arrays; 23 import org.checkerframework.checker.nullness.qual.Nullable; 24 25 /** 26 * Helper classes and static methods for implementing compact hash-based collections. 27 * 28 * @author Jon Noack 29 */ 30 @GwtIncompatible 31 final class CompactHashing { 32 private CompactHashing() {} 33 34 /** Indicates blank table entries. */ 35 static final byte UNSET = 0; 36 37 /** Number of bits used to store the numbers of hash table bits (max 30). */ 38 private static final int HASH_TABLE_BITS_MAX_BITS = 5; 39 40 /** Use high bits of metadata for modification count. */ 41 static final int MODIFICATION_COUNT_INCREMENT = (1 << HASH_TABLE_BITS_MAX_BITS); 42 43 /** Bitmask that selects the low bits of metadata to get hashTableBits. */ 44 static final int HASH_TABLE_BITS_MASK = (1 << HASH_TABLE_BITS_MAX_BITS) - 1; 45 46 /** Maximum size of a compact hash-based collection (2^30 - 1 because 0 is UNSET). */ 47 static final int MAX_SIZE = Ints.MAX_POWER_OF_TWO - 1; 48 49 /** Default size of a compact hash-based collection. */ 50 static final int DEFAULT_SIZE = 3; 51 52 /** 53 * Minimum size of the hash table of a compact hash-based collection. Because small hash tables 54 * use a byte[], any smaller size uses the same amount of memory due to object padding. 55 */ 56 private static final int MIN_HASH_TABLE_SIZE = 4; 57 58 private static final int BYTE_MAX_SIZE = 1 << Byte.SIZE; // 2^8 = 256 59 private static final int BYTE_MASK = (1 << Byte.SIZE) - 1; // 2^8 - 1 = 255 60 61 private static final int SHORT_MAX_SIZE = 1 << Short.SIZE; // 2^16 = 65_536 62 private static final int SHORT_MASK = (1 << Short.SIZE) - 1; // 2^16 - 1 = 65_535 63 64 /** 65 * Returns the power of 2 hashtable size required to hold the expected number of items or the 66 * minimum hashtable size, whichever is greater. 67 */ 68 static int tableSize(int expectedSize) { 69 // We use entries next == 0 to indicate UNSET, so actual capacity is 1 less than requested. 70 return Math.max(MIN_HASH_TABLE_SIZE, Hashing.closedTableSize(expectedSize + 1, 1.0f)); 71 } 72 73 /** Creates and returns a properly-sized array with the given number of buckets. */ 74 static Object createTable(int buckets) { 75 if (buckets < 2 76 || buckets > Ints.MAX_POWER_OF_TWO 77 || Integer.highestOneBit(buckets) != buckets) { 78 throw new IllegalArgumentException("must be power of 2 between 2^1 and 2^30: " + buckets); 79 } 80 if (buckets <= BYTE_MAX_SIZE) { 81 return new byte[buckets]; 82 } else if (buckets <= SHORT_MAX_SIZE) { 83 return new short[buckets]; 84 } else { 85 return new int[buckets]; 86 } 87 } 88 89 static void tableClear(Object table) { 90 if (table instanceof byte[]) { 91 Arrays.fill((byte[]) table, (byte) 0); 92 } else if (table instanceof short[]) { 93 Arrays.fill((short[]) table, (short) 0); 94 } else { 95 Arrays.fill((int[]) table, 0); 96 } 97 } 98 99 static int tableGet(Object table, int index) { 100 if (table instanceof byte[]) { 101 return ((byte[]) table)[index] & BYTE_MASK; // unsigned read 102 } else if (table instanceof short[]) { 103 return ((short[]) table)[index] & SHORT_MASK; // unsigned read 104 } else { 105 return ((int[]) table)[index]; 106 } 107 } 108 109 static void tableSet(Object table, int index, int entry) { 110 if (table instanceof byte[]) { 111 ((byte[]) table)[index] = (byte) entry; // unsigned write 112 } else if (table instanceof short[]) { 113 ((short[]) table)[index] = (short) entry; // unsigned write 114 } else { 115 ((int[]) table)[index] = entry; 116 } 117 } 118 119 /** 120 * Returns a larger power of 2 hashtable size given the current mask. 121 * 122 * <p>For hashtable sizes less than or equal to 32, the returned power of 2 is 4x the current 123 * hashtable size to reduce expensive rehashing. Otherwise the returned power of 2 is 2x the 124 * current hashtable size. 125 */ 126 static int newCapacity(int mask) { 127 return ((mask < 32) ? 4 : 2) * (mask + 1); 128 } 129 130 /** Returns the hash prefix given the current mask. */ 131 static int getHashPrefix(int value, int mask) { 132 return value & ~mask; 133 } 134 135 /** Returns the index, or 0 if the entry is "null". */ 136 static int getNext(int entry, int mask) { 137 return entry & mask; 138 } 139 140 /** Returns a new value combining the prefix and suffix using the given mask. */ 141 static int maskCombine(int prefix, int suffix, int mask) { 142 return (prefix & ~mask) | (suffix & mask); 143 } 144 145 static int remove( 146 @Nullable Object key, 147 @Nullable Object value, 148 int mask, 149 Object table, 150 int[] entries, 151 Object[] keys, 152 Object @Nullable [] values) { 153 int hash = Hashing.smearedHash(key); 154 int tableIndex = hash & mask; 155 int next = tableGet(table, tableIndex); 156 if (next == UNSET) { 157 return -1; 158 } 159 int hashPrefix = getHashPrefix(hash, mask); 160 int lastEntryIndex = -1; 161 do { 162 int entryIndex = next - 1; 163 int entry = entries[entryIndex]; 164 if (getHashPrefix(entry, mask) == hashPrefix 165 && Objects.equal(key, keys[entryIndex]) 166 && (values == null || Objects.equal(value, values[entryIndex]))) { 167 int newNext = getNext(entry, mask); 168 if (lastEntryIndex == -1) { 169 // we need to update the root link from table[] 170 tableSet(table, tableIndex, newNext); 171 } else { 172 // we need to update the link from the chain 173 entries[lastEntryIndex] = maskCombine(entries[lastEntryIndex], newNext, mask); 174 } 175 176 return entryIndex; 177 } 178 lastEntryIndex = entryIndex; 179 next = getNext(entry, mask); 180 } while (next != UNSET); 181 return -1; 182 } 183 }