blob: 15f6b6475b77619164db4d76762707d209740c5f [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.openapi.vfs.newvfs.impl;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.vfs.newvfs.persistent.FSRecords;
import com.intellij.util.IntSLRUCache;
import com.intellij.util.containers.IntObjectLinkedMap;
import com.intellij.util.io.IOUtil;
import com.intellij.util.io.PersistentStringEnumerator;
import com.intellij.util.text.ByteArrayCharSequence;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author peter
*/
public class FileNameCache {
private static final PersistentStringEnumerator ourNames = FSRecords.getNames();
@SuppressWarnings("unchecked") private static final IntSLRUCache<IntObjectLinkedMap.MapEntry<CharSequence>>[] ourNameCache = new IntSLRUCache[16];
static {
final int protectedSize = 40000 / ourNameCache.length;
final int probationalSize = 20000 / ourNameCache.length;
for(int i = 0; i < ourNameCache.length; ++i) {
ourNameCache[i] = new IntSLRUCache<IntObjectLinkedMap.MapEntry<CharSequence>>(protectedSize, probationalSize);
}
}
public static int storeName(@NotNull String name) {
final int idx = FSRecords.getNameId(name);
cacheData(name, idx, calcStripeIdFromNameId(idx));
return idx;
}
@NotNull
private static IntObjectLinkedMap.MapEntry<CharSequence> cacheData(String name, int id, int stripe) {
if (name == null) {
ourNames.markCorrupted();
throw new RuntimeException("VFS name enumerator corrupted");
}
CharSequence rawName = convertToBytesIfAsciiString(name);
IntObjectLinkedMap.MapEntry<CharSequence> entry = new IntObjectLinkedMap.MapEntry<CharSequence>(id, rawName);
IntSLRUCache<IntObjectLinkedMap.MapEntry<CharSequence>> cache = ourNameCache[stripe];
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (cache) {
return cache.cacheEntry(entry);
}
}
private static int calcStripeIdFromNameId(int id) {
int h = id;
h -= (h<<6);
h ^= (h>>17);
h -= (h<<9);
h ^= (h<<4);
h -= (h<<3);
h ^= (h<<10);
h ^= (h>>15);
return h % ourNameCache.length;
}
@NotNull
private static CharSequence convertToBytesIfAsciiString(@NotNull String name) {
int length = name.length();
if (length == 0) return "";
if (!IOUtil.isAscii(name)) {
return new String(name); // So we don't hold whole char[] buffer of a lengthy path on JDK 6
}
byte[] bytes = new byte[length];
for (int i = 0; i < length; i++) {
bytes[i] = (byte)name.charAt(i);
}
return new ByteArrayCharSequence(bytes);
}
@NotNull
private static IntObjectLinkedMap.MapEntry<CharSequence> getEntry(int id) {
assert id > 0;
final int stripe = calcStripeIdFromNameId(id);
IntSLRUCache<IntObjectLinkedMap.MapEntry<CharSequence>> cache = ourNameCache[stripe];
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (cache) {
IntObjectLinkedMap.MapEntry<CharSequence> entry = cache.getCachedEntry(id);
if (entry != null) {
return entry;
}
}
return cacheData(FSRecords.getNameByNameId(id), id, stripe);
}
@NotNull
public static CharSequence getVFileName(int nameId) {
return getEntry(nameId).value;
}
@NotNull
static char[] appendPathOnFileSystem(int nameId, @Nullable VirtualFileSystemEntry parent, int accumulatedPathLength, @NotNull int[] positionRef) {
IntObjectLinkedMap.MapEntry<CharSequence> entry = getEntry(nameId);
CharSequence o = entry.value;
int nameLength = o.length();
boolean appendSlash = SystemInfo.isWindows && parent == null && nameLength == 2 && o.charAt(1) == ':';
char[] chars;
if (parent != null) {
chars = parent.appendPathOnFileSystem(accumulatedPathLength + 1 + nameLength, positionRef);
if (positionRef[0] > 0 && chars[positionRef[0] - 1] != '/') {
chars[positionRef[0]++] = '/';
}
}
else {
int rootPathLength = accumulatedPathLength + nameLength;
if (appendSlash) ++rootPathLength;
chars = new char[rootPathLength];
}
positionRef[0] = VirtualFileSystemEntry.copyString(chars, positionRef[0], o);
if (appendSlash) {
chars[positionRef[0]++] = '/';
}
return chars;
}
}