org.antlr.v4.runtime.dfa.HashEdgeMap Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2012 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD-3-Clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.runtime.dfa;
import org.antlr.v4.runtime.misc.NotNull;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicIntegerArray;
/**
*
* @author Sam Harwell
*/
public final class HashEdgeMap extends AbstractEdgeMap {
private static final int DEFAULT_MAX_SIZE = 2;
private final AtomicIntegerArray keys;
private final T[] values;
public HashEdgeMap(int minIndex, int maxIndex) {
this(minIndex, maxIndex, DEFAULT_MAX_SIZE);
}
@SuppressWarnings("unchecked")
public HashEdgeMap(int minIndex, int maxIndex, int maxSparseSize) {
super(minIndex, maxIndex);
this.keys = new AtomicIntegerArray(maxSparseSize);
this.values = (T[])new Object[maxSparseSize];
}
@SuppressWarnings("unchecked")
private HashEdgeMap(@NotNull HashEdgeMap map, int maxSparseSize) {
super(map.minIndex, map.maxIndex);
synchronized (map) {
if (maxSparseSize < map.values.length) {
throw new IllegalArgumentException();
}
keys = new AtomicIntegerArray(maxSparseSize);
values = (T[])new Object[maxSparseSize];
for (int i = 0; i < map.values.length; i++) {
int key = map.keys.get(i);
T value = map.values[i];
if (value != null) {
int bucket = bucket(key);
keys.set(bucket, key);
values[bucket] = value;
}
}
}
}
private static int bucket(int length, int key) {
// Note: this returns a valid array index even if key is outside the
// allowed range or the minIndex is negative.
return key & (length - 1);
}
private int bucket(int key) {
// Note: this returns a valid array index even if key is outside the
// allowed range or the minIndex is negative.
return key & (values.length - 1);
}
@NotNull
/*package*/ AtomicIntegerArray getKeys() {
return keys;
}
@NotNull
/*package*/ T[] getValues() {
return values;
}
@Override
public int size() {
int size = 0;
for (T edge : values) {
if (edge != null) {
size++;
}
}
return size;
}
@Override
public boolean isEmpty() {
return size() == 0;
}
@Override
public boolean containsKey(int key) {
return get(key) != null;
}
@Override
public T get(int key) {
int bucket = bucket(key);
// Read the value first
T value = values[bucket];
if (value == null || keys.get(bucket) != key) {
return null;
}
return value;
}
@Override
public AbstractEdgeMap put(int key, T value) {
if (key < minIndex || key > maxIndex) {
return this;
}
if (value == null) {
return remove(key);
}
synchronized (this) {
int bucket = bucket(key);
int currentKey = keys.get(bucket);
if (currentKey == key) {
values[bucket] = value;
return this;
}
T currentValue = values[bucket];
if (currentValue == null) {
// Write the key first
keys.set(bucket, key);
values[bucket] = value;
return this;
}
// Resize on collision
int newSize = values.length;
while (true) {
newSize *= 2;
if (newSize >= (maxIndex - minIndex + 1) / 2) {
ArrayEdgeMap arrayMap = new ArrayEdgeMap(minIndex, maxIndex);
arrayMap = arrayMap.putAll(this);
arrayMap.put(key, value);
return arrayMap;
}
// Check for another collision
if (bucket(newSize, currentKey) != bucket(newSize, key)) {
break;
}
}
HashEdgeMap resized = new HashEdgeMap(this, newSize);
resized.put(key, value);
return resized;
}
}
@Override
public HashEdgeMap remove(int key) {
if (get(key) == null) {
return this;
}
HashEdgeMap result = new HashEdgeMap(this, values.length);
int bucket = result.bucket(key);
result.keys.set(bucket, 0);
result.values[bucket] = null;
return result;
}
@Override
public AbstractEdgeMap clear() {
if (isEmpty()) {
return this;
}
return new EmptyEdgeMap(minIndex, maxIndex);
}
@Override
public Map toMap() {
if (isEmpty()) {
return Collections.emptyMap();
}
synchronized (this) {
Map result = new TreeMap();
for (int i = 0; i < values.length; i++) {
int key = keys.get(i);
T value = values[i];
if (value != null) {
result.put(key, value);
}
}
return result;
}
}
@Override
public Set> entrySet() {
return toMap().entrySet();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy