1 package com
.intellij
.util
.indexing
;
3 import com
.intellij
.openapi
.diagnostic
.Logger
;
4 import com
.intellij
.openapi
.util
.io
.FileUtil
;
5 import com
.intellij
.util
.CommonProcessors
;
6 import com
.intellij
.util
.Processor
;
7 import com
.intellij
.util
.containers
.SLRUCache
;
8 import com
.intellij
.util
.io
.DataExternalizer
;
9 import com
.intellij
.util
.io
.DataInputOutputUtil
;
10 import com
.intellij
.util
.io
.KeyDescriptor
;
11 import com
.intellij
.util
.io
.PersistentHashMap
;
12 import gnu
.trove
.TIntHashSet
;
13 import org
.jetbrains
.annotations
.NotNull
;
16 import java
.util
.ArrayList
;
17 import java
.util
.Collection
;
18 import java
.util
.Iterator
;
19 import java
.util
.List
;
22 * @author Eugene Zhuravlev
25 public final class MapIndexStorage
<Key
, Value
> implements IndexStorage
<Key
, Value
>{
26 private static final Logger LOG
= Logger
.getInstance("#com.intellij.util.indexing.MapIndexStorage");
27 private PersistentHashMap
<Key
, ValueContainer
<Value
>> myMap
;
28 private SLRUCache
<Key
, ChangeTrackingValueContainer
<Value
>> myCache
;
29 private Key myKeyBeingRemoved
= null;
30 private final File myStorageFile
;
31 private final KeyDescriptor
<Key
> myKeyDescriptor
;
32 private final ValueContainerExternalizer
<Value
> myValueContainerExternalizer
;
33 private final int myCacheSize
;
35 public MapIndexStorage(File storageFile
, final KeyDescriptor
<Key
> keyDescriptor
, final DataExternalizer
<Value
> valueExternalizer
,
36 final int cacheSize
) throws IOException
{
38 myStorageFile
= storageFile
;
39 myKeyDescriptor
= keyDescriptor
;
40 myValueContainerExternalizer
= new ValueContainerExternalizer
<Value
>(valueExternalizer
);
41 myCacheSize
= cacheSize
;
45 private void initMapAndCache() throws IOException
{
46 final PersistentHashMap
<Key
, ValueContainer
<Value
>> map
=
47 new PersistentHashMap
<Key
, ValueContainer
<Value
>>(myStorageFile
, myKeyDescriptor
, myValueContainerExternalizer
);
48 myCache
= new SLRUCache
<Key
, ChangeTrackingValueContainer
<Value
>>(myCacheSize
, (int)(Math
.ceil(myCacheSize
* 0.25)) /* 25% from the main cache size*/) {
50 public ChangeTrackingValueContainer
<Value
> createValue(final Key key
) {
51 return new ChangeTrackingValueContainer
<Value
>(new ChangeTrackingValueContainer
.Initializer
<Value
>() {
52 public Object
getLock() {
56 public ValueContainer
<Value
> compute() {
57 ValueContainer
<Value
> value
= null;
61 value
= new ValueContainerImpl
<Value
>();
64 catch (IOException e
) {
65 throw new RuntimeException(e
);
72 protected void onDropFromCache(final Key key
, final ChangeTrackingValueContainer
<Value
> valueContainer
) {
73 if (key
.equals(myKeyBeingRemoved
) || !valueContainer
.isDirty()) {
77 if (!valueContainer
.needsCompacting()) {
78 final ByteArrayOutputStream bytes
= new ByteArrayOutputStream();
79 //noinspection IOResourceOpenedButNotSafelyClosed
80 final DataOutputStream _out
= new DataOutputStream(bytes
);
81 final TIntHashSet set
= valueContainer
.getInvalidated();
83 for (int inputId
: set
.toArray()) {
84 myValueContainerExternalizer
.saveInvalidateCommand(_out
, inputId
);
87 final ValueContainer
<Value
> toRemove
= valueContainer
.getRemovedDelta();
88 if (toRemove
.size() > 0) {
89 myValueContainerExternalizer
.saveAsRemoved(_out
, toRemove
);
92 final ValueContainer
<Value
> toAppend
= valueContainer
.getAddedDelta();
93 if (toAppend
.size() > 0) {
94 myValueContainerExternalizer
.save(_out
, toAppend
);
97 map
.appendData(key
, new PersistentHashMap
.ValueDataAppender() {
98 public void append(final DataOutput out
) throws IOException
{
99 final byte[] barr
= bytes
.toByteArray();
105 // rewrite the value container for defragmentation
106 map
.put(key
, valueContainer
);
109 catch (IOException e
) {
110 throw new RuntimeException(e
);
118 public synchronized void flush() {
119 if (!myMap
.isClosed()) {
125 public synchronized void close() throws StorageException
{
130 catch (IOException e
) {
131 throw new StorageException(e
);
133 catch (RuntimeException e
) {
134 final Throwable cause
= e
.getCause();
135 if (cause
instanceof IOException
) {
136 throw new StorageException(cause
);
138 if (cause
instanceof StorageException
) {
139 throw (StorageException
)cause
;
145 public synchronized void clear() throws StorageException
{
149 catch (IOException e
) {
153 FileUtil
.delete(myStorageFile
);
156 catch (IOException e
) {
157 throw new StorageException(e
);
159 catch (RuntimeException e
) {
160 final Throwable cause
= e
.getCause();
161 if (cause
instanceof IOException
) {
162 throw new StorageException(cause
);
164 if (cause
instanceof StorageException
) {
165 throw (StorageException
)cause
;
171 public synchronized boolean processKeys(final Processor
<Key
> processor
) throws StorageException
{
173 myCache
.clear(); // this will ensure that all new keys are made into the map
174 return myMap
.processKeys(processor
);
176 catch (IOException e
) {
177 throw new StorageException(e
);
179 catch (RuntimeException e
) {
180 final Throwable cause
= e
.getCause();
181 if (cause
instanceof IOException
) {
182 throw new StorageException(cause
);
184 if (cause
instanceof StorageException
) {
185 throw (StorageException
)cause
;
191 public Collection
<Key
> getKeys() throws StorageException
{
192 List
<Key
> keys
= new ArrayList
<Key
>();
193 processKeys(new CommonProcessors
.CollectProcessor
<Key
>(keys
));
198 public synchronized ChangeTrackingValueContainer
<Value
> read(final Key key
) throws StorageException
{
200 return myCache
.get(key
);
202 catch (RuntimeException e
) {
203 final Throwable cause
= e
.getCause();
204 if (cause
instanceof IOException
) {
205 throw new StorageException(cause
);
207 if (cause
instanceof StorageException
) {
208 throw (StorageException
)cause
;
214 public void addValue(final Key key
, final int inputId
, final Value value
) throws StorageException
{
215 read(key
).addValue(inputId
, value
);
218 public void removeValue(final Key key
, final int inputId
, final Value value
) throws StorageException
{
219 read(key
).removeValue(inputId
, value
);
222 public void removeAllValues(Key key
, int inputId
) throws StorageException
{
223 read(key
).removeAllValues(inputId
);
226 public synchronized void remove(final Key key
) throws StorageException
{
228 myKeyBeingRemoved
= key
;
232 catch (IOException e
) {
233 throw new StorageException(e
);
235 catch (RuntimeException e
) {
236 final Throwable cause
= e
.getCause();
237 if (cause
instanceof IOException
) {
238 throw new StorageException(cause
);
240 if (cause
instanceof StorageException
) {
241 throw (StorageException
)cause
;
246 myKeyBeingRemoved
= null;
250 private static final class ValueContainerExternalizer
<T
> implements DataExternalizer
<ValueContainer
<T
>> {
251 private final DataExternalizer
<T
> myExternalizer
;
253 private ValueContainerExternalizer(DataExternalizer
<T
> externalizer
) {
254 myExternalizer
= externalizer
;
257 public void save(final DataOutput out
, final ValueContainer
<T
> container
) throws IOException
{
258 saveImpl(out
, container
, false);
261 public void saveAsRemoved(final DataOutput out
, final ValueContainer
<T
> container
) throws IOException
{
262 saveImpl(out
, container
, true);
265 public void saveInvalidateCommand(final DataOutput out
, int inputId
) throws IOException
{
266 DataInputOutputUtil
.writeSINT(out
, -inputId
);
269 private void saveImpl(final DataOutput out
, final ValueContainer
<T
> container
, final boolean asRemovedData
) throws IOException
{
270 DataInputOutputUtil
.writeSINT(out
, container
.size());
271 for (final Iterator
<T
> valueIterator
= container
.getValueIterator(); valueIterator
.hasNext();) {
272 final T value
= valueIterator
.next();
273 myExternalizer
.save(out
, value
);
275 final ValueContainer
.IntIterator ids
= container
.getInputIdsIterator(value
);
277 DataInputOutputUtil
.writeSINT(out
, ids
.size());
278 while (ids
.hasNext()) {
279 final int id
= ids
.next();
280 DataInputOutputUtil
.writeSINT(out
, asRemovedData ?
-id
: id
);
284 DataInputOutputUtil
.writeSINT(out
, 0);
289 public ValueContainerImpl
<T
> read(final DataInput in
) throws IOException
{
290 DataInputStream stream
= (DataInputStream
)in
;
291 final ValueContainerImpl
<T
> valueContainer
= new ValueContainerImpl
<T
>();
293 while (stream
.available() > 0) {
294 final int valueCount
= DataInputOutputUtil
.readSINT(in
);
295 if (valueCount
< 0) {
296 valueContainer
.removeAllValues(-valueCount
);
297 valueContainer
.setNeedsCompacting(true);
300 for (int valueIdx
= 0; valueIdx
< valueCount
; valueIdx
++) {
301 final T value
= myExternalizer
.read(in
);
302 final int idCount
= DataInputOutputUtil
.readSINT(in
);
303 for (int i
= 0; i
< idCount
; i
++) {
304 final int id
= DataInputOutputUtil
.readSINT(in
);
306 valueContainer
.removeValue(-id
, value
);
307 valueContainer
.setNeedsCompacting(true);
310 valueContainer
.addValue(id
, value
);
316 return valueContainer
;