IndexedStorageFile class

Пакет: com.hypixel.hytale.storage

Файл: com/hypixel/hytale/storage/IndexedStorageFile.java

implements: Closeable

Поля (107)

МодификаторыТипИмя
final StampedLock[] EMPTY_STAMPED_LOCKS
break
break
private final int count
private int index
return
private final int segmentIndex
private final long[] stamps
ByteBuffer var1
ByteBuffer var1
long var1
long var1
var1
IntArrayList var1
int var10
ByteBuffer var11
int var12
int var18
byte[] var2
int var2
long var2
long var2
ByteBuffer var2
var2
ByteBuffer var2
long var2
int var2
long[] var2
long var2
int var2
ByteBuffer var25
ByteBuffer var3
ByteBuffer var3
int var3
int var3
int var3
StampedLock var3
int var3
var3
ByteBuffer var3
var3
ByteBuffer var3
int var3
int var3
int var3
var3
var3
var3
long var3
long var37
int var38
long var39
ByteBuffer var4
long var4
var4
int var4
int var4
long var4
int var4
var4
long var4
int var4
int var4
IndexedStorageFile.SegmentRangeWriteLock var4
long var4
int var4
IndexedStorageFile var5
int var5
var5
ByteBuffer var5
ByteBuffer var5
long var5
ByteBuffer var5
ByteBuffer var5
int var5
StampedLock[] var5
int var6
var6
var6
int var6
int var6
int var6
var6
var6
int var6
long var6
int var6
var6
var6
var6
long var7
ByteBuffer var7
int var7
ByteBuffer var7
int var7
int var7
var7
var7
int var8
ByteBuffer var8
long var8
int var8
var8
ByteBuffer var9
int var9
int var9
var9

Методы (100)

МодификаторыВозвратСигнатура
abstract throw new IOExceptionthrow new IOException("file channel is empty")
abstract throw new IOExceptionthrow new IOException("IndexedStorageFile version 0 is no longer supported")
abstract throw new IOExceptionthrow new IOException("file channel is not empty")
abstract throw new IOExceptionthrow new IOException("Invalid version! " + this.version)
abstract throw new IOExceptionthrow new IOException("Failed to read segments: " + var1 + ", " + var2 + ", " + var3)
abstract throw new IllegalArgumentExceptionthrow new IllegalArgumentException("blobCount must be > 0")
abstract throw new IllegalArgumentExceptionthrow new IllegalArgumentException("segmentSize must be > 0")
abstract throw new IllegalArgumentExceptionthrow new IllegalArgumentException("Invalid segment index!")
abstract throw new IllegalArgumentExceptionthrow new IllegalArgumentException("position is before the segments start")
abstract throw new IllegalArgumentExceptionthrow new IllegalArgumentException("Invalid count!")
abstract throw new IllegalStateExceptionthrow new IllegalStateException()
abstract throw new IndexOutOfBoundsExceptionthrow new IndexOutOfBoundsException("Index out of range: " + var1 + " blobCount: " + this.blobCount)
OffsetHelper OffsetHelper()
public SegmentRangeWriteLockpublic SegmentRangeWriteLock(int nullx, int nullxx, long[] nullxxx)
for for(int var1 = 0; var1 < this.blobCount; var1++)
for for(int var4 = 0; var4 < this.blobCount; var4++)
for for(int var2 = 0; var2 < this.blobCount; var2++)
for for(int var14 = var1 - 1; var14 >= 0; var14--)
for for(int var9 = var1 - 1; var9 > var14; var9--)
for for(int var6 = this.segmentLocks.length; var6 < var4; var6++)
for for(int var1 = 0; var1 < this.count; var1++)
public void forcevoid force(boolean var1)
public int getBlobCountint getBlobCount()
protected int getBlobIndexint getBlobIndex(int var1)
public int getCompressionLevelint getCompressionLevel()
protected StampedLock getSegmentLockStampedLock getSegmentLock(int var1)
public int getSegmentSizeint getSegmentSize()
public int getUsedBlobCountint getUsedBlobCount()
if if(var5.version == 0)
if if(var1 <= 0)
if if(var2 <= 0)
if if(this.version >= 0 && this.version <= 1)
if if(this.mappedBlobIndexes == null)
if if(this.mappedBlobIndexes == null)
if if(this.flushOnWrite)
if if(this.flushOnWrite)
if if(var5 == 0)
if if(var6 > 0)
if if(var6 != 0)
if if(var1 >= 0 && var1 < this.blobCount)
if if(var4 == 0)
if if(var1 >= 0 && var1 < this.blobCount)
if if(var4 == 0)
if if(var1 >= 0 && var1 < this.blobCount)
if if(var6 == 0)
if if(UnsafeUtil.UNSAFE != null)
if if(var1 >= 0 && var1 < this.blobCount)
if if(var7 == 0)
if if(UnsafeUtil.UNSAFE != null)
if if(UnsafeUtil.UNSAFE != null)
if if(var1 == 0)
if if(var1 >= 0 && var1 < this.blobCount)
if if(UnsafeUtil.UNSAFE != null)
if if(var10 != 0)
if if(this.flushOnWrite)
if if(var10 == 0)
if if(var9 > 0)
if if(UnsafeUtil.UNSAFE != null)
if if(var1 >= 0 && var1 < this.blobCount)
if if(var4 != 0)
if if(var8 < 0)
if if(var3 == var8)
if if(var8 < 0)
if if(var2[var14] == 0L)
if if(var1 < this.segmentLocks.length)
if if(var1 < this.segmentLocks.length)
if if(var1 == 0)
if if(var3 < 0L)
if if(this.mappedBlobIndexes != null)
if if(UnsafeUtil.UNSAFE != null && this.mappedBlobIndexes != null)
if if(nullx == 0)
if if(nullxx == 0)
public int lengthpublic int length()
public FileLock lockFileLock lock()
protected void memoryMapBlobIndexesvoid memoryMapBlobIndexes()
public int nextpublic int next(int var1)
abstract return openreturn open(var0, 1024, 4096, var1, var2)
protected int positionToSegmentint positionToSegment(long var1)
protected void putBlobIndexvoid putBlobIndex(int var1, int var2)
public void readBlobvoid readBlob(int var1, @Nonnull ByteBuffer var2)
public int readBlobCompressedLengthint readBlobCompressedLength(int var1)
public int readBlobLengthint readBlobLength(int var1)
protected void readHeadervoid readHeader()
protected void readUsedSegmentsvoid readUsedSegments()
public void removeBlobvoid removeBlob(int var1)
protected int requiredSegmentsint requiredSegments(long var1)
public int segmentCountint segmentCount()
protected long segmentOffsetlong segmentOffset(int var1)
protected long segmentPositionlong segmentPosition(int var1)
public int segmentSizeint segmentSize()
protected long segmentsBaselong segmentsBase()
public void setCompressionLevelvoid setCompressionLevel(int var1)
public void setFlushOnWritevoid setFlushOnWrite(boolean var1)
public long sizelong size()
protected void unlockprotected void unlock()
while while(true)
while while(var7 < var1)
public void writeBlobvoid writeBlob(int var1, @Nonnull ByteBuffer var2)
protected void writeHeadervoid writeHeader(int var1, int var2)
protected int writeSegmentsint writeSegments(@Nonnull ByteBuffer var1)

Исходный код

Показать/скрыть
class="kw">package com.hypixel.hytale.storage;

class="kw">import com.github.luben.zstd.Zstd;
class="kw">import com.hypixel.hytale.codec.Codec;
class="kw">import com.hypixel.hytale.metrics.MetricsRegistry;
class="kw">import com.hypixel.hytale.sneakythrow.SneakyThrow;
class="kw">import com.hypixel.hytale.unsafe.UnsafeUtil;
class="kw">import it.unimi.dsi.fastutil.ints.IntArrayList;
class="kw">import it.unimi.dsi.fastutil.ints.IntList;
class="kw">import java.io.Closeable;
class="kw">import java.io.IOException;
class="kw">import java.nio.ByteBuffer;
class="kw">import java.nio.MappedByteBuffer;
class="kw">import java.nio.channels.FileChannel;
class="kw">import java.nio.channels.FileLock;
class="kw">import java.nio.channels.FileChannel.MapMode;
class="kw">import java.nio.charset.StandardCharsets;
class="kw">import java.nio.file.OpenOption;
class="kw">import java.nio.file.Path;
class="kw">import java.nio.file.StandardOpenOption;
class="kw">import java.nio.file.attribute.FileAttribute;
class="kw">import java.util.Arrays;
class="kw">import java.util.BitSet;
class="kw">import java.util.Set;
class="kw">import java.util.concurrent.atomic.AtomicInteger;
class="kw">import java.util.concurrent.locks.StampedLock;
class="kw">import javax.annotation.Nonnull;
class="kw">import javax.annotation.Nullable;

class="kw">public class IndexedStorageFile class="kw">implements Closeable {
   class="kw">public class="kw">static class="kw">final StampedLock[] EMPTY_STAMPED_LOCKS = new StampedLock[0];
   class="kw">public class="kw">static class="kw">final MetricsRegistry<IndexedStorageFile> METRICS_REGISTRY = new MetricsRegistry<IndexedStorageFile>()
      .register("Size", var0 -> {
         try {
            class="kw">return var0.size();
         } catch (IOException var2) {
            class="kw">return -1L;
         }
      }, Codec.LONG)
      .register("CompressionLevel", var0 -> var0.getCompressionLevel(), Codec.INTEGER)
      .register("BlobCount", var0 -> var0.getBlobCount(), Codec.INTEGER)
      .register("UsedBlobCount", SneakyThrow.sneakyFunction(var0 -> var0.usedBlobCount.get()), Codec.INTEGER)
      .register("SegmentSize", var0 -> var0.segmentSize(), Codec.INTEGER)
      .register("SegmentCount", var0 -> var0.segmentCount(), Codec.INTEGER);
   class="kw">public class="kw">static class="kw">final String MAGIC_STRING = "HytaleIndexedStorage";
   class="kw">public class="kw">static class="kw">final int VERSION = 1;
   class="kw">public class="kw">static class="kw">final int DEFAULT_BLOB_COUNT = 1024;
   class="kw">public class="kw">static class="kw">final int DEFAULT_SEGMENT_SIZE = 4096;
   class="kw">public class="kw">static class="kw">final int DEFAULT_COMPRESSION_LEVEL = 3;
   class="kw">static class="kw">final IndexedStorageFile.OffsetHelper HOH = new IndexedStorageFile.OffsetHelper();
   class="kw">public class="kw">static class="kw">final int MAGIC_LENGTH = 20;
   class="kw">public class="kw">static class="kw">final int MAGIC_OFFSET = HOH.next(20);
   class="kw">public class="kw">static class="kw">final int VERSION_OFFSET = HOH.next(4);
   class="kw">public class="kw">static class="kw">final int BLOB_COUNT_OFFSET = HOH.next(4);
   class="kw">public class="kw">static class="kw">final int SEGMENT_SIZE_OFFSET = HOH.next(4);
   class="kw">public class="kw">static class="kw">final int HEADER_LENGTH = HOH.length();
   class="kw">static class="kw">final IndexedStorageFile.OffsetHelper BOH = new IndexedStorageFile.OffsetHelper();
   class="kw">public class="kw">static class="kw">final int SRC_LENGTH_OFFSET = BOH.next(4);
   class="kw">public class="kw">static class="kw">final int COMPRESSED_LENGTH_OFFSET = BOH.next(4);
   class="kw">public class="kw">static class="kw">final int BLOB_HEADER_LENGTH = BOH.length();
   class="kw">public class="kw">static class="kw">final int INDEX_SIZE = 4;
   class="kw">public class="kw">static class="kw">final int UNASSIGNED_INDEX = 0;
   class="kw">public class="kw">static class="kw">final int FIRST_SEGMENT_INDEX = 1;
   class="kw">public class="kw">static class="kw">final FileAttribute<?>[] NO_ATTRIBUTES = new FileAttribute[0];
   class="kw">static class="kw">final byte[] MAGIC_BYTES = "HytaleIndexedStorage".getBytes(StandardCharsets.UTF_8);
   class="kw">private class="kw">static class="kw">final ByteBuffer MAGIC_BUFFER = ByteBuffer.wrap(MAGIC_BYTES);
   class="kw">private class="kw">static class="kw">final ThreadLocal<ByteBuffer> CACHED_TEMP_BUFFER = ThreadLocal.withInitial(() -> ByteBuffer.allocateDirect(HEADER_LENGTH));
   @Nonnull
   class="kw">private class="kw">final Path path;
   class="kw">private class="kw">final FileChannel fileChannel;
   class="kw">private boolean flushOnWrite = false;
   class="kw">private int compressionLevel = 3;
   class="kw">private int version;
   class="kw">private int blobCount;
   class="kw">private int segmentSize;
   class="kw">private class="kw">final AtomicInteger usedBlobCount = new AtomicInteger(0);
   class="kw">private StampedLock[] indexLocks;
   @Nullable
   class="kw">private MappedByteBuffer mappedBlobIndexes;
   class="kw">private class="kw">final StampedLock segmentLocksLock = new StampedLock();
   class="kw">private StampedLock[] segmentLocks = EMPTY_STAMPED_LOCKS;
   class="kw">private class="kw">final StampedLock usedSegmentsLock = new StampedLock();
   class="kw">private class="kw">final BitSet usedSegments = new BitSet();

   @Nonnull
   class="kw">private class="kw">static ByteBuffer getTempBuffer(int var0) {
      ByteBuffer var1 = CACHED_TEMP_BUFFER.get();
      var1.position(0);
      var1.limit(var0);
      class="kw">return var1;
   }

   @Nonnull
   class="kw">private class="kw">static ByteBuffer allocateDirect(int var0) {
      class="kw">return ByteBuffer.allocateDirect(var0);
   }

   @Nonnull
   class="kw">public class="kw">static IndexedStorageFile open(@Nonnull Path var0, OpenOption... var1) class="kw">throws IOException {
      class="kw">return open(var0, 1024, 4096, Set.of(var1), NO_ATTRIBUTES);
   }

   @Nonnull
   class="kw">public class="kw">static IndexedStorageFile open(@Nonnull Path var0, @Nonnull Set<? class="kw">extends OpenOption> var1, FileAttribute<?>... var2) class="kw">throws IOException {
      class="kw">return open(var0, 1024, 4096, var1, var2);
   }

   @Nonnull
   class="kw">public class="kw">static IndexedStorageFile open(@Nonnull Path var0, int var1, int var2, OpenOption... var3) class="kw">throws IOException {
      class="kw">return open(var0, var1, var2, Set.of(var3), NO_ATTRIBUTES);
   }

   @Nonnull
   class="kw">public class="kw">static IndexedStorageFile open(@Nonnull Path var0, int var1, int var2, @Nonnull Set<? class="kw">extends OpenOption> var3, FileAttribute<?>... var4) class="kw">throws IOException {
      IndexedStorageFile var5 = new IndexedStorageFile(var0, FileChannel.open(var0, var3, var4));
      if (var3.contains(StandardOpenOption.CREATE_NEW)) {
         var5.create(var1, var2);
         class="kw">return var5;
      }

      if (var3.contains(StandardOpenOption.CREATE) && var5.fileChannel.size() == 0L) {
         var5.create(var1, var2);
      } else {
         if (var5.fileChannel.size() == 0L) {
            throw new IOException("file channel is empty");
         }

         var5.readHeader();
         var5.memoryMapBlobIndexes();
         if (var5.version == 0) {
            throw new IOException("IndexedStorageFile version 0 is no longer supported");
         }

         var5.readUsedSegments();
      }

      class="kw">return var5;
   }

   class="kw">private IndexedStorageFile(@Nonnull Path var1, @Nonnull FileChannel var2) {
      this.path = var1;
      this.fileChannel = var2;
   }

   @Nonnull
   class="kw">public Path getPath() {
      class="kw">return this.path;
   }

   class="kw">public int getBlobCount() {
      class="kw">return this.blobCount;
   }

   class="kw">public int getSegmentSize() {
      class="kw">return this.segmentSize;
   }

   class="kw">public int getCompressionLevel() {
      class="kw">return this.compressionLevel;
   }

   class="kw">public int getUsedBlobCount() {
      class="kw">return this.usedBlobCount.get();
   }

   class="kw">public void setFlushOnWrite(boolean var1) {
      this.flushOnWrite = var1;
   }

   class="kw">public void setCompressionLevel(int var1) {
      this.compressionLevel = var1;
   }

   @Nonnull
   class="kw">protected IndexedStorageFile create(int var1, int var2) class="kw">throws IOException {
      if (var1 <= 0) {
         throw new IllegalArgumentException("blobCount must be > 0");
      }

      if (var2 <= 0) {
         throw new IllegalArgumentException("segmentSize must be > 0");
      }

      this.blobCount = var1;
      this.segmentSize = var2;
      if (this.fileChannel.size() != 0L) {
         throw new IOException("file channel is not empty");
      }

      this.writeHeader(var1, var2);
      this.memoryMapBlobIndexes();
      class="kw">return this;
   }

   class="kw">protected void writeHeader(int var1, int var2) class="kw">throws IOException {
      ByteBuffer var3 = getTempBuffer(HEADER_LENGTH);
      var3.put(MAGIC_BYTES);
      var3.putInt(VERSION_OFFSET, 1);
      var3.putInt(BLOB_COUNT_OFFSET, var1);
      var3.putInt(SEGMENT_SIZE_OFFSET, var2);
      var3.position(0);
      if (this.fileChannel.write(var3, 0L) != HEADER_LENGTH) {
         throw new IllegalStateException();
      }
   }

   class="kw">protected void readHeader() class="kw">throws IOException {
      ByteBuffer var1 = getTempBuffer(HEADER_LENGTH);
      if (this.fileChannel.read(var1, 0L) != HEADER_LENGTH) {
         throw new IllegalStateException();
      }

      var1.position(0);
      var1.limit(20);
      if (!MAGIC_BUFFER.equals(var1)) {
         var1.position(0);
         byte[] var2 = new byte[20];
         var1.get(var2);
         throw new IOException("Invalid MAGIC! " + var1 + ", " + Arrays.toString(var2) + " expected " + Arrays.toString(MAGIC_BYTES));
      }

      var1.limit(HEADER_LENGTH);
      this.version = var1.getInt(VERSION_OFFSET);
      if (this.version >= 0 && this.version <= 1) {
         this.blobCount = var1.getInt(BLOB_COUNT_OFFSET);
         this.segmentSize = var1.getInt(SEGMENT_SIZE_OFFSET);
      } else {
         throw new IOException("Invalid version! " + this.version);
      }
   }

   class="kw">protected void memoryMapBlobIndexes() class="kw">throws IOException {
      this.indexLocks = new StampedLock[this.blobCount];

      for (int var1 = 0; var1 < this.blobCount; var1++) {
         this.indexLocks[var1] = new StampedLock();
      }

      try {
         this.mappedBlobIndexes = this.fileChannel.map(MapMode.READ_WRITE, HEADER_LENGTH, this.blobCount * 4L);
      } catch (UnsupportedOperationException var2) {
         this.mappedBlobIndexes = null;
      }
   }

   class="kw">protected int getBlobIndex(int var1) class="kw">throws IOException {
      int var2 = var1 * 4;
      if (this.mappedBlobIndexes == null) {
         ByteBuffer var3 = getTempBuffer(4);
         if (this.fileChannel.read(var3, HEADER_LENGTH + var2) != 4) {
            throw new IllegalStateException();
         } else {
            class="kw">return var3.getInt(0);
         }
      } else {
         class="kw">return this.mappedBlobIndexes.getInt(var2);
      }
   }

   class="kw">protected void putBlobIndex(int var1, int var2) class="kw">throws IOException {
      int var3 = var1 * 4;
      if (this.mappedBlobIndexes == null) {
         ByteBuffer var4 = getTempBuffer(4);
         var4.putInt(0, var2);
         if (this.fileChannel.write(var4, HEADER_LENGTH + var3) != 4) {
            throw new IllegalStateException();
         }

         if (this.flushOnWrite) {
            this.fileChannel.force(false);
         }
      } else {
         this.mappedBlobIndexes.putInt(var3, var2);
         if (this.flushOnWrite) {
            this.mappedBlobIndexes.force(var3, 4);
         }
      }
   }

   class="kw">protected void readUsedSegments() class="kw">throws IOException {
      long var1 = this.usedSegmentsLock.writeLock();

      try {
         int var3 = 0;

         for (int var4 = 0; var4 < this.blobCount; var4++) {
            long var7 = this.indexLocks[var4].readLock();

            int var5;
            int var6;
            try {
               var5 = this.getBlobIndex(var4);
               if (var5 == 0) {
                  var6 = 0;
               } else {
                  var3++;
                  ByteBuffer var9 = this.readBlobHeader(var5);
                  var6 = var9.getInt(COMPRESSED_LENGTH_OFFSET);
               }
            } class="kw">finally {
               this.indexLocks[var4].unlockRead(var7);
            }

            if (var6 > 0) {
               int var18 = this.requiredSegments(BLOB_HEADER_LENGTH + var6);
               this.usedSegments.set(var5, var5 + var18);
            }
         }

         this.usedBlobCount.set(var3);
      } class="kw">finally {
         this.usedSegmentsLock.unlockWrite(var1);
      }
   }

   class="kw">public long size() class="kw">throws IOException {
      class="kw">return this.fileChannel.size();
   }

   class="kw">public int segmentSize() {
      try {
         class="kw">return this.requiredSegments(this.fileChannel.size() - this.segmentsBase()) + 1;
      } catch (IOException var2) {
         class="kw">return -1;
      }
   }

   class="kw">public int segmentCount() {
      long var1 = this.usedSegmentsLock.tryOptimisticRead();
      int var3 = this.usedSegments.cardinality();
      if (this.usedSegmentsLock.validate(var1)) {
         class="kw">return var3;
      }

      var1 = this.usedSegmentsLock.readLock();

      try {
         class="kw">return this.usedSegments.cardinality();
      } class="kw">finally {
         this.usedSegmentsLock.unlockRead(var1);
      }
   }

   @Nonnull
   class="kw">public IntList keys() class="kw">throws IOException {
      IntArrayList var1 = new IntArrayList(this.blobCount);

      for (int var2 = 0; var2 < this.blobCount; var2++) {
         StampedLock var3 = this.indexLocks[var2];
         long var4 = var3.tryOptimisticRead();
         int var6 = this.getBlobIndex(var2);
         if (var3.validate(var4)) {
            if (var6 != 0) {
               var1.add(var2);
            }
         } else {
            var4 = var3.readLock();

            try {
               if (this.getBlobIndex(var2) != 0) {
                  var1.add(var2);
               }
            } class="kw">finally {
               var3.unlockRead(var4);
            }
         }
      }

      class="kw">return var1;
   }

   class="kw">public int readBlobLength(int var1) class="kw">throws IOException {
      if (var1 >= 0 && var1 < this.blobCount) {
         long var2 = this.indexLocks[var1].readLock();

         try {
            int var4 = this.getBlobIndex(var1);
            if (var4 == 0) {
               class="kw">return 0;
            }

            ByteBuffer var5 = this.readBlobHeader(var4);
            class="kw">return var5.getInt(SRC_LENGTH_OFFSET);
         } class="kw">finally {
            this.indexLocks[var1].unlockRead(var2);
         }
      } else {
         throw new IndexOutOfBoundsException("Index out of range: " + var1 + " blobCount: " + this.blobCount);
      }
   }

   class="kw">public int readBlobCompressedLength(int var1) class="kw">throws IOException {
      if (var1 >= 0 && var1 < this.blobCount) {
         long var2 = this.indexLocks[var1].readLock();

         try {
            int var4 = this.getBlobIndex(var1);
            if (var4 == 0) {
               class="kw">return 0;
            }

            ByteBuffer var5 = this.readBlobHeader(var4);
            class="kw">return var5.getInt(COMPRESSED_LENGTH_OFFSET);
         } class="kw">finally {
            this.indexLocks[var1].unlockRead(var2);
         }
      } else {
         throw new IndexOutOfBoundsException("Index out of range: " + var1 + " blobCount: " + this.blobCount);
      }
   }

   @Nullable
   class="kw">public ByteBuffer readBlob(int var1) class="kw">throws IOException {
      if (var1 >= 0 && var1 < this.blobCount) {
         long var4 = this.indexLocks[var1].readLock();

         ByteBuffer var2;
         int var3;
         try {
            int var6 = this.getBlobIndex(var1);
            if (var6 == 0) {
               class="kw">return null;
            }

            ByteBuffer var7 = this.readBlobHeader(var6);
            var3 = var7.getInt(SRC_LENGTH_OFFSET);
            int var8 = var7.getInt(COMPRESSED_LENGTH_OFFSET);
            var2 = this.readSegments(var6, var8);
         } class="kw">finally {
            this.indexLocks[var1].unlockRead(var4);
         }

         var2.position(0);

         try {
            class="kw">return Zstd.decompress(var2, var3);
         } class="kw">finally {
            if (UnsafeUtil.UNSAFE != null) {
               UnsafeUtil.UNSAFE.invokeCleaner(var2);
            }
         }
      } else {
         throw new IndexOutOfBoundsException("Index out of range: " + var1 + " blobCount: " + this.blobCount);
      }
   }

   class="kw">public void readBlob(int var1, @Nonnull ByteBuffer var2) class="kw">throws IOException {
      if (var1 >= 0 && var1 < this.blobCount) {
         long var5 = this.indexLocks[var1].readLock();

         ByteBuffer var3;
         int var4;
         try {
            int var7 = this.getBlobIndex(var1);
            if (var7 == 0) {
               class="kw">return;
            }

            ByteBuffer var8 = this.readBlobHeader(var7);
            var4 = var8.getInt(SRC_LENGTH_OFFSET);
            int var9 = var8.getInt(COMPRESSED_LENGTH_OFFSET);
            if (var4 > var2.remaining()) {
               throw new IllegalArgumentException("dest buffer is not large enough! required dest.remaining() >= " + var4);
            }

            var3 = this.readSegments(var7, var9);
         } class="kw">finally {
            this.indexLocks[var1].unlockRead(var5);
         }

         var3.position(0);

         try {
            if (var2.isDirect()) {
               Zstd.decompress(var2, var3);
            } else {
               ByteBuffer var25 = allocateDirect(var4);

               try {
                  Zstd.decompress(var25, var3);
                  var25.position(0);
                  var2.put(var25);
               } class="kw">finally {
                  if (UnsafeUtil.UNSAFE != null) {
                     UnsafeUtil.UNSAFE.invokeCleaner(var25);
                  }
               }
            }
         } class="kw">finally {
            if (UnsafeUtil.UNSAFE != null) {
               UnsafeUtil.UNSAFE.invokeCleaner(var3);
            }
         }
      } else {
         throw new IndexOutOfBoundsException("Index out of range: " + var1 + " blobCount: " + this.blobCount);
      }
   }

   @Nonnull
   class="kw">protected ByteBuffer readBlobHeader(int var1) class="kw">throws IOException {
      if (var1 == 0) {
         throw new IllegalArgumentException("Invalid segment index!");
      } else {
         ByteBuffer var2 = getTempBuffer(BLOB_HEADER_LENGTH);
         if (this.fileChannel.read(var2, this.segmentPosition(var1)) != BLOB_HEADER_LENGTH) {
            throw new IllegalStateException();
         } else {
            class="kw">return var2;
         }
      }
   }

   @Nonnull
   class="kw">protected ByteBuffer readSegments(int var1, int var2) class="kw">throws IOException {
      ByteBuffer var3 = allocateDirect(var2);
      long var4 = this.segmentPosition(var1);
      if (this.fileChannel.read(var3, var4 + BLOB_HEADER_LENGTH) != var2) {
         throw new IllegalStateException();
      } else if (var3.remaining() != 0) {
         throw new IOException("Failed to read segments: " + var1 + ", " + var2 + ", " + var3);
      } else {
         class="kw">return var3;
      }
   }

   class="kw">public void writeBlob(int var1, @Nonnull ByteBuffer var2) class="kw">throws IOException {
      if (var1 >= 0 && var1 < this.blobCount) {
         int var3 = var2.remaining();
         int var4 = (int)Zstd.compressBound(var3);
         ByteBuffer var5 = allocateDirect(BLOB_HEADER_LENGTH + var4);

         try {
            var5.putInt(SRC_LENGTH_OFFSET, var3);
            var5.position(BLOB_HEADER_LENGTH);
            int var6;
            if (var2.isDirect()) {
               var6 = Zstd.compress(var5, var2, this.compressionLevel);
            } else {
               ByteBuffer var7 = allocateDirect(var3);

               try {
                  var7.put(var2);
                  var7.position(0);
                  var6 = Zstd.compress(var5, var7, this.compressionLevel);
               } class="kw">finally {
                  if (UnsafeUtil.UNSAFE != null) {
                     UnsafeUtil.UNSAFE.invokeCleaner(var7);
                  }
               }
            }

            var5.putInt(COMPRESSED_LENGTH_OFFSET, var6);
            var5.limit(var5.position());
            var5.position(0);
            long var37 = this.indexLocks[var1].writeLock();

            try {
               int var9 = 0;
               int var10 = this.getBlobIndex(var1);
               if (var10 != 0) {
                  ByteBuffer var11 = this.readBlobHeader(var10);
                  int var12 = var11.getInt(COMPRESSED_LENGTH_OFFSET);
                  var9 = this.requiredSegments(BLOB_HEADER_LENGTH + var12);
               }

               int var38 = this.writeSegments(var5);
               if (this.flushOnWrite) {
                  this.fileChannel.force(false);
               }

               this.putBlobIndex(var1, var38);
               if (var10 == 0) {
                  this.usedBlobCount.incrementAndGet();
               }

               if (var9 > 0) {
                  long var39 = this.usedSegmentsLock.writeLock();

                  try {
                     this.usedSegments.clear(var10, var10 + var9);
                  } class="kw">finally {
                     this.usedSegmentsLock.unlockWrite(var39);
                  }
               }
            } class="kw">finally {
               this.indexLocks[var1].unlockWrite(var37);
            }
         } class="kw">finally {
            if (UnsafeUtil.UNSAFE != null) {
               UnsafeUtil.UNSAFE.invokeCleaner(var5);
            }
         }
      } else {
         throw new IndexOutOfBoundsException("Index out of range: " + var1 + " blobCount: " + this.blobCount);
      }
   }

   class="kw">public void removeBlob(int var1) class="kw">throws IOException {
      if (var1 >= 0 && var1 < this.blobCount) {
         long var2 = this.indexLocks[var1].writeLock();

         try {
            int var4 = this.getBlobIndex(var1);
            if (var4 != 0) {
               ByteBuffer var5 = this.readBlobHeader(var4);
               int var6 = var5.getInt(COMPRESSED_LENGTH_OFFSET);
               int var7 = this.requiredSegments(BLOB_HEADER_LENGTH + var6);
               this.putBlobIndex(var1, 0);
               this.usedBlobCount.decrementAndGet();
               long var8 = this.usedSegmentsLock.writeLock();

               try {
                  this.usedSegments.clear(var4, var4 + var7);
               } class="kw">finally {
                  this.usedSegmentsLock.unlockWrite(var8);
               }
            }
         } class="kw">finally {
            this.indexLocks[var1].unlockWrite(var2);
         }
      } else {
         throw new IndexOutOfBoundsException("Index out of range: " + var1 + " blobCount: " + this.blobCount);
      }
   }

   class="kw">protected int writeSegments(@Nonnull ByteBuffer var1) class="kw">throws IOException {
      int var2 = var1.remaining();
      int var3 = this.requiredSegments(var2);
      IndexedStorageFile.SegmentRangeWriteLock var4 = this.findFreeSegment(var3);

      try {
         int var5 = var4.segmentIndex;
         if (this.fileChannel.write(var1, this.segmentPosition(var5)) != var2) {
            throw new IllegalStateException();
         }

         long var6 = this.usedSegmentsLock.writeLock();

         try {
            this.usedSegments.set(var5, var5 + var3);
         } class="kw">finally {
            this.usedSegmentsLock.unlockWrite(var6);
         }

         class="kw">return var5;
      } class="kw">finally {
         var4.unlock();
      }
   }

   @Nonnull
   class="kw">private IndexedStorageFile.SegmentRangeWriteLock findFreeSegment(int var1) {
      long[] var2 = new long[var1];
      int var3 = 1;

      label98:
      while (true) {
         long var4 = this.usedSegmentsLock.readLock();

         try {
            int var6 = 0;
            int var7 = 0;

            while (var7 < var1) {
               int var8 = this.usedSegments.nextSetBit(var3);
               if (var8 < 0) {
                  var6 = var3;
                  break;
               }

               if (var3 == var8) {
                  var6 = this.usedSegments.nextClearBit(var3);
                  var8 = this.usedSegments.nextSetBit(var6 + 1);
                  if (var8 < 0) {
                     break;
                  }

                  var7 = var8 - var6;
                  var3 = var8 + 1;
               } else {
                  var6 = var3;
                  var7 = var8 - var6;
                  var3 = var8 + 1;
               }
            }

            for (int var14 = var1 - 1; var14 >= 0; var14--) {
               var2[var14] = this.getSegmentLock(var6 + var14).tryWriteLock();
               if (var2[var14] == 0L) {
                  for (int var9 = var1 - 1; var9 > var14; var9--) {
                     this.getSegmentLock(var6 + var9).unlockWrite(var2[var9]);
                  }

                  var3 = var6 + var14 + 1;
                  class="kw">continue label98;
               }
            }

            class="kw">return new IndexedStorageFile.SegmentRangeWriteLock(var6, var1, var2);
         } class="kw">finally {
            this.usedSegmentsLock.unlockRead(var4);
         }
      }
   }

   class="kw">protected StampedLock getSegmentLock(int var1) {
      if (var1 < this.segmentLocks.length) {
         class="kw">return this.segmentLocks[var1];
      }

      long var2 = this.segmentLocksLock.writeLock();

      try {
         if (var1 < this.segmentLocks.length) {
            class="kw">return this.segmentLocks[var1];
         }

         int var4 = var1 + 1;
         StampedLock[] var5 = Arrays.copyOf(this.segmentLocks, var4);

         for (int var6 = this.segmentLocks.length; var6 < var4; var6++) {
            var5[var6] = new StampedLock();
         }

         this.segmentLocks = var5;
         class="kw">return this.segmentLocks[var1];
      } class="kw">finally {
         this.segmentLocksLock.unlockWrite(var2);
      }
   }

   class="kw">protected long segmentsBase() {
      class="kw">return HEADER_LENGTH + this.blobCount * 4L;
   }

   class="kw">protected long segmentOffset(int var1) {
      if (var1 == 0) {
         throw new IllegalArgumentException("Invalid segment index!");
      } else {
         class="kw">return (long)(var1 - 1) * this.segmentSize;
      }
   }

   class="kw">protected long segmentPosition(int var1) {
      class="kw">return this.segmentOffset(var1) + this.segmentsBase();
   }

   class="kw">protected int positionToSegment(long var1) {
      long var3 = var1 - this.segmentsBase();
      if (var3 < 0L) {
         throw new IllegalArgumentException("position is before the segments start");
      } else {
         class="kw">return (int)(var3 / this.segmentSize) + 1;
      }
   }

   class="kw">protected int requiredSegments(long var1) {
      class="kw">return (int)((var1 + this.segmentSize - 1L) / this.segmentSize);
   }

   class="kw">public FileLock lock() class="kw">throws IOException {
      class="kw">return this.fileChannel.lock();
   }

   class="kw">public void force(boolean var1) class="kw">throws IOException {
      this.fileChannel.force(var1);
      if (this.mappedBlobIndexes != null) {
         this.mappedBlobIndexes.force();
      }
   }

   @Override
   class="kw">public void close() class="kw">throws IOException {
      this.fileChannel.close();
      if (UnsafeUtil.UNSAFE != null && this.mappedBlobIndexes != null) {
         UnsafeUtil.UNSAFE.invokeCleaner(this.mappedBlobIndexes);
      }

      this.mappedBlobIndexes = null;
   }

   @Nonnull
   @Override
   class="kw">public String toString() {
      class="kw">return "IndexedStorageFile{fileChannel="
         + this.fileChannel
         + ", compressionLevel="
         + this.compressionLevel
         + ", blobCount="
         + this.blobCount
         + ", segmentSize="
         + this.segmentSize
         + ", mappedBlobIndexes="
         + this.mappedBlobIndexes
         + ", usedSegments="
         + this.usedSegments
         + "}";
   }

   class="kw">static {
      MAGIC_BUFFER.position(0);
   }

   class="kw">static class OffsetHelper {
      class="kw">private int index;

      OffsetHelper() {
      }

      class="kw">public int next(int var1) {
         int var2 = this.index;
         this.index += var1;
         class="kw">return var2;
      }

      class="kw">public int length() {
         class="kw">return this.index;
      }
   }

   class="kw">protected class SegmentRangeWriteLock {
      class="kw">private class="kw">final int segmentIndex;
      class="kw">private class="kw">final int count;
      class="kw">private class="kw">final long[] stamps;

      class="kw">public SegmentRangeWriteLock(int nullx, int nullxx, long[] nullxxx) {
         if (nullx == 0) {
            throw new IllegalArgumentException("Invalid segment index!");
         }

         if (nullxx == 0) {
            throw new IllegalArgumentException("Invalid count!");
         }

         this.segmentIndex = nullx;
         this.count = nullxx;
         this.stamps = nullxxx;
      }

      class="kw">protected void unlock() {
         for (int var1 = 0; var1 < this.count; var1++) {
            IndexedStorageFile.this.getSegmentLock(this.segmentIndex + var1).unlockWrite(this.stamps[var1]);
            this.stamps[var1] = 0L;
         }
      }
   }
}