Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,12 @@ public static void registerKnownKeys(Collection<String> keys) {
knownTable = tbl;
}

private static String deduplicateKey(String key) {
private static String deduplicateKey(String key, int hash) {
int[] tbl = knownTable;
if (tbl == null) {
return key;
}
int idx = tbl[caseInsensitiveHash(key) & knownMask];
int idx = tbl[hash & knownMask];
while (idx != EMPTY) {
if (knownEntries[idx].equalsIgnoreCase(key)) {
return knownEntries[idx];
Expand Down Expand Up @@ -142,18 +142,31 @@ private static int tableSizeFor(int cap) {
static int caseInsensitiveHash(String key) {
int h = 0;
for (int i = 0, len = key.length(); i < len; i++) {
// two-step fold matches String.equalsIgnoreCase / CASE_INSENSITIVE_ORDER
h = 31 * h + Character.toLowerCase(Character.toUpperCase(key.charAt(i)));
char c = key.charAt(i);
if (c >= 'A' && c <= 'Z') {
c += 32; // fast ASCII upper-to-lower
} else if (c >= 128) {
// full Unicode two-step fold for non-ASCII
c = Character.toLowerCase(Character.toUpperCase(c));
}
h = 31 * h + c;
}
return h ^ (h >>> 16);
}

private int bucketIndex(String key) {
return caseInsensitiveHash(key) & (table.length - 1);
private int findIndex(String key) {
int idx = table[caseInsensitiveHash(key) & (table.length - 1)];
while (idx != EMPTY) {
if (keys[idx].equalsIgnoreCase(key)) {
return idx;
}
idx = chainNext[idx];
}
return EMPTY;
}

private int findIndex(String key) {
int idx = table[bucketIndex(key)];
private int findIndex(String key, int hash) {
int idx = table[hash & (table.length - 1)];
while (idx != EMPTY) {
if (keys[idx].equalsIgnoreCase(key)) {
return idx;
Expand Down Expand Up @@ -186,15 +199,17 @@ public boolean containsValue(Object value) {

@Override
public Object put(String key, Object value) {
key = deduplicateKey(key);
int idx = findIndex(key);
int hash = caseInsensitiveHash(key);
key = deduplicateKey(key, hash);
int idx = findIndex(key, hash);
if (idx != EMPTY) {
Object old = values[idx];
values[idx] = value;
return old;
}
if (size >= threshold) {
resize(table.length * 2);
// table length changed, but hash is still valid
}
if (usedSlots >= keys.length) {
int newCap = keys.length + (keys.length >> 1);
Expand All @@ -205,7 +220,7 @@ public Object put(String key, Object value) {
int slot = usedSlots++;
keys[slot] = key;
values[slot] = value;
int b = bucketIndex(key);
int b = hash & (table.length - 1);
chainNext[slot] = table[b];
table[b] = slot;
size++;
Expand All @@ -214,18 +229,32 @@ public Object put(String key, Object value) {

@Override
public Object remove(Object key) {
int idx = findIndex((String) key);
if (idx != EMPTY) {
Object old = values[idx];
removeByIndex(idx);
return old;
int hash = caseInsensitiveHash((String) key);
int b = hash & (table.length - 1);
int prev = EMPTY;
int cur = table[b];
while (cur != EMPTY) {
if (keys[cur].equalsIgnoreCase((String) key)) {
Object old = values[cur];
if (prev == EMPTY) {
table[b] = chainNext[cur];
} else {
chainNext[prev] = chainNext[cur];
}
keys[cur] = null;
values[cur] = null;
size--;
return old;
}
prev = cur;
cur = chainNext[cur];
}
return null;
}

private void removeByIndex(int idx) {
String key = keys[idx];
int b = bucketIndex(key);
int b = caseInsensitiveHash(key) & (table.length - 1);
int prev = EMPTY;
int cur = table[b];
while (cur != EMPTY) {
Expand Down