From 7b8d55ed9fe971fb151eda90db1981c79684c409 Mon Sep 17 00:00:00 2001 From: Maxim Mossienko Date: Wed, 13 Jul 2011 04:15:10 +0400 Subject: [PATCH] extracted PersistentEnumeratorBase to allow different enumerators --- .../util/io/PersistentEnumerator.java | 451 +---------------- .../util/io/PersistentEnumeratorBase.java | 477 ++++++++++++++++++ 2 files changed, 496 insertions(+), 432 deletions(-) create mode 100644 platform/util/src/com/intellij/util/io/PersistentEnumeratorBase.java diff --git a/platform/util/src/com/intellij/util/io/PersistentEnumerator.java b/platform/util/src/com/intellij/util/io/PersistentEnumerator.java index 7fb982bd152f..615e6290b57c 100644 --- a/platform/util/src/com/intellij/util/io/PersistentEnumerator.java +++ b/platform/util/src/com/intellij/util/io/PersistentEnumerator.java @@ -15,35 +15,20 @@ */ package com.intellij.util.io; -import com.intellij.openapi.Forceable; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.util.CommonProcessors; -import com.intellij.util.Processor; -import com.intellij.util.containers.SLRUMap; -import com.intellij.util.containers.ShareableKey; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.TestOnly; -import java.io.*; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; +import java.io.File; +import java.io.IOException; /** * @author max * @author jeka */ -public class PersistentEnumerator implements Forceable, Closeable { +public class PersistentEnumerator extends PersistentEnumeratorBase { private static final Logger LOG = Logger.getInstance("#com.intellij.util.io.PersistentEnumerator"); protected static final int NULL_ID = 0; - protected static final int DATA_OFFSET = 8; - private static final int META_DATA_OFFSET = 4; - private static final int FIRST_VECTOR_OFFSET = 8; - private static final int DIRTY_MAGIC = 0xbabe0589; - private static final int VERSION = 5; - private static final int CORRECTLY_CLOSED_MAGIC = 0xebabafac + VERSION; + + private static final int FIRST_VECTOR_OFFSET = DATA_START; private static final int BITS_PER_LEVEL = 4; private static final int SLOTS_PER_VECTOR = 1 << BITS_PER_LEVEL; @@ -55,228 +40,19 @@ public class PersistentEnumerator implements Forceable, Closeable { private static final int FIRST_LEVEL_MASK = SLOTS_PER_FIRST_VECTOR - 1; private static final byte[] FIRST_VECTOR = new byte[SLOTS_PER_FIRST_VECTOR * 4]; - - protected final ResizeableMappedFile myStorage; - private final ResizeableMappedFile myKeyStorage; - - private boolean myClosed = false; - private boolean myDirty = false; - private final KeyDescriptor myDataDescriptor; private final byte[] myBuffer = new byte[RECORD_SIZE]; - private static final CacheKey ourFlyweight = new FlyweightKey(); - - protected final File myFile; private static final int COLLISION_OFFSET = 0; private static final int KEY_HASHCODE_OFFSET = COLLISION_OFFSET + 4; private static final int KEY_REF_OFFSET = KEY_HASHCODE_OFFSET + 4; protected static final int RECORD_SIZE = KEY_REF_OFFSET + 4; - private boolean myCorrupted = false; - private final MyDataIS myKeyReadStream; - - private static class CacheKey implements ShareableKey { - public PersistentEnumerator owner; - public Object key; - - private CacheKey(final Object key, final PersistentEnumerator owner) { - this.key = key; - this.owner = owner; - } - - public ShareableKey getStableCopy() { - return this; - } - - public boolean equals(final Object o) { - if (this == o) return true; - if (!(o instanceof CacheKey)) return false; - - final CacheKey cacheKey = (CacheKey)o; - - if (!key.equals(cacheKey.key)) return false; - if (!owner.equals(cacheKey.owner)) return false; - - return true; - } - - public int hashCode() { - return key.hashCode(); - } - } - - private static CacheKey sharedKey(Object key, PersistentEnumerator owner) { - ourFlyweight.key = key; - ourFlyweight.owner = owner; - return ourFlyweight; - } - - protected static final PagedFileStorage.StorageLock ourLock = new PagedFileStorage.StorageLock(); - - private static final int ENUMERATION_CACHE_SIZE; - static { - String property = System.getProperty("idea.enumerationCacheSize"); - ENUMERATION_CACHE_SIZE = property == null ? 8192 : Integer.valueOf(property); - } - - private static final SLRUMap ourEnumerationCache = new SLRUMap(ENUMERATION_CACHE_SIZE, ENUMERATION_CACHE_SIZE); - - @TestOnly - public static void clearCacheForTests() { - ourEnumerationCache.clear(); - } - - public static class CorruptedException extends IOException { - @SuppressWarnings({"HardCodedStringLiteral"}) - public CorruptedException(File file) { - super("PersistentStringEnumerator storage corrupted " + file.getPath()); - } - } - public PersistentEnumerator(File file, KeyDescriptor dataDescriptor, int initialSize) throws IOException { - myDataDescriptor = dataDescriptor; - myFile = file; - if (!file.exists()) { - FileUtil.delete(keystreamFile()); - if (!FileUtil.createIfDoesntExist(file)) { - throw new IOException("Cannot create empty file: " + file); - } - } - - myStorage = new ResizeableMappedFile(myFile, initialSize, ourLock); - - synchronized (ourLock) { - if (myStorage.length() == 0) { - try { - markDirty(true); - putMetaData(0); - allocVector(FIRST_VECTOR); - } - catch (RuntimeException e) { - LOG.info(e); - myStorage.close(); - if (e.getCause() instanceof IOException) { - throw (IOException)e.getCause(); - } - throw e; - } - catch (IOException e) { - LOG.info(e); - myStorage.close(); - throw e; - } - catch (Exception e) { - LOG.info(e); - myStorage.close(); - throw new CorruptedException(file); - } - } - else { - int sign; - try { - sign = myStorage.getInt(0); - } - catch(Exception e) { - LOG.info(e); - sign = DIRTY_MAGIC; - } - if (sign != CORRECTLY_CLOSED_MAGIC) { - myStorage.close(); - throw new CorruptedException(file); - } - } - } - - if (myDataDescriptor instanceof InlineKeyDescriptor) { - myKeyStorage = null; - myKeyReadStream = null; - } - else { - myKeyStorage = new ResizeableMappedFile(keystreamFile(), initialSize, ourLock); - myKeyReadStream = new MyDataIS(myKeyStorage); - } - } - - protected int tryEnumerate(Data value) throws IOException { - synchronized (ourEnumerationCache) { - final Integer cachedId = ourEnumerationCache.get(sharedKey(value, this)); - if (cachedId != null) return cachedId.intValue(); - } - - final int id; - synchronized (this) { - synchronized (ourLock) { - id = enumerateImpl(value, false); - } - } - - if (id != NULL_ID) { - synchronized (ourEnumerationCache) { - ourEnumerationCache.put(new CacheKey(value, this), id); - } - } - - return id; + super(file, dataDescriptor, initialSize); } - public int enumerate(Data value) throws IOException { - synchronized (ourEnumerationCache) { - final Integer cachedId = ourEnumerationCache.get(sharedKey(value, this)); - if (cachedId != null) { - return cachedId.intValue(); - } - } - - final int id; - synchronized (this) { - synchronized (ourLock) { - id = enumerateImpl(value, true); - } - } - - synchronized (ourEnumerationCache) { - ourEnumerationCache.put(new CacheKey(value, this), id); - } - - return id; - } - - public interface DataFilter { - boolean accept(int id); - } - - protected final synchronized void putMetaData(int data) throws IOException { - synchronized (ourLock) { - myStorage.putInt(META_DATA_OFFSET, data); - } - } - - protected final synchronized int getMetaData() throws IOException { - synchronized (ourLock) { - return myStorage.getInt(META_DATA_OFFSET); - } - } - - public boolean processAllDataObject(final Processor processor, @Nullable final DataFilter filter) throws IOException { - return traverseAllRecords(new RecordsProcessor() { - public boolean process(final int record) throws IOException { - if (filter == null || filter.accept(record)) { - return processor.process(valueOf(record)); - } - return true; - } - }); - - } - - public Collection getAllDataObjects(@Nullable final DataFilter filter) throws IOException { - final List values = new ArrayList(); - processAllDataObject(new CommonProcessors.CollectProcessor(values), filter); - return values; - } - - public interface RecordsProcessor { - boolean process(int record) throws IOException; + protected void setupEmptyFile() throws IOException { + allocVector(FIRST_VECTOR); } public synchronized boolean traverseAllRecords(RecordsProcessor p) throws IOException { @@ -300,7 +76,7 @@ public class PersistentEnumerator implements Forceable, Closeable { } } - private int enumerateImpl(final Data value, final boolean saveNewValue) throws IOException { + protected int enumerateImpl(final Data value, final boolean saveNewValue) throws IOException { try { int depth = 0; final int valueHC = myDataDescriptor.getHashCode(value); @@ -395,6 +171,11 @@ public class PersistentEnumerator implements Forceable, Closeable { } } + @Override + protected int recordWriteOffset(byte[] buf) { + return (int)myStorage.length(); + } + private static int hcByte(int hashcode, int byteN) { if (byteN == 0) { return hashcode & FIRST_LEVEL_MASK; @@ -402,7 +183,7 @@ public class PersistentEnumerator implements Forceable, Closeable { hashcode >>>= BITS_PER_FIRST_LEVEL; byteN--; - + return (hashcode >>> (byteN * BITS_PER_LEVEL)) & LEVEL_MASK; } @@ -416,37 +197,6 @@ public class PersistentEnumerator implements Forceable, Closeable { return -myStorage.getInt(idx); } - private int writeData(final Data value, int hashCode) { - try { - markDirty(true); - - final int dataOff = myKeyStorage != null ? (int)myKeyStorage.length() : ((InlineKeyDescriptor)myDataDescriptor).toInt(value); - byte[] buf = prepareEntryRecordBuf(hashCode, dataOff); - - if (myKeyStorage != null) { - final BufferExposingByteArrayOutputStream bos = new BufferExposingByteArrayOutputStream(); - DataOutput out = new DataOutputStream(bos); - myDataDescriptor.save(out, value); - myKeyStorage.put(dataOff, bos.getInternalBuffer(), 0, bos.size()); - } - - final ResizeableMappedFile storage = myStorage; - final int pos = (int)storage.length(); - storage.put(pos, buf, 0, buf.length); - - return pos; - } - catch (IOException e) { - throw new RuntimeException(e); - } - } - - private byte[] prepareEntryRecordBuf(int hashCode, int dataOffset) { - final byte[] buf = getRecordBuffer(); - setupRecord(hashCode, dataOffset, buf); - return buf; - } - protected byte[] getRecordBuffer() { return myBuffer; } @@ -457,175 +207,12 @@ public class PersistentEnumerator implements Forceable, Closeable { Bits.putInt(buf, KEY_REF_OFFSET, dataOffset); } - protected boolean iterateData(final Processor processor) throws IOException { - if (myKeyStorage == null) { - throw new UnsupportedOperationException("Iteration over InlineIntegerKeyDescriptors is not supported"); - } - - myKeyStorage.force(); - - DataInputStream keysStream = new DataInputStream(new BufferedInputStream(new LimitedInputStream(new FileInputStream(keystreamFile()), - (int)myKeyStorage.length()))); - try { - try { - while (true) { - Data key = myDataDescriptor.read(keysStream); - if (!processor.process(key)) return false; - } - } - catch (EOFException e) { - // Done - } - return true; - } - finally { - keysStream.close(); - } - } - - private File keystreamFile() { - return new File(myFile.getPath() + ".keystream"); - } - private int hashCodeOf(int idx) throws IOException { return myStorage.getInt(idx + KEY_HASHCODE_OFFSET); } - public synchronized Data valueOf(int idx) throws IOException { - synchronized (ourLock) { - try { - final ResizeableMappedFile storage = myStorage; - int addr = storage.getInt(idx + KEY_REF_OFFSET); - - if (myKeyReadStream == null) return ((InlineKeyDescriptor)myDataDescriptor).fromInt(addr); - - myKeyReadStream.setup(addr, myKeyStorage.length()); - return myDataDescriptor.read(myKeyReadStream); - } - catch (IOException io) { - markCorrupted(); - throw io; - } - catch (Throwable e) { - markCorrupted(); - throw new RuntimeException(e); - } - } + @Override + protected int indexToAddr(int idx) { + return myStorage.getInt(idx + KEY_REF_OFFSET); } - - private static class MyDataIS extends DataInputStream { - private MyDataIS(ResizeableMappedFile raf) { - super(new MyBufferedIS(new MappedFileInputStream(raf, 0, 0))); - } - - public void setup(long pos, long limit) { - ((MyBufferedIS)in).setup(pos, limit); - } - } - - private static class MyBufferedIS extends BufferedInputStream { - public MyBufferedIS(final InputStream in) { - super(in, 512); - } - - public void setup(long pos, long limit) { - this.pos = 0; - count = 0; - ((MappedFileInputStream)in).setup(pos, limit); - } - } - - public synchronized void close() throws IOException { - synchronized (ourLock) { - if (!myClosed) { - myClosed = true; - try { - if (myKeyStorage != null) { - myKeyStorage.close(); - } - flush(); - } - finally { - myStorage.close(); - } - } - } - } - - public synchronized boolean isClosed() { - return myClosed; - } - - public synchronized boolean isDirty() { - return myDirty; - } - - private synchronized void flush() throws IOException { - synchronized (ourLock) { - if (myStorage.isDirty() || isDirty()) { - markDirty(false); - myStorage.force(); - } - } - } - - public synchronized void force() { - synchronized (ourLock) { - try { - if (myKeyStorage != null) { - myKeyStorage.force(); - } - flush(); - } - catch (IOException e) { - throw new RuntimeException(e); - } - } - } - - protected final void markDirty(boolean dirty) throws IOException { - synchronized (ourLock) { - if (myDirty) { - if (!dirty) { - markClean(); - } - } - else { - if (dirty) { - myStorage.putInt(0, DIRTY_MAGIC); - myDirty = true; - } - } - } - } - - private void markCorrupted() { - if (!myCorrupted) { - myCorrupted = true; - try { - markDirty(true); - force(); - } - catch (IOException e) { - // ignore... - } - } - } - - protected void markClean() throws IOException { - if (!myCorrupted) { - myStorage.putInt(0, CORRECTLY_CLOSED_MAGIC); - myDirty = false; - } - } - - private static class FlyweightKey extends CacheKey { - public FlyweightKey() { - super(null, null); - } - - public ShareableKey getStableCopy() { - return new CacheKey(key, owner); - } - } -} +} \ No newline at end of file diff --git a/platform/util/src/com/intellij/util/io/PersistentEnumeratorBase.java b/platform/util/src/com/intellij/util/io/PersistentEnumeratorBase.java new file mode 100644 index 000000000000..bea3b735d256 --- /dev/null +++ b/platform/util/src/com/intellij/util/io/PersistentEnumeratorBase.java @@ -0,0 +1,477 @@ +/* + * Copyright 2000-2009 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.openapi.Forceable; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.CommonProcessors; +import com.intellij.util.Processor; +import com.intellij.util.containers.SLRUMap; +import com.intellij.util.containers.ShareableKey; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; + +import java.io.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * @author max + * @author jeka + */ +abstract class PersistentEnumeratorBase implements Forceable, Closeable { + protected static final Logger LOG = Logger.getInstance("#com.intellij.util.io.PersistentEnumerator"); + protected static final int NULL_ID = 0; + + private static final int DIRTY_MAGIC = 0xbabe0589; + private static final int VERSION = 6; + private static final int CORRECTLY_CLOSED_MAGIC = 0xebabafac + VERSION; + + private static final int META_DATA_OFFSET = 4; + protected static final int DATA_START = META_DATA_OFFSET + 4; + + protected final ResizeableMappedFile myStorage; + private final ResizeableMappedFile myKeyStorage; + + private boolean myClosed = false; + private boolean myDirty = false; + protected final KeyDescriptor myDataDescriptor; + + private static final CacheKey ourFlyweight = new FlyweightKey(); + + protected final File myFile; + + private boolean myCorrupted = false; + private final MyDataIS myKeyReadStream; + + private static class CacheKey implements ShareableKey { + public PersistentEnumeratorBase owner; + public Object key; + + private CacheKey(final Object key, final PersistentEnumeratorBase owner) { + this.key = key; + this.owner = owner; + } + + public ShareableKey getStableCopy() { + return this; + } + + public boolean equals(final Object o) { + if (this == o) return true; + if (!(o instanceof CacheKey)) return false; + + final CacheKey cacheKey = (CacheKey)o; + + if (!key.equals(cacheKey.key)) return false; + if (!owner.equals(cacheKey.owner)) return false; + + return true; + } + + public int hashCode() { + return key.hashCode(); + } + } + + private static CacheKey sharedKey(Object key, PersistentEnumeratorBase owner) { + ourFlyweight.key = key; + ourFlyweight.owner = owner; + return ourFlyweight; + } + + protected static final PagedFileStorage.StorageLock ourLock = new PagedFileStorage.StorageLock(); + + private static final int ENUMERATION_CACHE_SIZE; + static { + String property = System.getProperty("idea.enumerationCacheSize"); + ENUMERATION_CACHE_SIZE = property == null ? 8192 : Integer.valueOf(property); + } + + private static final SLRUMap ourEnumerationCache = new SLRUMap(ENUMERATION_CACHE_SIZE, ENUMERATION_CACHE_SIZE); + + @TestOnly + public static void clearCacheForTests() { + ourEnumerationCache.clear(); + } + + public static class CorruptedException extends IOException { + @SuppressWarnings({"HardCodedStringLiteral"}) + public CorruptedException(File file) { + super("PersistentStringEnumerator storage corrupted " + file.getPath()); + } + } + + public PersistentEnumeratorBase(File file, KeyDescriptor dataDescriptor, int initialSize) throws IOException { + myDataDescriptor = dataDescriptor; + myFile = file; + if (!file.exists()) { + FileUtil.delete(keystreamFile()); + if (!FileUtil.createIfDoesntExist(file)) { + throw new IOException("Cannot create empty file: " + file); + } + } + + myStorage = new ResizeableMappedFile(myFile, initialSize, ourLock); + + synchronized (ourLock) { + if (myStorage.length() == 0) { + try { + markDirty(true); + putMetaData(0); + setupEmptyFile(); + } + catch (RuntimeException e) { + LOG.info(e); + myStorage.close(); + if (e.getCause() instanceof IOException) { + throw (IOException)e.getCause(); + } + throw e; + } + catch (IOException e) { + LOG.info(e); + myStorage.close(); + throw e; + } + catch (Exception e) { + LOG.info(e); + myStorage.close(); + throw new CorruptedException(file); + } + } + else { + int sign; + try { + sign = myStorage.getInt(0); + } + catch(Exception e) { + LOG.info(e); + sign = DIRTY_MAGIC; + } + if (sign != CORRECTLY_CLOSED_MAGIC) { + myStorage.close(); + throw new CorruptedException(file); + } + } + } + + if (myDataDescriptor instanceof InlineKeyDescriptor) { + myKeyStorage = null; + myKeyReadStream = null; + } + else { + myKeyStorage = new ResizeableMappedFile(keystreamFile(), initialSize, ourLock); + myKeyReadStream = new MyDataIS(myKeyStorage); + } + } + + protected abstract void setupEmptyFile() throws IOException; + + protected int tryEnumerate(Data value) throws IOException { + synchronized (ourEnumerationCache) { + final Integer cachedId = ourEnumerationCache.get(sharedKey(value, this)); + if (cachedId != null) return cachedId.intValue(); + } + + final int id; + synchronized (this) { + synchronized (ourLock) { + id = enumerateImpl(value, false); + } + } + + if (id != NULL_ID) { + synchronized (ourEnumerationCache) { + ourEnumerationCache.put(new CacheKey(value, this), id); + } + } + + return id; + } + + public int enumerate(Data value) throws IOException { + synchronized (ourEnumerationCache) { + final Integer cachedId = ourEnumerationCache.get(sharedKey(value, this)); + if (cachedId != null) { + return cachedId.intValue(); + } + } + + final int id; + synchronized (this) { + synchronized (ourLock) { + id = enumerateImpl(value, true); + } + } + + synchronized (ourEnumerationCache) { + ourEnumerationCache.put(new CacheKey(value, this), id); + } + + return id; + } + + public interface DataFilter { + boolean accept(int id); + } + + protected void putMetaData(int data) throws IOException { + synchronized (ourLock) { + myStorage.putInt(META_DATA_OFFSET, data); + } + } + + protected int getMetaData() throws IOException { + synchronized (ourLock) { + return myStorage.getInt(META_DATA_OFFSET); + } + } + + public boolean processAllDataObject(final Processor processor, @Nullable final DataFilter filter) throws IOException { + return traverseAllRecords(new RecordsProcessor() { + public boolean process(final int record) throws IOException { + if (filter == null || filter.accept(record)) { + return processor.process(valueOf(record)); + } + return true; + } + }); + + } + + public Collection getAllDataObjects(@Nullable final DataFilter filter) throws IOException { + final List values = new ArrayList(); + processAllDataObject(new CommonProcessors.CollectProcessor(values), filter); + return values; + } + + public interface RecordsProcessor { + boolean process(int record) throws IOException; + } + + public abstract boolean traverseAllRecords(RecordsProcessor p) throws IOException; + + protected abstract int enumerateImpl(final Data value, final boolean saveNewValue) throws IOException; + + protected int writeData(final Data value, int hashCode) { + try { + markDirty(true); + + final int dataOff = myKeyStorage != null ? (int)myKeyStorage.length() : ((InlineKeyDescriptor)myDataDescriptor).toInt(value); + byte[] buf = prepareEntryRecordBuf(hashCode, dataOff); + + if (myKeyStorage != null) { + final BufferExposingByteArrayOutputStream bos = new BufferExposingByteArrayOutputStream(); + DataOutput out = new DataOutputStream(bos); + myDataDescriptor.save(out, value); + myKeyStorage.put(dataOff, bos.getInternalBuffer(), 0, bos.size()); + } + + final int pos = recordWriteOffset(buf); + myStorage.put(pos, buf, 0, buf.length); + + return pos; + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + + protected abstract int recordWriteOffset(byte[] buf); + + private byte[] prepareEntryRecordBuf(int hashCode, int dataOffset) { + final byte[] buf = getRecordBuffer(); + setupRecord(hashCode, dataOffset, buf); + return buf; + } + + protected abstract byte[] getRecordBuffer(); + + protected abstract void setupRecord(int hashCode, final int dataOffset, final byte[] buf); + + protected boolean iterateData(final Processor processor) throws IOException { + if (myKeyStorage == null) { + throw new UnsupportedOperationException("Iteration over InlineIntegerKeyDescriptors is not supported"); + } + + myKeyStorage.force(); + + DataInputStream keysStream = new DataInputStream(new BufferedInputStream(new LimitedInputStream(new FileInputStream(keystreamFile()), + (int)myKeyStorage.length()))); + try { + try { + while (true) { + Data key = myDataDescriptor.read(keysStream); + if (!processor.process(key)) return false; + } + } + catch (EOFException e) { + // Done + } + return true; + } + finally { + keysStream.close(); + } + } + + private File keystreamFile() { + return new File(myFile.getPath() + ".keystream"); + } + + public synchronized Data valueOf(int idx) throws IOException { + synchronized (ourLock) { + try { + int addr = indexToAddr(idx); + + if (myKeyReadStream == null) return ((InlineKeyDescriptor)myDataDescriptor).fromInt(addr); + + myKeyReadStream.setup(addr, myKeyStorage.length()); + return myDataDescriptor.read(myKeyReadStream); + } + catch (IOException io) { + markCorrupted(); + throw io; + } + catch (Throwable e) { + markCorrupted(); + throw new RuntimeException(e); + } + } + } + + protected abstract int indexToAddr(int idx); + + private static class MyDataIS extends DataInputStream { + private MyDataIS(ResizeableMappedFile raf) { + super(new MyBufferedIS(new MappedFileInputStream(raf, 0, 0))); + } + + public void setup(long pos, long limit) { + ((MyBufferedIS)in).setup(pos, limit); + } + } + + private static class MyBufferedIS extends BufferedInputStream { + public MyBufferedIS(final InputStream in) { + super(in, 512); + } + + public void setup(long pos, long limit) { + this.pos = 0; + count = 0; + ((MappedFileInputStream)in).setup(pos, limit); + } + } + + public synchronized void close() throws IOException { + synchronized (ourLock) { + if (!myClosed) { + myClosed = true; + try { + if (myKeyStorage != null) { + myKeyStorage.close(); + } + flush(); + } + finally { + myStorage.close(); + } + } + } + } + + public synchronized boolean isClosed() { + return myClosed; + } + + public synchronized boolean isDirty() { + return myDirty; + } + + private synchronized void flush() throws IOException { + synchronized (ourLock) { + if (myStorage.isDirty() || isDirty()) { + markDirty(false); + myStorage.force(); + } + } + } + + public synchronized void force() { + synchronized (ourLock) { + try { + if (myKeyStorage != null) { + myKeyStorage.force(); + } + flush(); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + protected final void markDirty(boolean dirty) throws IOException { + synchronized (ourLock) { + if (myDirty) { + if (!dirty) { + markClean(); + } + } + else { + if (dirty) { + myStorage.putInt(0, DIRTY_MAGIC); + myDirty = true; + } + } + } + } + + protected void markCorrupted() { + if (!myCorrupted) { + myCorrupted = true; + try { + markDirty(true); + force(); + } + catch (IOException e) { + // ignore... + } + } + } + + protected void markClean() throws IOException { + if (!myCorrupted) { + myStorage.putInt(0, CORRECTLY_CLOSED_MAGIC); + myDirty = false; + } + } + + private static class FlyweightKey extends CacheKey { + public FlyweightKey() { + super(null, null); + } + + public ShareableKey getStableCopy() { + return new CacheKey(key, owner); + } + } +} \ No newline at end of file