OffsetHelper class

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

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

Поля (2)

МодификаторыТипИмя
private int index
int var2

Методы (2)

МодификаторыВозвратСигнатура
public int lengthpublic int length()
public int nextpublic int next(int 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;
         }
      }
   }
}