blob: 7181b67bc5119689b765fdf7eddd2536201255d4 [file] [log] [blame]
// Copyright 2018 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.lib.actions;
import com.google.common.base.Preconditions;
import java.util.Arrays;
import javax.annotation.Nullable;
/**
* Helper for {@link MetadataProvider} implementations.
*
* <p>Allows {@link FileArtifactValue} lookups by exec path or {@link ActionInput}. <i>Also</i>
* allows {@link ActionInput} to be looked up by exec path.
*
* <p>This class is thread-compatible.
*/
public final class ActionInputMap implements MetadataProvider {
private ActionInput[] keys;
private FileArtifactValue[] values;
/** Number of contained elements. */
private int size;
/** Length of keys = 2^(numBits+1) */
private int numBits;
/** Mask to use to perform the modulo operation since the table size is a power of 2. */
private int mask;
public ActionInputMap(int sizeHint) {
this.numBits = 1;
while ((1 << numBits) <= sizeHint) {
++numBits;
}
this.mask = (1 << numBits) - 1;
this.keys = new ActionInput[1 << numBits];
this.values = new FileArtifactValue[1 << numBits];
this.size = 0;
}
@Nullable
@Override
public FileArtifactValue getMetadata(ActionInput input) {
return getMetadata(input.getExecPathString());
}
@Nullable
public FileArtifactValue getMetadata(String execPathString) {
int hashCode = execPathString.hashCode();
int probe = getProbe(hashCode);
while (keys[probe] != null) {
String execPath = keys[probe].getExecPathString();
if (hashCode == execPath.hashCode() && execPath.equals(execPathString)) {
return values[probe];
}
probe = incProbe(probe);
}
return null;
}
@Nullable
@Override
public ActionInput getInput(String execPathString) {
int hashCode = execPathString.hashCode();
int probe = getProbe(hashCode);
while (keys[probe] != null) {
String execPath = keys[probe].getExecPathString();
if (hashCode == execPath.hashCode() && execPath.equals(execPathString)) {
return keys[probe];
}
probe = incProbe(probe);
}
return null;
}
/** Count of contained entries. */
public int size() {
return size;
}
/** @return true if an entry was added, false if the map already contains {@code input} */
public boolean put(ActionInput input, FileArtifactValue metadata) {
Preconditions.checkNotNull(input);
if (size * 2 >= keys.length) {
resize();
}
return putImpl(input, metadata);
}
public void clear() {
Arrays.fill(keys, null);
Arrays.fill(values, null);
size = 0;
}
private void resize() {
ActionInput[] oldKeys = keys;
FileArtifactValue[] oldValues = values;
keys = new ActionInput[keys.length * 2];
values = new FileArtifactValue[values.length * 2];
++numBits;
mask = (1 << numBits) - 1;
for (int i = 0; i < oldKeys.length; i++) {
ActionInput key = oldKeys[i];
if (key == null) {
continue;
}
int hashCode = key.getExecPathString().hashCode();
int probe = getProbe(hashCode);
while (true) {
// Only checks for empty slots because all map keys are known to be unique.
if (keys[probe] == null) {
keys[probe] = key;
values[probe] = oldValues[i];
break;
}
probe = incProbe(probe);
}
}
}
/**
* Unlike the public version, this doesn't resize.
*
* <p>REQUIRES: there are free positions in {@link keys}.
*/
private boolean putImpl(ActionInput key, FileArtifactValue value) {
int hashCode = key.getExecPathString().hashCode();
int probe = getProbe(hashCode);
while (true) {
ActionInput next = keys[probe];
if (next == null) {
keys[probe] = key;
values[probe] = value;
++size;
return true;
}
if (hashCode == next.getExecPathString().hashCode()
&& next.getExecPathString().equals(key.getExecPathString())) {
return false; // already present
}
probe = incProbe(probe);
}
}
private int getProbe(int hashCode) {
return hashCode & mask;
}
private int incProbe(int probe) {
return (probe + 1) & mask;
}
}