blob: 94ce68c981ca6ec1f183831c2a2dccae658b8c1c [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* 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.intellij.util.io;
import com.intellij.util.containers.SLRUMap;
import jsr166e.extra.SequenceLock;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.concurrent.locks.Lock;
/**
* @author peter
*/
public class CachingEnumerator<Data> implements DataEnumerator<Data> {
private static final int STRIPE_POWER = 4;
private static final int STRIPE_COUNT = 1 << STRIPE_POWER;
private static final int STRIPE_MASK = STRIPE_COUNT - 1;
@SuppressWarnings("unchecked") private final SLRUMap<Integer, Integer>[] myHashcodeToIdCache = new SLRUMap[STRIPE_COUNT];
@SuppressWarnings("unchecked") private final SLRUMap<Integer, Data>[] myIdToStringCache = new SLRUMap[STRIPE_COUNT];
private final Lock[] myStripeLocks = new Lock[STRIPE_COUNT];
private final DataEnumerator<Data> myBase;
private final KeyDescriptor<Data> myDataDescriptor;
public CachingEnumerator(DataEnumerator<Data> base, KeyDescriptor<Data> dataDescriptor) {
myBase = base;
myDataDescriptor = dataDescriptor;
int protectedSize = 8192;
int probationalSize = 8192;
for(int i = 0; i < STRIPE_COUNT; ++i) {
myHashcodeToIdCache[i] = new SLRUMap<Integer, Integer>(protectedSize / STRIPE_COUNT, probationalSize / STRIPE_COUNT);
myIdToStringCache[i] = new SLRUMap<Integer, Data>(protectedSize / STRIPE_COUNT, probationalSize / STRIPE_COUNT);
myStripeLocks[i] = new SequenceLock();
}
}
public int enumerate(@Nullable Data value) throws IOException {
int valueHashCode =-1;
int stripe = -1;
if (myHashcodeToIdCache != null && value != null) {
valueHashCode = myDataDescriptor.getHashCode(value);
stripe = Math.abs(valueHashCode) & STRIPE_MASK;
Integer cachedId;
myStripeLocks[stripe].lock();
try {
cachedId = myHashcodeToIdCache[stripe].get(valueHashCode);
}
finally {
myStripeLocks[stripe].unlock();
}
if (cachedId != null) {
int stripe2 = idStripe(cachedId.intValue());
myStripeLocks[stripe2].lock();
try {
Data s = myIdToStringCache[stripe2].get(cachedId);
if (s != null && myDataDescriptor.isEqual(value, s)) return cachedId.intValue();
}
finally {
myStripeLocks[stripe2].unlock();
}
}
}
int enumerate = myBase.enumerate(value);
if (stripe != -1) {
Integer enumeratedInteger;
myStripeLocks[stripe].lock();
try {
enumeratedInteger = enumerate;
myHashcodeToIdCache[stripe].put(valueHashCode, enumeratedInteger);
} finally {
myStripeLocks[stripe].unlock();
}
int stripe2 = idStripe(enumerate);
myStripeLocks[stripe2].lock();
try {
myIdToStringCache[stripe2].put(enumeratedInteger, value);
} finally {
myStripeLocks[stripe2].unlock();
}
}
return enumerate;
}
private static int idStripe(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return Math.abs(h ^ (h >>> 7) ^ (h >>> 4)) & STRIPE_MASK;
}
@Nullable
public Data valueOf(int idx) throws IOException {
int stripe = -1;
if (myIdToStringCache != null) {
stripe = idStripe(idx);
myStripeLocks[stripe].lock();
try {
Data s = myIdToStringCache[stripe].get(idx);
if (s != null) return s;
}
finally {
myStripeLocks[stripe].unlock();
}
}
Data s = myBase.valueOf(idx);
if (stripe != -1 && s != null) {
myStripeLocks[stripe].lock();
try {
myIdToStringCache[stripe].put(idx, s);
}
finally {
myStripeLocks[stripe].unlock();
}
}
return s;
}
public void close() throws IOException {
for(int i = 0; i < myIdToStringCache.length; ++i) {
myStripeLocks[i].lock();
myIdToStringCache[i].clear();
myHashcodeToIdCache[i].clear();
myStripeLocks[i].unlock();
}
}
}